From 70f952638a2b4467572288f8228a296cd1a2f9ab Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 8 May 2026 21:11:40 -0400 Subject: [PATCH 01/41] Remove zigpy dependency for Home Assistant units and constants --- zha/application/__init__.py | 19 + zha/application/platforms/__init__.py | 8 +- .../platforms/binary_sensor/const.py | 4 +- .../platforms/binary_sensor/device_class.py | 91 ++++ zha/application/platforms/number/const.py | 3 +- .../platforms/number/device_class.py | 405 ++++++++++++++++ zha/application/platforms/sensor/const.py | 6 +- .../platforms/sensor/device_class.py | 446 ++++++++++++++++++ zha/units.py | 115 +++++ 9 files changed, 1087 insertions(+), 10 deletions(-) create mode 100644 zha/application/platforms/binary_sensor/device_class.py create mode 100644 zha/application/platforms/number/device_class.py create mode 100644 zha/application/platforms/sensor/device_class.py diff --git a/zha/application/__init__.py b/zha/application/__init__.py index c3d9c5c23..d8c01161c 100644 --- a/zha/application/__init__.py +++ b/zha/application/__init__.py @@ -24,3 +24,22 @@ class Platform(StrEnum): VALVE = "valve" VIRTUAL = "virtual" UPDATE = "update" + + +class EntityType(StrEnum): + """Entity type.""" + + CONFIG = "config" + DIAGNOSTIC = "diagnostic" + STANDARD = "standard" + + +class EntityPlatform(StrEnum): + """Entity platform exposed by quirks v2 metadata.""" + + BINARY_SENSOR = "binary_sensor" + BUTTON = "button" + NUMBER = "number" + SENSOR = "sensor" + SELECT = "select" + SWITCH = "switch" diff --git a/zha/application/platforms/__init__.py b/zha/application/platforms/__init__.py index 46abf7454..2a04ac1c6 100644 --- a/zha/application/platforms/__init__.py +++ b/zha/application/platforms/__init__.py @@ -17,14 +17,14 @@ from zigpy.profiles.zha import PROFILE_ID as ZHA_PROFILE_ID from zigpy.profiles.zll import PROFILE_ID as ZLL_PROFILE_ID -from zigpy.quirks.v2 import EntityMetadata, EntityType +from zigpy.quirks.v2 import EntityMetadata from zigpy.types import ClusterId from zigpy.types.named import EUI64 import zigpy.zcl from zigpy.zcl import ReportingConfig from zigpy.zcl.foundation import ZCLAttributeDef -from zha.application import Platform +from zha.application import EntityType, Platform from zha.application.const import UniqueIdMigration from zha.const import STATE_CHANGED from zha.debounce import Debouncer @@ -552,9 +552,9 @@ def _init_from_quirks_metadata(self, entity_metadata: EntityMetadata) -> None: elif has_command_name: self._unique_id_suffix = entity_metadata.command_name - if entity_metadata.entity_type is EntityType.CONFIG: + if entity_metadata.entity_type == EntityType.CONFIG: self._attr_entity_category = EntityCategory.CONFIG - elif entity_metadata.entity_type is EntityType.DIAGNOSTIC: + elif entity_metadata.entity_type == EntityType.DIAGNOSTIC: self._attr_entity_category = EntityCategory.DIAGNOSTIC else: self._attr_entity_category = None diff --git a/zha/application/platforms/binary_sensor/const.py b/zha/application/platforms/binary_sensor/const.py index ece9a1fbd..56a0b31d9 100644 --- a/zha/application/platforms/binary_sensor/const.py +++ b/zha/application/platforms/binary_sensor/const.py @@ -1,9 +1,9 @@ """Constants for the binary_sensor platform.""" -from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass from zigpy.zcl.clusters.security import IasZone -# Re-exported from zigpy for use throughout ZHA +from zha.application.platforms.binary_sensor.device_class import BinarySensorDeviceClass + __all__ = ["BinarySensorDeviceClass"] diff --git a/zha/application/platforms/binary_sensor/device_class.py b/zha/application/platforms/binary_sensor/device_class.py new file mode 100644 index 000000000..0eea82253 --- /dev/null +++ b/zha/application/platforms/binary_sensor/device_class.py @@ -0,0 +1,91 @@ +"""Device classes for the binary_sensor platform.""" + +from enum import StrEnum + + +class BinarySensorDeviceClass(StrEnum): + """Device class for binary sensors.""" + + # On means low, Off means normal + BATTERY = "battery" + + # On means charging, Off means not charging + BATTERY_CHARGING = "battery_charging" + + # On means carbon monoxide detected, Off means no carbon monoxide (clear) + CO = "carbon_monoxide" + + # On means cold, Off means normal + COLD = "cold" + + # On means connected, Off means disconnected + CONNECTIVITY = "connectivity" + + # On means open, Off means closed + DOOR = "door" + + # On means open, Off means closed + GARAGE_DOOR = "garage_door" + + # On means gas detected, Off means no gas (clear) + GAS = "gas" + + # On means hot, Off means normal + HEAT = "heat" + + # On means light detected, Off means no light + LIGHT = "light" + + # On means open (unlocked), Off means closed (locked) + LOCK = "lock" + + # On means wet, Off means dry + MOISTURE = "moisture" + + # On means motion detected, Off means no motion (clear) + MOTION = "motion" + + # On means moving, Off means not moving (stopped) + MOVING = "moving" + + # On means occupied, Off means not occupied (clear) + OCCUPANCY = "occupancy" + + # On means open, Off means closed + OPENING = "opening" + + # On means plugged in, Off means unplugged + PLUG = "plug" + + # On means power detected, Off means no power + POWER = "power" + + # On means home, Off means away + PRESENCE = "presence" + + # On means problem detected, Off means no problem (OK) + PROBLEM = "problem" + + # On means running, Off means not running + RUNNING = "running" + + # On means unsafe, Off means safe + SAFETY = "safety" + + # On means smoke detected, Off means no smoke (clear) + SMOKE = "smoke" + + # On means sound detected, Off means no sound (clear) + SOUND = "sound" + + # On means tampering detected, Off means no tampering (clear) + TAMPER = "tamper" + + # On means update available, Off means up-to-date + UPDATE = "update" + + # On means vibration detected, Off means no vibration + VIBRATION = "vibration" + + # On means open, Off means closed + WINDOW = "window" diff --git a/zha/application/platforms/number/const.py b/zha/application/platforms/number/const.py index 36870772c..6f52df930 100644 --- a/zha/application/platforms/number/const.py +++ b/zha/application/platforms/number/const.py @@ -1,8 +1,7 @@ """Constants for the Number platform.""" -from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass, NumberMode +from zha.application.platforms.number.device_class import NumberDeviceClass, NumberMode -# Re-exported from zigpy for use throughout ZHA __all__ = ["NumberDeviceClass", "NumberMode"] ICONS = { diff --git a/zha/application/platforms/number/device_class.py b/zha/application/platforms/number/device_class.py new file mode 100644 index 000000000..592148235 --- /dev/null +++ b/zha/application/platforms/number/device_class.py @@ -0,0 +1,405 @@ +"""Device classes for the Number platform.""" + +from enum import StrEnum + + +class NumberMode(StrEnum): + """Modes for number entities.""" + + AUTO = "auto" + BOX = "box" + SLIDER = "slider" + + +class NumberDeviceClass(StrEnum): + """Device class for numbers.""" + + # NumberDeviceClass should be aligned with SensorDeviceClass + ABSOLUTE_HUMIDITY = "absolute_humidity" + """Absolute humidity. + + Unit of measurement: `g/m³`, `mg/m³` + """ + + APPARENT_POWER = "apparent_power" + """Apparent power. + + Unit of measurement: `mVA`, `VA`, `kVA` + """ + + AQI = "aqi" + """Air Quality Index. + + Unit of measurement: `None` + """ + + AREA = "area" + """Area + + Unit of measurement: `UnitOfArea` units + """ + + ATMOSPHERIC_PRESSURE = "atmospheric_pressure" + """Atmospheric pressure. + + Unit of measurement: `UnitOfPressure` units + """ + + BATTERY = "battery" + """Percentage of battery that is left. + + Unit of measurement: `%` + """ + + BLOOD_GLUCOSE_CONCENTRATION = "blood_glucose_concentration" + """Blood glucose concentration. + + Unit of measurement: `mg/dL`, `mmol/L` + """ + + CO = "carbon_monoxide" + """Carbon Monoxide gas concentration. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `mg/m³`, `μg/m³` + """ + + CO2 = "carbon_dioxide" + """Carbon Dioxide gas concentration. + + Unit of measurement: `ppm` (parts per million) + """ + + CONDUCTIVITY = "conductivity" + """Conductivity. + + Unit of measurement: `S/cm`, `mS/cm`, `μS/cm` + """ + + CURRENT = "current" + """Current. + + Unit of measurement: `A`, `mA` + """ + + DATA_RATE = "data_rate" + """Data rate. + + Unit of measurement: UnitOfDataRate + """ + + DATA_SIZE = "data_size" + """Data size. + + Unit of measurement: UnitOfInformation + """ + + DISTANCE = "distance" + """Generic distance. + + Unit of measurement: `LENGTH_*` units + - SI /metric: `mm`, `cm`, `m`, `km` + - USCS / imperial: `in`, `ft`, `yd`, `mi` + """ + + DURATION = "duration" + """Fixed duration. + + Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `μs` + """ + + ENERGY = "energy" + """Energy. + + Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal` + """ + + ENERGY_DISTANCE = "energy_distance" + """Energy distance. + + Use this device class for sensors measuring energy by distance, for example the amount + of electric energy consumed by an electric car. + + Unit of measurement: `kWh/100km`, `Wh/km`, `mi/kWh`, `km/kWh` + """ + + ENERGY_STORAGE = "energy_storage" + """Stored energy. + + Use this device class for sensors measuring stored energy, for example the amount + of electric energy currently stored in a battery or the capacity of a battery. + + Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal` + """ + + FREQUENCY = "frequency" + """Frequency. + + Unit of measurement: `Hz`, `kHz`, `MHz`, `GHz` + """ + + GAS = "gas" + """Gas. + + Unit of measurement: + - SI / metric: `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF` + """ + + HUMIDITY = "humidity" + """Relative humidity. + + Unit of measurement: `%` + """ + + ILLUMINANCE = "illuminance" + """Illuminance. + + Unit of measurement: `lx` + """ + + IRRADIANCE = "irradiance" + """Irradiance. + + Unit of measurement: + - SI / metric: `W/m²` + - USCS / imperial: `BTU/(h⋅ft²)` + """ + + MOISTURE = "moisture" + """Moisture. + + Unit of measurement: `%` + """ + + MONETARY = "monetary" + """Amount of money. + + Unit of measurement: ISO4217 currency code + + See https://en.wikipedia.org/wiki/ISO_4217#Active_codes for active codes + """ + + NITROGEN_DIOXIDE = "nitrogen_dioxide" + """Amount of NO2. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `μg/m³` + """ + + NITROGEN_MONOXIDE = "nitrogen_monoxide" + """Amount of NO. + + Unit of measurement: `ppb` (parts per billion), `μg/m³` + """ + + NITROUS_OXIDE = "nitrous_oxide" + """Amount of N2O. + + Unit of measurement: `μg/m³` + """ + + OZONE = "ozone" + """Amount of O3. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `μg/m³` + """ + + PH = "ph" + """Potential hydrogen (acidity/alkalinity). + + Unit of measurement: Unitless + """ + + PM1 = "pm1" + """Particulate matter <= 1 μm. + + Unit of measurement: `μg/m³` + """ + + PM10 = "pm10" + """Particulate matter <= 10 μm. + + Unit of measurement: `μg/m³` + """ + + PM25 = "pm25" + """Particulate matter <= 2.5 μm. + + Unit of measurement: `μg/m³` + """ + + PM4 = "pm4" + """Particulate matter <= 4 μm. + + Unit of measurement: `μg/m³` + """ + + POWER_FACTOR = "power_factor" + """Power factor. + + Unit of measurement: `%`, `None` + """ + + POWER = "power" + """Power. + + Unit of measurement: `mW`, `W`, `kW`, `MW`, `GW`, `TW`, `BTU/h` + """ + + PRECIPITATION = "precipitation" + """Accumulated precipitation. + + Unit of measurement: UnitOfPrecipitationDepth + - SI / metric: `cm`, `mm` + - USCS / imperial: `in` + """ + + PRECIPITATION_INTENSITY = "precipitation_intensity" + """Precipitation intensity. + + Unit of measurement: UnitOfVolumetricFlux + - SI /metric: `mm/d`, `mm/h` + - USCS / imperial: `in/d`, `in/h` + """ + + PRESSURE = "pressure" + """Pressure. + + Unit of measurement: + - `mbar`, `cbar`, `bar` + - `mPa`, `Pa`, `hPa`, `kPa` + - `inHg` + - `psi` + - `inH₂O` + """ + + REACTIVE_ENERGY = "reactive_energy" + """Reactive energy. + + Unit of measurement: `varh`, `kvarh` + """ + + REACTIVE_POWER = "reactive_power" + """Reactive power. + + Unit of measurement: `mvar`, `var`, `kvar` + """ + + SIGNAL_STRENGTH = "signal_strength" + """Signal strength. + + Unit of measurement: `dB`, `dBm` + """ + + SOUND_PRESSURE = "sound_pressure" + """Sound pressure. + + Unit of measurement: `dB`, `dBA` + """ + + SPEED = "speed" + """Generic speed. + + Unit of measurement: `SPEED_*` units or `UnitOfVolumetricFlux` + - SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h` + - USCS / imperial: `in/d`, `in/h`, `ft/s`, `mph` + - Nautical: `kn` + """ + + SULPHUR_DIOXIDE = "sulphur_dioxide" + """Amount of SO2. + + Unit of measurement: `ppb` (parts per billion), `μg/m³` + """ + + TEMPERATURE = "temperature" + """Temperature. + + Unit of measurement: `°C`, `°F`, `K` + """ + + TEMPERATURE_DELTA = "temperature_delta" + """Difference of temperatures - Temperature range. + + Unit of measurement: `°C`, `°F`, `K` + """ + + VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" + """Amount of VOC. + + Unit of measurement: `μg/m³`, `mg/m³` + """ + + VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" + """Ratio of VOC. + + Unit of measurement: `ppm`, `ppb` + """ + + VOLTAGE = "voltage" + """Voltage. + + Unit of measurement: `V`, `mV`, `μV`, `kV`, `MV` + """ + + VOLUME = "volume" + """Generic volume. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `fl. oz.`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + VOLUME_STORAGE = "volume_storage" + """Generic stored volume. + + Use this device class for sensors measuring stored volume, for example the amount + of fuel in a fuel tank. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `fl. oz.`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + VOLUME_FLOW_RATE = "volume_flow_rate" + """Generic flow rate + + Unit of measurement: UnitOfVolumeFlowRate + - SI / metric: `m³/h`, `m³/min`, `m³/s`, `L/h`, `L/min`, `L/s`, `mL/s` + - USCS / imperial: `ft³/min`, `gal/min`, `gal/d` + """ + + WATER = "water" + """Water. + + Unit of measurement: + - SI / metric: `m³`, `L` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + WEIGHT = "weight" + """Generic weight, represents a measurement of an object's mass. + + Weight is used instead of mass to fit with every day language. + + Unit of measurement: `MASS_*` units + - SI / metric: `μg`, `mg`, `g`, `kg` + - USCS / imperial: `oz`, `lb` + """ + + WIND_DIRECTION = "wind_direction" + """Wind direction. + + Unit of measurement: `°` + """ + + WIND_SPEED = "wind_speed" + """Wind speed. + + Unit of measurement: `SPEED_*` units + - SI /metric: `m/s`, `km/h` + - USCS / imperial: `ft/s`, `mph` + - Nautical: `kn` + """ diff --git a/zha/application/platforms/sensor/const.py b/zha/application/platforms/sensor/const.py index 82420a5d9..42d72a386 100644 --- a/zha/application/platforms/sensor/const.py +++ b/zha/application/platforms/sensor/const.py @@ -3,11 +3,14 @@ from datetime import UTC, datetime import enum -from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass from zigpy.zcl.clusters.general_const import AnalogInputType from zigpy.zcl.clusters.homeautomation import MeasurementType from zigpy.zcl.clusters.smartenergy import MeteringDeviceType +from zha.application.platforms.sensor.device_class import ( + SensorDeviceClass, + SensorStateClass, +) from zha.units import ( CONCENTRATION_PARTS_PER_MILLION, COUNT, @@ -23,7 +26,6 @@ UnitOfTime, ) -# Re-exported from zigpy for use throughout ZHA __all__ = [ "SensorDeviceClass", "SensorStateClass", diff --git a/zha/application/platforms/sensor/device_class.py b/zha/application/platforms/sensor/device_class.py new file mode 100644 index 000000000..9dd174a22 --- /dev/null +++ b/zha/application/platforms/sensor/device_class.py @@ -0,0 +1,446 @@ +"""Device classes for the sensor platform.""" + +from enum import StrEnum + + +class SensorDeviceClass(StrEnum): + """Device class for sensors.""" + + # Non-numerical device classes + DATE = "date" + """Date. + + Unit of measurement: `None` + + ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 + """ + + ENUM = "enum" + """Enumeration. + + Provides a fixed list of options the state of the sensor can be in. + + Unit of measurement: `None` + """ + + TIMESTAMP = "timestamp" + """Timestamp. + + Unit of measurement: `None` + + ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 + """ + + # Numerical device classes, these should be aligned with NumberDeviceClass + ABSOLUTE_HUMIDITY = "absolute_humidity" + """Absolute humidity. + + Unit of measurement: `g/m³`, `mg/m³` + """ + + APPARENT_POWER = "apparent_power" + """Apparent power. + + Unit of measurement: `mVA`, `VA`, `kVA` + """ + + AQI = "aqi" + """Air Quality Index. + + Unit of measurement: `None` + """ + + AREA = "area" + """Area + + Unit of measurement: `UnitOfArea` units + """ + + ATMOSPHERIC_PRESSURE = "atmospheric_pressure" + """Atmospheric pressure. + + Unit of measurement: `UnitOfPressure` units + """ + + BATTERY = "battery" + """Percentage of battery that is left. + + Unit of measurement: `%` + """ + + BLOOD_GLUCOSE_CONCENTRATION = "blood_glucose_concentration" + """Blood glucose concentration. + + Unit of measurement: `mg/dL`, `mmol/L` + """ + + CO = "carbon_monoxide" + """Carbon Monoxide gas concentration. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `mg/m³`, `μg/m³` + """ + + CO2 = "carbon_dioxide" + """Carbon Dioxide gas concentration. + + Unit of measurement: `ppm` (parts per million) + """ + + CONDUCTIVITY = "conductivity" + """Conductivity. + + Unit of measurement: `S/cm`, `mS/cm`, `μS/cm` + """ + + CURRENT = "current" + """Current. + + Unit of measurement: `A`, `mA` + """ + + DATA_RATE = "data_rate" + """Data rate. + + Unit of measurement: UnitOfDataRate + """ + + DATA_SIZE = "data_size" + """Data size. + + Unit of measurement: UnitOfInformation + """ + + DISTANCE = "distance" + """Generic distance. + + Unit of measurement: `LENGTH_*` units + - SI /metric: `mm`, `cm`, `m`, `km` + - USCS / imperial: `in`, `ft`, `yd`, `mi` + """ + + DURATION = "duration" + """Fixed duration. + + Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `μs` + """ + + ENERGY = "energy" + """Energy. + + Use this device class for sensors measuring energy consumption, for example + electric energy consumption. + Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal` + """ + + ENERGY_DISTANCE = "energy_distance" + """Energy distance. + + Use this device class for sensors measuring energy by distance, for example the amount + of electric energy consumed by an electric car. + + Unit of measurement: `kWh/100km`, `Wh/km`, `mi/kWh`, `km/kWh` + """ + + ENERGY_STORAGE = "energy_storage" + """Stored energy. + + Use this device class for sensors measuring stored energy, for example the amount + of electric energy currently stored in a battery or the capacity of a battery. + + Unit of measurement: `J`, `kJ`, `MJ`, `GJ`, `mWh`, `Wh`, `kWh`, `MWh`, `GWh`, `TWh`, `cal`, `kcal`, `Mcal`, `Gcal` + """ + + FREQUENCY = "frequency" + """Frequency. + + Unit of measurement: `Hz`, `kHz`, `MHz`, `GHz` + """ + + GAS = "gas" + """Gas. + + Unit of measurement: + - SI / metric: `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF` + """ + + HUMIDITY = "humidity" + """Relative humidity. + + Unit of measurement: `%` + """ + + ILLUMINANCE = "illuminance" + """Illuminance. + + Unit of measurement: `lx` + """ + + IRRADIANCE = "irradiance" + """Irradiance. + + Unit of measurement: + - SI / metric: `W/m²` + - USCS / imperial: `BTU/(h⋅ft²)` + """ + + MOISTURE = "moisture" + """Moisture. + + Unit of measurement: `%` + """ + + MONETARY = "monetary" + """Amount of money. + + Unit of measurement: ISO4217 currency code + + See https://en.wikipedia.org/wiki/ISO_4217#Active_codes for active codes + """ + + NITROGEN_DIOXIDE = "nitrogen_dioxide" + """Amount of NO2. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `μg/m³` + """ + + NITROGEN_MONOXIDE = "nitrogen_monoxide" + """Amount of NO. + + Unit of measurement: `ppb` (parts per billion), `μg/m³` + """ + + NITROUS_OXIDE = "nitrous_oxide" + """Amount of N2O. + + Unit of measurement: `μg/m³` + """ + + OZONE = "ozone" + """Amount of O3. + + Unit of measurement: `ppb` (parts per billion), `ppm` (parts per million), `μg/m³` + """ + + PH = "ph" + """Potential hydrogen (acidity/alkalinity). + + Unit of measurement: Unitless + """ + + PM1 = "pm1" + """Particulate matter <= 1 μm. + + Unit of measurement: `μg/m³` + """ + + PM10 = "pm10" + """Particulate matter <= 10 μm. + + Unit of measurement: `μg/m³` + """ + + PM25 = "pm25" + """Particulate matter <= 2.5 μm. + + Unit of measurement: `μg/m³` + """ + + PM4 = "pm4" + """Particulate matter <= 4 μm. + + Unit of measurement: `μg/m³` + """ + + POWER_FACTOR = "power_factor" + """Power factor. + + Unit of measurement: `%`, `None` + """ + + POWER = "power" + """Power. + + Unit of measurement: `mW`, `W`, `kW`, `MW`, `GW`, `TW`, `BTU/h` + """ + + PRECIPITATION = "precipitation" + """Accumulated precipitation. + + Unit of measurement: UnitOfPrecipitationDepth + - SI / metric: `cm`, `mm` + - USCS / imperial: `in` + """ + + PRECIPITATION_INTENSITY = "precipitation_intensity" + """Precipitation intensity. + + Unit of measurement: UnitOfVolumetricFlux + - SI /metric: `mm/d`, `mm/h` + - USCS / imperial: `in/d`, `in/h` + """ + + PRESSURE = "pressure" + """Pressure. + + Unit of measurement: + - `mbar`, `cbar`, `bar` + - `mPa`, `Pa`, `hPa`, `kPa` + - `inHg` + - `psi` + - `inH₂O` + """ + + REACTIVE_ENERGY = "reactive_energy" + """Reactive energy. + + Unit of measurement: `varh`, `kvarh` + """ + + REACTIVE_POWER = "reactive_power" + """Reactive power. + + Unit of measurement: `mvar`, `var`, `kvar` + """ + + SIGNAL_STRENGTH = "signal_strength" + """Signal strength. + + Unit of measurement: `dB`, `dBm` + """ + + SOUND_PRESSURE = "sound_pressure" + """Sound pressure. + + Unit of measurement: `dB`, `dBA` + """ + + SPEED = "speed" + """Generic speed. + + Unit of measurement: `SPEED_*` units or `UnitOfVolumetricFlux` + - SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h`, `mm/s` + - USCS / imperial: `in/d`, `in/h`, `in/s`, `ft/s`, `mph` + - Nautical: `kn` + - Beaufort: `Beaufort` + """ + + SULPHUR_DIOXIDE = "sulphur_dioxide" + """Amount of SO2. + + Unit of measurement: `ppb` (parts per billion), `μg/m³` + """ + + TEMPERATURE = "temperature" + """Temperature. + + Unit of measurement: `°C`, `°F`, `K` + """ + + TEMPERATURE_DELTA = "temperature_delta" + """Difference of temperatures - Temperature range. + + Unit of measurement: `°C`, `°F`, `K` + """ + + VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" + """Amount of VOC. + + Unit of measurement: `μg/m³`, `mg/m³` + """ + + VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" + """Ratio of VOC. + + Unit of measurement: `ppm`, `ppb` + """ + + VOLTAGE = "voltage" + """Voltage. + + Unit of measurement: `V`, `mV`, `μV`, `kV`, `MV` + """ + + VOLUME = "volume" + """Generic volume. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `fl. oz.`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + VOLUME_STORAGE = "volume_storage" + """Generic stored volume. + + Use this device class for sensors measuring stored volume, for example the amount + of fuel in a fuel tank. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `fl. oz.`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + VOLUME_FLOW_RATE = "volume_flow_rate" + """Generic flow rate + + Unit of measurement: UnitOfVolumeFlowRate + - SI / metric: `m³/h`, `m³/min`, `m³/s`, `L/h`, `L/min`, `L/s`, `mL/s` + - USCS / imperial: `ft³/min`, `gal/min`, `gal/d` + """ + + WATER = "water" + """Water. + + Unit of measurement: + - SI / metric: `m³`, `L` + - USCS / imperial: `ft³`, `CCF`, `MCF`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + WEIGHT = "weight" + """Generic weight, represents a measurement of an object's mass. + + Weight is used instead of mass to fit with every day language. + + Unit of measurement: `MASS_*` units + - SI / metric: `μg`, `mg`, `g`, `kg` + - USCS / imperial: `oz`, `lb` + """ + + WIND_DIRECTION = "wind_direction" + """Wind direction. + + Unit of measurement: `°` + """ + + WIND_SPEED = "wind_speed" + """Wind speed. + + Unit of measurement: `SPEED_*` units + - SI /metric: `m/s`, `km/h` + - USCS / imperial: `ft/s`, `mph` + - Nautical: `kn` + - Beaufort: `Beaufort` + """ + + +class SensorStateClass(StrEnum): + """State class for sensors.""" + + MEASUREMENT = "measurement" + """The state represents a measurement in present time.""" + + MEASUREMENT_ANGLE = "measurement_angle" + """The state represents a angle measurement in present time. Currently only degrees are supported.""" + + TOTAL = "total" + """The state represents a total amount. + + For example: net energy consumption""" + + TOTAL_INCREASING = "total_increasing" + """The state represents a monotonically increasing total. + + For example: an amount of consumed gas""" diff --git a/zha/units.py b/zha/units.py index 3501c7f10..adf95a4f5 100644 --- a/zha/units.py +++ b/zha/units.py @@ -27,6 +27,7 @@ class UnitOfMass(StrEnum): class UnitOfPressure(StrEnum): """Pressure units.""" + MILLIPASCAL = "mPa" PA = "Pa" HPA = "hPa" KPA = "kPa" @@ -35,6 +36,7 @@ class UnitOfPressure(StrEnum): MBAR = "mbar" MMHG = "mmHg" INHG = "inHg" + INH2O = "inH₂O" PSI = "psi" @@ -60,14 +62,24 @@ class UnitOfPower(StrEnum): class UnitOfReactivePower(StrEnum): """Reactive power units.""" + MILLIVOLT_AMPERE_REACTIVE = "mvar" VOLT_AMPERE_REACTIVE = "var" KILO_VOLT_AMPERE_REACTIVE = "kvar" +class UnitOfReactiveEnergy(StrEnum): + """Reactive energy units.""" + + VOLT_AMPERE_REACTIVE_HOUR = "varh" + KILO_VOLT_AMPERE_REACTIVE_HOUR = "kvarh" + + class UnitOfApparentPower(StrEnum): """Apparent power units.""" + MILLIVOLT_AMPERE = "mVA" VOLT_AMPERE = "VA" + KILO_VOLT_AMPERE = "kVA" class UnitOfElectricCurrent(StrEnum): @@ -101,12 +113,15 @@ class UnitOfVolumeFlowRate(StrEnum): """Volume flow rate units.""" CUBIC_METERS_PER_HOUR = "m³/h" + CUBIC_METERS_PER_MINUTE = "m³/min" CUBIC_METERS_PER_SECOND = "m³/s" CUBIC_FEET_PER_MINUTE = "ft³/min" LITERS_PER_HOUR = "L/h" LITERS_PER_MINUTE = "L/min" LITERS_PER_SECOND = "L/s" + GALLONS_PER_HOUR = "gal/h" GALLONS_PER_MINUTE = "gal/min" + GALLONS_PER_DAY = "gal/d" MILLILITERS_PER_SECOND = "mL/s" @@ -130,6 +145,7 @@ class UnitOfVolume(StrEnum): CUBIC_FEET = "ft³" CENTUM_CUBIC_FEET = "CCF" + MILLE_CUBIC_FEET = "MCF" CUBIC_METERS = "m³" LITERS = "L" MILLILITERS = "mL" @@ -194,6 +210,7 @@ class UnitOfEnergyDistance(StrEnum): """Energy Distance units.""" KILO_WATT_HOUR_PER_100_KM = "kWh/100km" + WATT_HOUR_PER_KM = "Wh/km" MILES_PER_KILO_WATT_HOUR = "mi/kWh" KM_PER_KILO_WATT_HOUR = "km/kWh" @@ -212,6 +229,7 @@ class UnitOfSpeed(StrEnum): BEAUFORT = "Beaufort" FEET_PER_SECOND = "ft/s" INCHES_PER_SECOND = "in/s" + METERS_PER_MINUTE = "m/min" METERS_PER_SECOND = "m/s" KILOMETERS_PER_HOUR = "km/h" KNOTS = "kn" @@ -220,6 +238,7 @@ class UnitOfSpeed(StrEnum): # Concentration units +CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³" CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³" CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final = "mg/m³" CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³" @@ -227,6 +246,14 @@ class UnitOfSpeed(StrEnum): CONCENTRATION_PARTS_PER_MILLION: Final = "ppm" CONCENTRATION_PARTS_PER_BILLION: Final = "ppb" + +class UnitOfBloodGlucoseConcentration(StrEnum): + """Blood glucose concentration units.""" + + MILLIGRAMS_PER_DECILITER = "mg/dL" + MILLIMOLE_PER_LITER = "mmol/L" + + # Signal_strength units SIGNAL_STRENGTH_DECIBELS: Final = "dB" SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final = "dBm" @@ -234,12 +261,20 @@ class UnitOfSpeed(StrEnum): # Light units LIGHT_LUX: Final = "lx" +# UV Index units +UV_INDEX: Final = "UV index" + # Percentage units PERCENTAGE: Final[str] = "%" # Rotational speed units REVOLUTIONS_PER_MINUTE: Final = "rpm" +# Currency units +CURRENCY_EURO: Final = "€" +CURRENCY_DOLLAR: Final = "$" +CURRENCY_CENT: Final = "¢" + # Irradiance units class UnitOfIrradiance(StrEnum): @@ -249,6 +284,86 @@ class UnitOfIrradiance(StrEnum): BTUS_PER_HOUR_SQUARE_FOOT = "BTU/(h⋅ft²)" +class UnitOfVolumetricFlux(StrEnum): + """Volumetric flux, commonly used for precipitation intensity. + + The derivation of these units is a volume of rain amassing in a container + with constant cross section in a given time + """ + + INCHES_PER_DAY = "in/d" + """Derived from in³/(in²⋅d)""" + + INCHES_PER_HOUR = "in/h" + """Derived from in³/(in²⋅h)""" + + MILLIMETERS_PER_DAY = "mm/d" + """Derived from mm³/(mm²⋅d)""" + + MILLIMETERS_PER_HOUR = "mm/h" + """Derived from mm³/(mm²⋅h)""" + + +class UnitOfPrecipitationDepth(StrEnum): + """Precipitation depth. + + The derivation of these units is a volume of rain amassing in a container + with constant cross section + """ + + INCHES = "in" + """Derived from in³/in²""" + + MILLIMETERS = "mm" + """Derived from mm³/mm²""" + + CENTIMETERS = "cm" + """Derived from cm³/cm²""" + + +# Data units +class UnitOfInformation(StrEnum): + """Information units.""" + + BITS = "bit" + KILOBITS = "kbit" + MEGABITS = "Mbit" + GIGABITS = "Gbit" + BYTES = "B" + KILOBYTES = "kB" + MEGABYTES = "MB" + GIGABYTES = "GB" + TERABYTES = "TB" + PETABYTES = "PB" + EXABYTES = "EB" + ZETTABYTES = "ZB" + YOTTABYTES = "YB" + KIBIBYTES = "KiB" + MEBIBYTES = "MiB" + GIBIBYTES = "GiB" + TEBIBYTES = "TiB" + PEBIBYTES = "PiB" + EXBIBYTES = "EiB" + ZEBIBYTES = "ZiB" + YOBIBYTES = "YiB" + + +class UnitOfDataRate(StrEnum): + """Data rate units.""" + + BITS_PER_SECOND = "bit/s" + KILOBITS_PER_SECOND = "kbit/s" + MEGABITS_PER_SECOND = "Mbit/s" + GIGABITS_PER_SECOND = "Gbit/s" + BYTES_PER_SECOND = "B/s" + KILOBYTES_PER_SECOND = "kB/s" + MEGABYTES_PER_SECOND = "MB/s" + GIGABYTES_PER_SECOND = "GB/s" + KIBIBYTES_PER_SECOND = "KiB/s" + MEBIBYTES_PER_SECOND = "MiB/s" + GIBIBYTES_PER_SECOND = "GiB/s" + + # Kinetic energy KILOJOULES_PER_KG: Final = "KJ/kg" From 3ca1c018a30ab6c911254b263400047a4be2f71b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 8 May 2026 21:32:15 -0400 Subject: [PATCH 02/41] WIP: Compare units against Home Assistant --- pyproject.toml | 4 +- tools/compare_constants.py | 194 ++++++++++++++++++ .../platforms/sensor/device_class.py | 14 ++ zha/units.py | 10 +- 4 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 tools/compare_constants.py diff --git a/pyproject.toml b/pyproject.toml index ee6fc4da2..5beb2eb02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,8 @@ exclude = ["tests", "tests.*"] testing = [ "pytest", "coloredlogs", - "python-slugify" + "python-slugify", + "homeassistant", ] [dependency-groups] @@ -461,6 +462,7 @@ split-on-trailing-comma = false # Allow for main entry & scripts to write to stdout "script/*" = ["T20"] +"tools/*" = ["T20"] [tool.ruff.lint.mccabe] max-complexity = 25 diff --git a/tools/compare_constants.py b/tools/compare_constants.py new file mode 100644 index 000000000..831faf839 --- /dev/null +++ b/tools/compare_constants.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Compare ZHA's local copies of HA constants against the canonical `homeassistant` package. + +Run this whenever `homeassistant` is bumped to surface drift in unit enums, +device-class enums, and module-level constants that ZHA mirrors. + +Exits 1 on drift, 0 if fully in sync. +""" + +from __future__ import annotations + +from enum import Enum +import importlib +import sys +from typing import Any + +import homeassistant.const as ha_const + +import zha.units as zha_units + +# Explicit (zha, ha) pairs for enums that don't live in `zha.units`. +ENUM_PAIRS: list[tuple[str, str]] = [ + ( + "zha.application.platforms.binary_sensor.device_class.BinarySensorDeviceClass", + "homeassistant.components.binary_sensor.BinarySensorDeviceClass", + ), + ( + "zha.application.platforms.sensor.device_class.SensorDeviceClass", + "homeassistant.components.sensor.SensorDeviceClass", + ), + ( + "zha.application.platforms.sensor.device_class.SensorStateClass", + "homeassistant.components.sensor.SensorStateClass", + ), + ( + "zha.application.platforms.number.device_class.NumberDeviceClass", + "homeassistant.components.number.NumberDeviceClass", + ), + ( + "zha.application.platforms.number.device_class.NumberMode", + "homeassistant.components.number.NumberMode", + ), + # zha.application.Platform is intentionally a Zigbee-only subset of + # homeassistant.const.Platform — comparison would produce noise. +] + +# Constants intentionally defined in ZHA without an HA counterpart. +ZHA_ONLY_CONSTANTS: frozenset[str] = frozenset({"COUNT", "KILOJOULES_PER_KG"}) + + +def import_qualified(qualified_name: str) -> Any: + """Import a dotted name like 'zha.units.UnitOfTemperature'.""" + module_name, attr = qualified_name.rsplit(".", 1) + return getattr(importlib.import_module(module_name), attr) + + +def is_enum_class(obj: Any) -> bool: + """Return True if obj is an Enum subclass.""" + return isinstance(obj, type) and issubclass(obj, Enum) + + +def compare_enums(zha_enum: type[Enum], ha_enum: type[Enum]) -> list[str]: + """Return a list of difference lines between two enums (empty if identical).""" + zha_members = {m.name: m.value for m in zha_enum} + ha_members = {m.name: m.value for m in ha_enum} + + diffs: list[str] = [] + for name in sorted(set(ha_members) - set(zha_members)): + diffs.append(f" missing in ZHA: {name} = {ha_members[name]!r}") + for name in sorted(set(zha_members) - set(ha_members)): + diffs.append(f" extra in ZHA: {name} = {zha_members[name]!r}") + for name in sorted(set(zha_members) & set(ha_members)): + if zha_members[name] != ha_members[name]: + diffs.append( + f" value mismatch: {name}: ZHA={zha_members[name]!r} HA={ha_members[name]!r}" + ) + return diffs + + +def compare_units_module() -> list[tuple[str, list[str]]]: + """Compare zha.units against homeassistant.const in both directions. + + Forward: walk zha.units and compare each against the same-named symbol in + homeassistant.const. Reverse: surface UnitOf* enums in homeassistant.const + that don't exist in zha.units at all. + """ + results: list[tuple[str, list[str]]] = [] + for name in sorted(vars(zha_units)): + if name.startswith("_"): + continue + zha_obj = getattr(zha_units, name) + + # Only compare classes/strings defined in zha.units itself + # (skip re-imports like StrEnum, Final). + if is_enum_class(zha_obj): + if zha_obj.__module__ != zha_units.__name__: + continue + elif not isinstance(zha_obj, str): + continue + + if name in ZHA_ONLY_CONSTANTS: + continue + + if not hasattr(ha_const, name): + results.append((name, [" not present in homeassistant.const"])) + continue + + ha_obj = getattr(ha_const, name) + + if is_enum_class(zha_obj) and is_enum_class(ha_obj): + results.append((name, compare_enums(zha_obj, ha_obj))) + elif isinstance(zha_obj, str) and isinstance(ha_obj, str): + if zha_obj != ha_obj: + results.append( + (name, [f" value mismatch: ZHA={zha_obj!r} HA={ha_obj!r}"]) + ) + else: + results.append((name, [])) + else: + results.append( + ( + name, + [ + f" type mismatch: ZHA={type(zha_obj).__name__} HA={type(ha_obj).__name__}" + ], + ) + ) + + # Reverse scan: UnitOf* enums in homeassistant.const that ZHA doesn't have. + zha_names = {n for n in vars(zha_units) if not n.startswith("_")} + for name in sorted(vars(ha_const)): + if not name.startswith("UnitOf"): + continue + ha_obj = getattr(ha_const, name) + if not is_enum_class(ha_obj): + continue + if ha_obj.__module__ != ha_const.__name__: + continue + if name in zha_names: + continue + members = ", ".join(f"{m.name}={m.value!r}" for m in ha_obj) + results.append((name, [f" not present in zha.units (HA has: {members})"])) + + return results + + +def print_block(title: str, results: list[tuple[str, list[str]]]) -> bool: + """Print one section. Return True if any drift was found.""" + print(title) + print("=" * len(title)) + drift = False + in_sync: list[str] = [] + for name, diffs in results: + if not diffs: + in_sync.append(name) + continue + drift = True + print(f"\n{name}:") + for line in diffs: + print(line) + if in_sync: + print(f"\nin sync ({len(in_sync)}): {', '.join(in_sync)}") + print() + return drift + + +def main() -> int: + """Run the comparison and return an exit code (0 = in sync, 1 = drift).""" + units_results = compare_units_module() + + enum_results: list[tuple[str, list[str]]] = [] + for zha_name, ha_name in ENUM_PAIRS: + label = f"{zha_name.split('.')[-1]} ({zha_name} ↔ {ha_name})" + try: + zha_enum = import_qualified(zha_name) + ha_enum = import_qualified(ha_name) + except (ImportError, AttributeError) as exc: + enum_results.append((label, [f" failed to import: {exc}"])) + continue + enum_results.append((label, compare_enums(zha_enum, ha_enum))) + + drift = False + drift |= print_block("zha.units vs homeassistant.const", units_results) + drift |= print_block("Device classes & Platform enums", enum_results) + + if drift: + print("DRIFT detected — ZHA constants are out of sync with homeassistant.") + return 1 + print("All compared constants match homeassistant.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/zha/application/platforms/sensor/device_class.py b/zha/application/platforms/sensor/device_class.py index 9dd174a22..24fc70eee 100644 --- a/zha/application/platforms/sensor/device_class.py +++ b/zha/application/platforms/sensor/device_class.py @@ -31,6 +31,20 @@ class SensorDeviceClass(StrEnum): ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 """ + UPTIME = "uptime" + """Uptime. + + Represents the point in time when a device or service last restarted. + + Small drift between updates is automatically suppressed in + `SensorEntity.state` to avoid unnecessary state changes caused by clock + jitter. + + Unit of measurement: `None` + + ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 + """ + # Numerical device classes, these should be aligned with NumberDeviceClass ABSOLUTE_HUMIDITY = "absolute_humidity" """Absolute humidity. diff --git a/zha/units.py b/zha/units.py index adf95a4f5..540b2ab6c 100644 --- a/zha/units.py +++ b/zha/units.py @@ -18,7 +18,7 @@ class UnitOfMass(StrEnum): GRAMS = "g" KILOGRAMS = "kg" MILLIGRAMS = "mg" - MICROGRAMS = "µg" + MICROGRAMS = "μg" OUNCES = "oz" POUNDS = "lb" STONES = "st" @@ -85,6 +85,7 @@ class UnitOfApparentPower(StrEnum): class UnitOfElectricCurrent(StrEnum): """Electric current units.""" + MICROAMPERE = "μA" MILLIAMPERE = "mA" AMPERE = "A" @@ -93,7 +94,7 @@ class UnitOfElectricCurrent(StrEnum): class UnitOfElectricPotential(StrEnum): """Electric potential units.""" - MICROVOLT = "µV" + MICROVOLT = "μV" MILLIVOLT = "mV" VOLT = "V" KILOVOLT = "kV" @@ -103,6 +104,7 @@ class UnitOfElectricPotential(StrEnum): class UnitOfFrequency(StrEnum): """Frequency units.""" + MILLIHERTZ = "mHz" HERTZ = "Hz" KILOHERTZ = "kHz" MEGAHERTZ = "MHz" @@ -219,7 +221,7 @@ class UnitOfConductivity(StrEnum): """Conductivity units.""" SIEMENS_PER_CM = "S/cm" - MICROSIEMENS_PER_CM = "µS/cm" + MICROSIEMENS_PER_CM = "μS/cm" MILLISIEMENS_PER_CM = "mS/cm" @@ -239,7 +241,7 @@ class UnitOfSpeed(StrEnum): # Concentration units CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³" -CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³" +CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "μg/m³" CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final = "mg/m³" CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³" CONCENTRATION_PARTS_PER_CUBIC_METER: Final = "p/m³" From 219255ffd89d208ca3352e70180fda6b03d9c7e3 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 8 May 2026 21:37:19 -0400 Subject: [PATCH 03/41] Drop strict quirks requirement --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5beb2eb02..82c839257 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "zigpy-deconz==0.25.5", "zigpy-xbee==0.21.1", "zigpy-zigate==0.14.0", - "zha-quirks==1.2.0", + "zha-quirks>=1.2.0", ] [tool.setuptools.packages.find] From 9ade966f4d8f55d630fb79ca71da981d6d8ea2f8 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 27 May 2026 17:40:54 -0400 Subject: [PATCH 04/41] Regenerate diagnostics after unit change --- .../devices/ikea-of-sweden-starkvind-air-purifier-table.json | 2 +- tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json | 2 +- tests/data/devices/tze200-7bztmfm1-ts0601.json | 4 ++-- tests/data/devices/tze200-dwcarsat-ts0601.json | 4 ++-- tests/data/devices/tze200-mja3fuja-ts0601.json | 2 +- tests/data/devices/tze200-yvx5lh6k-ts0601.json | 4 ++-- tests/data/devices/tze204-c2fmom5z-ts0601.json | 4 ++-- tests/data/devices/tze204-dwcarsat-ts0601.json | 4 ++-- tests/data/devices/tze204-yvx5lh6k-ts0601.json | 4 ++-- tests/data/devices/tze284-rqcuwlsa-ts0601.json | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json index 2f2e1ca1c..f50cf30ef 100644 --- a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json +++ b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json @@ -522,7 +522,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", diff --git a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json index 7964e3bd7..252bcff00 100644 --- a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json +++ b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json @@ -450,7 +450,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", diff --git a/tests/data/devices/tze200-7bztmfm1-ts0601.json b/tests/data/devices/tze200-7bztmfm1-ts0601.json index 09f962a29..4d438cc6b 100644 --- a/tests/data/devices/tze200-7bztmfm1-ts0601.json +++ b/tests/data/devices/tze200-7bztmfm1-ts0601.json @@ -358,7 +358,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -414,7 +414,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze200-dwcarsat-ts0601.json b/tests/data/devices/tze200-dwcarsat-ts0601.json index 967a34e2b..7e9db91f2 100644 --- a/tests/data/devices/tze200-dwcarsat-ts0601.json +++ b/tests/data/devices/tze200-dwcarsat-ts0601.json @@ -458,7 +458,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -514,7 +514,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze200-mja3fuja-ts0601.json b/tests/data/devices/tze200-mja3fuja-ts0601.json index 635583552..370204956 100644 --- a/tests/data/devices/tze200-mja3fuja-ts0601.json +++ b/tests/data/devices/tze200-mja3fuja-ts0601.json @@ -373,7 +373,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze200-yvx5lh6k-ts0601.json b/tests/data/devices/tze200-yvx5lh6k-ts0601.json index 1c981b7f6..baf759420 100644 --- a/tests/data/devices/tze200-yvx5lh6k-ts0601.json +++ b/tests/data/devices/tze200-yvx5lh6k-ts0601.json @@ -289,7 +289,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -317,7 +317,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze204-c2fmom5z-ts0601.json b/tests/data/devices/tze204-c2fmom5z-ts0601.json index 9d160ada5..64c071b91 100644 --- a/tests/data/devices/tze204-c2fmom5z-ts0601.json +++ b/tests/data/devices/tze204-c2fmom5z-ts0601.json @@ -521,7 +521,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -577,7 +577,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze204-dwcarsat-ts0601.json b/tests/data/devices/tze204-dwcarsat-ts0601.json index 720cf239b..42a70a43b 100644 --- a/tests/data/devices/tze204-dwcarsat-ts0601.json +++ b/tests/data/devices/tze204-dwcarsat-ts0601.json @@ -400,7 +400,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -456,7 +456,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze204-yvx5lh6k-ts0601.json b/tests/data/devices/tze204-yvx5lh6k-ts0601.json index cd1535ff5..34f65ad7c 100644 --- a/tests/data/devices/tze204-yvx5lh6k-ts0601.json +++ b/tests/data/devices/tze204-yvx5lh6k-ts0601.json @@ -358,7 +358,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "PM25", @@ -414,7 +414,7 @@ "available": true, "group_id": null, "suggested_display_precision": 0, - "unit": "\u00b5g/m\u00b3" + "unit": "\u03bcg/m\u00b3" }, "state": { "class_name": "VOCLevel", diff --git a/tests/data/devices/tze284-rqcuwlsa-ts0601.json b/tests/data/devices/tze284-rqcuwlsa-ts0601.json index 04f4f22a0..af25a8145 100644 --- a/tests/data/devices/tze284-rqcuwlsa-ts0601.json +++ b/tests/data/devices/tze284-rqcuwlsa-ts0601.json @@ -356,7 +356,7 @@ "available": true, "group_id": null, "suggested_display_precision": null, - "unit": "\u00b5S/cm" + "unit": "\u03bcS/cm" }, "state": { "class_name": "ElectricalConductivity", From 8c995a7acc1e2192e4da728e3177c0d0f601aa15 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 8 May 2026 22:39:54 -0400 Subject: [PATCH 05/41] Move most entity discovery out of discovery --- tests/test_discover.py | 5 +-- zha/application/discovery.py | 62 ---------------------------- zha/zigbee/device.py | 80 +++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 79 deletions(-) diff --git a/tests/test_discover.py b/tests/test_discover.py index c8d69c970..c62ca46b8 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -56,7 +56,6 @@ zigpy_device_from_json, ) from zha.application import Platform -from zha.application.discovery import discover_device_entities from zha.application.gateway import Gateway from zha.application.helpers import DeviceOverridesConfiguration from zha.application.platforms import PlatformEntity, binary_sensor, sensor @@ -158,7 +157,7 @@ async def test_device_override_picks_highest_priority( zha_device = await join_zigpy_device(zha_gateway, zigpy_device) # Only one light entity will be discovered - entities = list(discover_device_entities(zha_device)) + entities = list(zha_device.discover_entities()) light_entities = [e for e in entities if e.PLATFORM == Platform.LIGHT] assert len(light_entities) == 1 assert isinstance(light_entities[0], HueLight) @@ -168,7 +167,7 @@ async def test_device_override_picks_highest_priority( f"{zigpy_device.ieee}-11": DeviceOverridesConfiguration(type=Platform.SWITCH) } - entities = list(discover_device_entities(zha_device)) + entities = list(zha_device.discover_entities()) switch_entities = [e for e in entities if e.PLATFORM == Platform.SWITCH] assert len(switch_entities) == 1 diff --git a/zha/application/discovery.py b/zha/application/discovery.py index c7f74dfe3..7fb2a47eb 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -19,7 +19,6 @@ ZCLEnumMetadata, ZCLSensorMetadata, ) -from zigpy.state import State from zigpy.zcl import Cluster, ClusterType, ReportingConfig from zha.application import Platform, const as zha_const @@ -131,67 +130,6 @@ def inner(*args: P.args, **kwargs: P.kwargs) -> Iterator[T]: return inner -@ignore_exceptions_during_iteration -def discover_device_entities(device: Device) -> Iterator[BaseEntity]: - """Discover entities for a ZHA device.""" - _LOGGER.debug( - "Discovering entities for device: %s-%s", - str(device.ieee), - device.name, - ) - - assert not device.is_active_coordinator - - for ep_id, endpoint in device.endpoints.items(): - if ep_id == 0: - continue - - _LOGGER.debug( - "Discovering entities for endpoint: %s-%s", - str(endpoint.device.ieee), - endpoint.id, - ) - - yield from discover_entities_for_endpoint(endpoint) - - yield from discover_quirks_v2_entities(device) - - -@ignore_exceptions_during_iteration -def discover_coordinator_device_entities( - device: Device, -) -> Iterator[sensor.DeviceCounterSensor]: - """Discover entities for the coordinator device.""" - _LOGGER.debug( - "Discovering entities for coordinator device: %s-%s", - str(device.ieee), - device.name, - ) - state: State = device.gateway.application_controller.state - - for counter_groups in ( - "counters", - "broadcast_counters", - "device_counters", - "group_counters", - ): - for counter_group, counters in getattr(state, counter_groups).items(): - for counter in counters: - yield sensor.DeviceCounterSensor( - zha_device=device, - counter_groups=counter_groups, - counter_group=counter_group, - counter=counter, - ) - - _LOGGER.debug( - "'%s' platform -> '%s' using %s", - Platform.SENSOR, - sensor.DeviceCounterSensor.__name__, - f"counter groups[{counter_groups}] counter group[{counter_group}] counter[{counter}]", - ) - - @ignore_exceptions_during_iteration def discover_group_entities(group: Group) -> Iterator[GroupEntity]: """Process a group and create any entities that are needed.""" diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 208f05d11..37c62f6d1 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -6,7 +6,7 @@ import asyncio from collections import defaultdict -from collections.abc import Callable, Iterable, Sequence +from collections.abc import Callable, Iterable, Iterator, Sequence import contextlib import copy import dataclasses @@ -15,7 +15,7 @@ from functools import cached_property import logging import time -from typing import TYPE_CHECKING, Any, Final, Self +from typing import TYPE_CHECKING, Any, Final from zigpy.device import Device as ZigpyDevice import zigpy.exceptions @@ -82,6 +82,7 @@ BaseEntityInfo, EntityStateChangedEvent, PlatformEntity, + sensor, ) from zha.application.platforms.update import BaseFirmwareUpdateEntity from zha.const import STATE_CHANGED @@ -791,8 +792,10 @@ def new( cls, zigpy_dev: zigpy.device.Device, gateway: Gateway, - ) -> Self: + ) -> Device: """Create new device.""" + if zigpy_dev.ieee == gateway.state.node_info.ieee: + return CoordinatorDevice(zigpy_dev, gateway) return cls(zigpy_dev, gateway) def async_update_firmware_version(self, firmware_version: str) -> None: @@ -1143,22 +1146,42 @@ def _apply_entity_metadata_changes(self, entity: PlatformEntity) -> None: if meta.new_fallback_name is not None: entity._attr_fallback_name = meta.new_fallback_name - def _discover_new_entities(self) -> None: - new_entities: Iterable[BaseEntity] + def discover_entities(self) -> Iterator[BaseEntity]: + """Yield entities for this device.""" + # TODO: purge old coordinator entities + if self.is_coordinator: + return + + for ep_id, endpoint in self.endpoints.items(): + if ep_id == 0: + continue + _LOGGER.debug( + "Discovering entities for endpoint: %s-%s", + str(endpoint.device.ieee), + endpoint.id, + ) + yield from discovery.discover_entities_for_endpoint(endpoint) + + yield from discovery.discover_quirks_v2_entities(self) + + def _discover_new_entities(self) -> None: self._discovered_entities.clear() - if self.is_active_coordinator: - new_entities = discovery.discover_coordinator_device_entities(self) - elif self.is_coordinator: - # TODO: purge old coordinator entities - new_entities = [] - else: - new_entities = discovery.discover_device_entities(self) + # Iterate defensively so a failure in any single entity construction + # does not abort discovery for the rest of the device. + iterator = iter(self.discover_entities()) + while True: + try: + entity = next(iterator) + except StopIteration: + break + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Failed to create entity during discovery") + continue - # Discover all applicable entities - for entity in new_entities: self._discovered_entities.append(entity) + if self._is_entity_removed_by_quirk(entity): continue @@ -1883,3 +1906,32 @@ def get_diagnostics_json(self): ] return info + + +class CoordinatorDevice(Device): + """ZHA wrapper for the active coordinator device.""" + + def discover_entities(self) -> Iterator[BaseEntity]: + """Yield counter sensors for the active coordinator.""" + state = self.gateway.application_controller.state + for counter_groups in ( + "counters", + "broadcast_counters", + "device_counters", + "group_counters", + ): + for counter_group, counters in getattr(state, counter_groups).items(): + for counter in counters: + yield sensor.DeviceCounterSensor( + zha_device=self, + counter_groups=counter_groups, + counter_group=counter_group, + counter=counter, + ) + + _LOGGER.debug( + "'%s' platform -> '%s' using %s", + Platform.SENSOR, + sensor.DeviceCounterSensor.__name__, + f"counter groups[{counter_groups}] counter group[{counter_group}] counter[{counter}]", + ) From f96f25b9397bc06afb343573d6237d416b80a771 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 27 May 2026 17:37:33 -0400 Subject: [PATCH 06/41] Allow for whole-device registration --- zha/zigbee/device.py | 70 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 37c62f6d1..396585315 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -26,6 +26,7 @@ from zigpy.types.named import EUI64, NWK, ExtendedPanId from zigpy.typing import UNDEFINED, UndefinedType import zigpy.zcl +from zigpy.zcl import ClusterType from zigpy.zcl.clusters import Cluster from zigpy.zcl.clusters.general import Basic, Groups, Identify, Ota from zigpy.zcl.foundation import ( @@ -191,6 +192,38 @@ def get_device_automation_triggers( } +def _read_current_firmware_version( + zigpy_device: zigpy.device.Device, +) -> int | None: + """Read `current_file_version` from the device's OTA cluster, or None.""" + try: + ota = zigpy_device.find_cluster( + cluster_id=Ota.cluster_id, cluster_type=ClusterType.Client + ) + except ValueError: + return None + return ota.get(Ota.AttributeDefs.current_file_version.id) + + +@dataclass(frozen=True) +class DeviceMatch: + """Fingerprint criteria for matching a `Device` subclass to a zigpy device.""" + + manufacturers: frozenset[str] | None = None + models: frozenset[str] | None = None + firmware_versions: tuple[int | None, int | None] = (None, None) + firmware_version_allow_missing: bool = True + + +DEVICE_QUIRKS: list[type[Device]] = [] + + +def register_device(cls: type[Device]) -> type[Device]: + """Register a `Device` subclass for fingerprint-based dispatch.""" + DEVICE_QUIRKS.append(cls) + return cls + + @dataclass(frozen=True, kw_only=True) class ClusterBinding: """Describes a cluster binding.""" @@ -369,6 +402,10 @@ class Device(LogMixin, EventBase): unique_id: str + # The base `Device` is the universal fallback (matches anything) and is never + # iterated through `DEVICE_QUIRKS`. + _device_match: DeviceMatch = DeviceMatch() + # Cached properties that depend on the zigpy device and must be invalidated # when the underlying device is swapped (e.g. after a re-interview). _ZIGPY_CACHED_PROPERTIES: Final = ( @@ -417,6 +454,32 @@ def __init__( self._init_from_zigpy_device(zigpy_device) + @classmethod + def matches(cls, zigpy_device: zigpy.device.Device) -> bool: + """Return True if this Device subclass should wrap `zigpy_device`.""" + m = cls._device_match + if ( + m.manufacturers is not None + and zigpy_device.manufacturer not in m.manufacturers + ): + return False + if m.models is not None and zigpy_device.model not in m.models: + return False + + min_fw, max_fw = m.firmware_versions + if min_fw is not None or max_fw is not None: + current = _read_current_firmware_version(zigpy_device) + if current is None: + if not m.firmware_version_allow_missing: + return False + else: + if min_fw is not None and current < min_fw: + return False + if max_fw is not None and current >= max_fw: + return False + + return True + def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: """(Re-)initialize device state from a zigpy device. @@ -793,9 +856,14 @@ def new( zigpy_dev: zigpy.device.Device, gateway: Gateway, ) -> Device: - """Create new device.""" + """Create new device, dispatching to a registered subclass when applicable.""" if zigpy_dev.ieee == gateway.state.node_info.ieee: return CoordinatorDevice(zigpy_dev, gateway) + + for quirk_cls in DEVICE_QUIRKS: + if quirk_cls.matches(zigpy_dev): + return quirk_cls(zigpy_dev, gateway) + return cls(zigpy_dev, gateway) def async_update_firmware_version(self, firmware_version: str) -> None: From 8c826f391e61f704bb0eb12def13bcce8341ff69 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 11 May 2026 11:34:55 -0400 Subject: [PATCH 07/41] WIP: register quirks device subclasses with zigpy --- tests/common.py | 5 ++--- zha/application/gateway.py | 9 +++++++- zha/zigbee/device.py | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/tests/common.py b/tests/common.py index 3f45f52cd..9dadbce60 100644 --- a/tests/common.py +++ b/tests/common.py @@ -13,7 +13,6 @@ from zigpy.application import ControllerApplication from zigpy.const import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE -from zigpy.quirks import get_device as quirks_get_device import zigpy.types as t import zigpy.zcl import zigpy.zcl.foundation as zcl_f @@ -423,7 +422,7 @@ def zigpy_device_from_device_data( if quirk: device = quirk(app, device.ieee, device.nwk, device) else: - device = quirks_get_device(device) + device = app._device_resolver(device) for epid, ep in device_data["endpoints"].items(): try: @@ -601,7 +600,7 @@ def create_mock_zigpy_device( if quirk: device = quirk(zigpy_app_controller, device.ieee, device.nwk, device) else: - device = quirks_get_device(device) + device = zigpy_app_controller._device_resolver(device) if patch_cluster: for endpoint in (ep for epid, ep in device.endpoints.items() if epid): diff --git a/zha/application/gateway.py b/zha/application/gateway.py index e73478477..c23a7fb73 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -55,7 +55,13 @@ gather_with_limited_concurrency, ) from zha.event import EventBase -from zha.zigbee.device import Device, DeviceInfo, DeviceStatus, ExtendedDeviceInfo +from zha.zigbee.device import ( + Device, + DeviceInfo, + DeviceStatus, + ExtendedDeviceInfo, + resolve_device, +) from zha.zigbee.group import Group, GroupInfo, GroupMemberReference BLOCK_LOG_TIMEOUT: Final[int] = 60 @@ -251,6 +257,7 @@ async def _async_initialize(self) -> None: config=app_config, auto_form=False, start_radio=False, + device_resolver=resolve_device, ) await self.application_controller.startup(auto_form=True) diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 396585315..203650c31 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -224,6 +224,47 @@ def register_device(cls: type[Device]) -> type[Device]: return cls +@dataclass(frozen=True) +class Replace: + """Replace a cluster on a device's endpoint with a CustomCluster subclass.""" + + endpoint_id: int + cluster_id: int + cluster_type: ClusterType + replacement: type[zigpy.quirks.CustomCluster] + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + endpoint = device.endpoints[self.endpoint_id] + if self.cluster_type is ClusterType.Server: + endpoint.in_clusters.pop(self.cluster_id, None) + endpoint.add_input_cluster( + self.cluster_id, self.replacement(endpoint, is_server=True) + ) + else: + endpoint.out_clusters.pop(self.cluster_id, None) + endpoint.add_output_cluster( + self.cluster_id, self.replacement(endpoint, is_server=False) + ) + + +def resolve_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: + """Zigpy device resolver.""" + for cls in DEVICE_QUIRKS: + if cls.matches(zigpy_device): + _LOGGER.warning( + "v3 resolver matched %s for %s/%s, applying %d ops", + cls.__name__, + zigpy_device.manufacturer, + zigpy_device.model, + len(cls._operations), + ) + for op in cls._operations: + op.apply(zigpy_device) + return zigpy_device + return zigpy.quirks.get_device(zigpy_device) + + @dataclass(frozen=True, kw_only=True) class ClusterBinding: """Describes a cluster binding.""" @@ -405,6 +446,7 @@ class Device(LogMixin, EventBase): # The base `Device` is the universal fallback (matches anything) and is never # iterated through `DEVICE_QUIRKS`. _device_match: DeviceMatch = DeviceMatch() + _operations: tuple[Replace, ...] = () # Cached properties that depend on the zigpy device and must be invalidated # when the underlying device is swapped (e.g. after a re-interview). From b9b1a377d4e59891dd325fbfbb4b2d7895374f70 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:54:17 -0400 Subject: [PATCH 08/41] WIP --- pyproject.toml | 184 +- tests/common.py | 5 +- tests/test_climate.py | 5 +- tests/test_discover.py | 53 +- tests/test_switch.py | 17 +- uv.lock | 3574 ++++++++++++++++- zha/application/discovery.py | 187 +- zha/application/gateway.py | 21 +- zha/application/platforms/__init__.py | 2 +- .../platforms/binary_sensor/__init__.py | 2 +- zha/application/platforms/button/__init__.py | 5 +- zha/application/platforms/number/__init__.py | 3 +- zha/application/platforms/select.py | 2 +- zha/application/platforms/sensor/__init__.py | 2 +- zha/application/platforms/switch.py | 2 +- zha/zigbee/device.py | 171 +- 16 files changed, 3699 insertions(+), 536 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 82c839257..39014687d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,8 @@ readme = "README.md" license = {text = "Apache-2.0"} requires-python = ">=3.12" dependencies = [ + "attrs", + "frozendict", "zigpy==1.5.1", "bellows==0.49.2", "zigpy-znp==1.1.0", @@ -62,6 +64,10 @@ ci = [ [tool.uv] default-groups = ["testing"] +# Temporary, until the `device_resolver` hook is released in zigpy +[tool.uv.sources] +zigpy = { path = "../zigpy", editable = true } + [tool.setuptools-git-versioning] enabled = true @@ -99,206 +105,192 @@ non_interactive = true [[tool.mypy.overrides]] module = "zha.application.discovery" disable_error_code = [ - "no-any-return", # 3 + "no-any-return", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.update" +module = "zha.application.platforms.lock" disable_error_code = [ - "arg-type", # 3 + "no-untyped-def", # 1 ] [[tool.mypy.overrides]] -module = "zha.async_" +module = "zha.application.platforms.select" disable_error_code = [ - "arg-type", # 2 - "no-untyped-def", # 1 + "override", # 1 ] [[tool.mypy.overrides]] -module = "zha.zigbee.cluster_config" +module = "zha.application.platforms.virtual" disable_error_code = [ - "arg-type", # 2 - "no-untyped-call", # 1 + "no-any-return", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.lock" +module = "zha.zigbee.endpoint" disable_error_code = [ - "arg-type", # 3 - "no-untyped-def", # 1 + "no-any-return", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.siren" +module = "zha.quirks.metadata" disable_error_code = [ - "arg-type", # 2 - "assignment", # 2 + "no-untyped-def", # 1 + "var-annotated", # 1 ] [[tool.mypy.overrides]] -module = "zha.event" +module = "zha.application.platforms.binary_sensor" disable_error_code = [ - "no-untyped-def", # 4 + "no-any-return", # 1 + "no-untyped-def", # 1 + "override", # 1 ] [[tool.mypy.overrides]] module = "zha.application.platforms.device_tracker" disable_error_code = [ - "arg-type", # 2 "no-untyped-def", # 3 ] [[tool.mypy.overrides]] -module = "zha.zigbee.group" +module = "zha.application.platforms.switch" disable_error_code = [ - "no-untyped-def", # 3 - "return-value", # 2 + "arg-type", # 1 + "no-any-return", # 1 + "override", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms" +module = "zha.async_" disable_error_code = [ - "arg-type", # 1 - "assignment", # 1 - "attr-defined", # 2 - "no-any-return", # 2 + "arg-type", # 2 + "no-untyped-def", # 1 ] [[tool.mypy.overrides]] module = "zha.application.platforms.button" disable_error_code = [ - "arg-type", # 5 "assignment", # 2 "override", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.gateway" +module = "zha.application.platforms.number" disable_error_code = [ - "arg-type", # 3 - "assignment", # 2 - "no-untyped-call", # 4 - "return-value", # 1 + "arg-type", # 1 + "no-any-return", # 2 + "override", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.fan" +module = "zha.event" disable_error_code = [ - "arg-type", # 5 - "index", # 1 - "no-any-return", # 3 - "no-untyped-def", # 1 + "no-untyped-def", # 4 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.alarm_control_panel" +module = "zha.quirks" disable_error_code = [ "arg-type", # 1 - "no-untyped-call", # 1 - "no-untyped-def", # 11 + "no-any-return", # 1 + "union-attr", # 3 ] [[tool.mypy.overrides]] -module = "zha.application.helpers" +module = "zha.zigbee.group" disable_error_code = [ - "arg-type", # 2 + "no-any-return", # 2 + "no-untyped-def", # 3 +] + +[[tool.mypy.overrides]] +module = "zha.application.gateway" +disable_error_code = [ + "no-any-return", # 2 "no-untyped-call", # 4 - "no-untyped-def", # 10 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.cover" +module = "zha.application.platforms" disable_error_code = [ - "arg-type", # 3 - "no-any-return", # 3 - "no-untyped-call", # 6 - "no-untyped-def", # 5 + "arg-type", # 1 + "assignment", # 1 + "attr-defined", # 2 + "no-any-return", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.binary_sensor" +module = "zha.application.platforms.fan" disable_error_code = [ - "arg-type", # 22 - "no-any-return", # 1 - "no-untyped-call", # 1 + "arg-type", # 2 + "no-any-return", # 3 "no-untyped-def", # 1 - "override", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.light" +module = "zha.application.platforms.alarm_control_panel" disable_error_code = [ - "arg-type", # 9 - "no-any-return", # 4 - "no-untyped-call", # 11 - "no-untyped-def", # 8 - "type-var", # 2 + "no-untyped-def", # 11 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.select" +module = "zha.application.platforms.cover" disable_error_code = [ - "arg-type", # 33 - "override", # 1 + "no-any-return", # 2 + "no-untyped-call", # 6 + "no-untyped-def", # 5 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.virtual" +module = "zha.application.helpers" disable_error_code = [ - "arg-type", # 40 - "no-any-return", # 1 + "arg-type", # 1 + "no-untyped-call", # 4 + "no-untyped-def", # 10 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.climate" +module = "zha.quirks.v2" disable_error_code = [ - "arg-type", # 9 - "call-overload", # 4 - "no-any-return", # 23 - "no-untyped-def", # 8 + "arg-type", # 18 + "assignment", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.switch" +module = "zha.application.platforms.light" disable_error_code = [ - "arg-type", # 41 - "no-any-return", # 1 - "no-untyped-call", # 2 - "override", # 1 + "arg-type", # 4 + "no-any-return", # 4 + "no-untyped-call", # 7 + "no-untyped-def", # 8 ] [[tool.mypy.overrides]] -module = "zha.zigbee.device" +module = "zha.application.platforms.sensor" disable_error_code = [ - "arg-type", # 12 - "assignment", # 2 - "no-any-return", # 4 - "no-untyped-call", # 4 - "no-untyped-def", # 10 - "return-value", # 1 - "union-attr", # 18 + "arg-type", # 2 + "no-any-return", # 13 + "no-untyped-call", # 2 + "no-untyped-def", # 3 + "override", # 3 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.number" +module = "zha.application.platforms.climate" disable_error_code = [ - "arg-type", # 56 - "no-any-return", # 2 - "override", # 1 + "no-any-return", # 21 + "no-untyped-def", # 8 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.sensor" +module = "zha.zigbee.device" disable_error_code = [ - "arg-type", # 88 - "assignment", # 1 - "attr-defined", # 5 - "no-any-return", # 9 - "no-untyped-call", # 4 - "no-untyped-def", # 3 - "operator", # 4 - "override", # 3 + "arg-type", # 10 + "assignment", # 2 + "no-any-return", # 14 + "no-untyped-call", # 1 + "no-untyped-def", # 10 ] # Autogenerated mypy overrides: end diff --git a/tests/common.py b/tests/common.py index 9dadbce60..78c6c6693 100644 --- a/tests/common.py +++ b/tests/common.py @@ -19,6 +19,7 @@ import zigpy.zdo.types as zdo_t from zha.application import Platform +from zha.quirks import resolve_device from zha.application.gateway import Gateway from zha.application.platforms import BaseEntity, GroupEntity, PlatformEntity from zha.zigbee.device import Device @@ -422,7 +423,7 @@ def zigpy_device_from_device_data( if quirk: device = quirk(app, device.ieee, device.nwk, device) else: - device = app._device_resolver(device) + device = resolve_device(device) for epid, ep in device_data["endpoints"].items(): try: @@ -600,7 +601,7 @@ def create_mock_zigpy_device( if quirk: device = quirk(zigpy_app_controller, device.ieee, device.nwk, device) else: - device = zigpy_app_controller._device_resolver(device) + device = resolve_device(device) if patch_cluster: for endpoint in (ep for epid, ep in device.endpoints.items() if epid): diff --git a/tests/test_climate.py b/tests/test_climate.py index e23dd0420..437ff2d9c 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -39,6 +39,7 @@ PRESET_TEMP_MANUAL, ) from zha.application.gateway import Gateway +from zha.quirks import resolve_device from zha.application.platforms.climate import ( HVAC_MODE_2_SYSTEM, SEQ_OF_OPERATION, @@ -1641,7 +1642,7 @@ async def test_thermostat_quirkv2_local_temperature_calibration_config_overwrite zigpy_device.endpoints[1].thermostat.PLUGGED_ATTR_READS = ZCL_ATTR_PLUG ( - QuirkBuilder("unk_manufacturer", "FakeModel", zigpy.quirks.DEVICE_REGISTRY) + QuirkBuilder("unk_manufacturer", "FakeModel") # Local temperature calibration. .number( Thermostat.AttributeDefs.local_temperature_calibration.name, @@ -1656,7 +1657,7 @@ async def test_thermostat_quirkv2_local_temperature_calibration_config_overwrite .add_to_registry() ) - zigpy_device = zigpy.quirks.DEVICE_REGISTRY.get_device(zigpy_device) + zigpy_device = resolve_device(zigpy_device) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert zha_device.model == "FakeModel" diff --git a/tests/test_discover.py b/tests/test_discover.py index c62ca46b8..e696ee917 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -1,6 +1,7 @@ """Test ZHA device discovery.""" import asyncio +import attrs from collections import defaultdict from collections.abc import Callable import contextlib @@ -57,6 +58,11 @@ ) from zha.application import Platform from zha.application.gateway import Gateway +from zha.quirks import ( + DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, + ZHA_DEVICE_CLASS_ATTRIBUTE, + resolve_device, +) from zha.application.helpers import DeviceOverridesConfiguration from zha.application.platforms import PlatformEntity, binary_sensor, sensor from zha.application.platforms.const import PHILIPS_REMOTE_CLUSTER @@ -223,9 +229,7 @@ async def test_quirks_v2_entity_discovery( ) ( - QuirkBuilder( - "Ikea of Sweden", "TRADFRI remote control", zigpy.quirks.DEVICE_REGISTRY - ) + QuirkBuilder("Ikea of Sweden", "TRADFRI remote control") .replaces(PowerConfig1CRCluster) .replaces(ScenesCluster, cluster_type=ClusterType.Client) .number( @@ -243,7 +247,7 @@ async def test_quirks_v2_entity_discovery( .add_to_registry() ) - zigpy_device = zigpy.quirks.DEVICE_REGISTRY.get_device(zigpy_device) + zigpy_device = resolve_device(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -345,7 +349,7 @@ class FakeXiaomiAqaraDriverE1(XiaomiAqaraDriverE1): manufacturer="LUMI", model="lumi.curtain.agl006", ) - aqara_E1_device = zigpy.quirks.DEVICE_REGISTRY.get_device(aqara_E1_device) + aqara_E1_device = resolve_device(aqara_E1_device) aqara_E1_device.endpoints[1].opple_cluster.PLUGGED_ATTR_READS = { "hand_open": 0, @@ -430,7 +434,7 @@ def _get_test_device( ) quirk_builder = ( - QuirkBuilder(manufacturer, model, zigpy.quirks.DEVICE_REGISTRY) + QuirkBuilder(manufacturer, model) .replaces(PowerConfig1CRCluster) .replaces(ScenesCluster, cluster_type=ClusterType.Client) .number( @@ -470,7 +474,7 @@ def _get_test_device( quirk_builder.add_to_registry() - zigpy_device = zigpy.quirks.DEVICE_REGISTRY.get_device(zigpy_device) + zigpy_device = resolve_device(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -492,7 +496,10 @@ async def test_quirks_v2_entity_no_metadata( zigpy_device = _get_test_device( zha_gateway, "Ikea of Sweden2", "TRADFRI remote control2" ) - setattr(zigpy_device, "_exposes_metadata", {}) + quirk_cls = getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE) + quirk_cls._quirk_definition = attrs.evolve( + quirk_cls._quirk_definition, entity_metadata=() + ) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert ( f"Device: {str(zigpy_device.ieee)}-{zha_device.name} does not expose any quirks v2 entities" @@ -512,18 +519,25 @@ async def test_quirks_v2_entity_discovery_errors( # Inject unknown quirks v2 entity metadata class UnknownEntityMetadata: + endpoint_id = 1 + cluster_id = zigpy.zcl.clusters.general.OnOff.cluster_id + cluster_type = ClusterType.Server entity_platform = Platform.UPDATE - zigpy_device._exposes_metadata[ - (1, zigpy.zcl.clusters.general.OnOff.cluster_id, ClusterType.Server) - ].append(UnknownEntityMetadata()) + quirk_cls = getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE) + quirk_cls._quirk_definition = attrs.evolve( + quirk_cls._quirk_definition, + entity_metadata=( + *quirk_cls._quirk_definition.entity_metadata, + UnknownEntityMetadata(), + ), + ) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert ( f"Device: {zigpy_device.ieee}-{zha_device.name} does not have an" - " endpoint with id: 3 - unable to create entity with" - " cluster details: (3, 6, )" + " endpoint with id: 3 - unable to create entity with metadata:" ) in caplog.text time_cluster_id = zigpy.zcl.clusters.general.Time.cluster_id @@ -531,7 +545,7 @@ class UnknownEntityMetadata: assert ( f"Device: {zigpy_device.ieee}-{zha_device.name} does not have a" f" cluster with id: {time_cluster_id} - unable to create entity with" - f" cluster details: (1, {time_cluster_id}, )" + " metadata:" ) in caplog.text device_info = f"{zigpy_device.ieee}-{zha_device.name}" @@ -632,8 +646,8 @@ async def test_quirks_v2_metadata_bad_device_classes( assert expected_exception_string in caplog.text - # remove the device so we don't pollute the rest of the tests - zigpy.quirks.DEVICE_REGISTRY.remove(zigpy_device) + # remove the quirk so we don't pollute the rest of the tests + QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE)) async def test_quirks_v2_fallback_name(zha_gateway: Gateway) -> None: @@ -743,6 +757,13 @@ async def test_devices_from_files( loaded_device_data = json.loads( json.dumps(zha_device.get_diagnostics_json(), cls=ZhaJsonEncoder) ) + + # The quirk class path varies with the quirks implementation (v2 quirks + # used to all be `zigpy.quirks.v2.CustomDeviceV2`, compiled ZHA quirks + # name the defining module); `quirk_applied` still has to match. + del loaded_device_data["quirk_class"] + del device_data["quirk_class"] + assert loaded_device_data == device_data # Assert identify called on join for devices that support it diff --git a/tests/test_switch.py b/tests/test_switch.py index bf37af7cb..68149eea0 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -14,8 +14,8 @@ ) from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha -from zigpy.quirks import DEVICE_REGISTRY, CustomCluster, CustomDevice -from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder +from zigpy.quirks import CustomCluster, CustomDevice +from zigpy.quirks.v2 import QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import closures, general @@ -39,6 +39,7 @@ ) from zha.application import Platform from zha.application.gateway import Gateway +from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_device from zha.application.platforms import GroupEntity, PlatformEntity from zha.exceptions import ZHAException from zha.zigbee.device import Device @@ -509,9 +510,9 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = resolve_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = {"window_detection_function": 5} update_attribute_cache(cluster) @@ -590,9 +591,9 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = resolve_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = {"window_detection_function": 5} update_attribute_cache(cluster) @@ -671,9 +672,9 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = resolve_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = { "window_detection_function": 5, diff --git a/uv.lock b/uv.lock index 53a4494bb..89bfe5e2a 100644 --- a/uv.lock +++ b/uv.lock @@ -3,7 +3,99 @@ revision = 3 requires-python = ">=3.12" resolution-markers = [ "python_full_version >= '3.15'", - "python_full_version < '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] + +[[package]] +name = "acme" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "josepy", marker = "python_full_version < '3.14.2'" }, + { name = "pyopenssl", version = "24.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "pyrfc3339", marker = "python_full_version < '3.14.2'" }, + { name = "pytz", marker = "python_full_version < '3.14.2'" }, + { name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "setuptools", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/eb/7731d7e3c099adaaa814ecf12bd1f6741605b8d80e9b33b69a51849cf280/acme-2.10.0.tar.gz", hash = "sha256:de110d6550f22094c920ad6022f4b329380a6bd8f58dd671135c6226c3a470cc", size = 91659, upload-time = "2024-04-03T23:28:36.472Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c8/39ca7907482766a3ea3af436474735a0051dba97253b1241794a58eb88b9/acme-2.10.0-py3-none-any.whl", hash = "sha256:4a46172573608195a44a13e10f2b7c862cfd1f4046a913c765a936ba1bb7261d", size = 95757, upload-time = "2024-04-03T23:27:59.712Z" }, +] + +[[package]] +name = "acme" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "josepy", marker = "python_full_version >= '3.14.2'" }, + { name = "pyopenssl", version = "26.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "pyrfc3339", marker = "python_full_version >= '3.14.2'" }, + { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/4f/813bc8c11a2b705e9c18d0e806aa8f069aa8faca58188500c781a793b364/acme-5.4.0.tar.gz", hash = "sha256:906e6cca7f58b5526c0ddfe3d71a7a41f8fa10acf3b083dd35cf619b5b015ca8", size = 90596, upload-time = "2026-03-10T19:06:17.383Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/f6/2a35be29ce639e7cc86c95fc67f98cd5fb93d51c6cd68aa8c07a89de40db/acme-5.4.0-py3-none-any.whl", hash = "sha256:e05c50f64958fe26475df21e3a18949993eff9d68a7e771f215c56b83e9e66cc", size = 94962, upload-time = "2026-03-10T19:05:49.192Z" }, +] + +[[package]] +name = "aiodns" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "pycares", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/84/41a6a2765abc124563f5380e76b9b24118977729e25a84112f8dfb2b33dc/aiodns-3.2.0.tar.gz", hash = "sha256:62869b23409349c21b072883ec8998316b234c9a9e36675756e8e317e8768f72", size = 7823, upload-time = "2024-03-31T11:27:30.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/14/13c65b1bd59f7e707e0cc0964fbab45c003f90292ed267d159eeeeaa2224/aiodns-3.2.0-py3-none-any.whl", hash = "sha256:e443c0c27b07da3174a109fd9e736d69058d808f144d3c9d56dbd1776964c5f5", size = 5735, upload-time = "2024-03-31T11:27:28.615Z" }, +] + +[[package]] +name = "aiodns" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "pycares", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/22/a2d928e0e42baad0471d12ec44c71152ac870486e8298dddb2893b888c29/aiodns-4.0.4.tar.gz", hash = "sha256:cb10e0c0d2591636716ad2fe402e977c16d71bdaf76bb8cb49e8a6633596f736", size = 29918, upload-time = "2026-05-20T01:54:15.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/70/72e4ab117425ccdc4d10bd523a94c1baa051a15586057d64a4c6888f9e3f/aiodns-4.0.4-py3-none-any.whl", hash = "sha256:c24dd605bac70a1676ce503f967a98483ff163507198557d8e9db16267e6cfd2", size = 12696, upload-time = "2026-05-20T01:54:14.134Z" }, +] + +[[package]] +name = "aiogithubapi" +version = "26.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "backoff", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/4c/1319dc5f7772f2ad960bd84d47d972a64aa596b6cdd966956e5e85501333/aiogithubapi-26.0.0.tar.gz", hash = "sha256:71ee97ebb242378535551ede80605384d1d3536b83e68dae938ce201d06dac33", size = 37561, upload-time = "2026-02-28T09:56:52.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/a0/3102dbe2457eceb2e71f295e5d08f0f07c4982f82366cce34b2a332e348e/aiogithubapi-26.0.0-py3-none-any.whl", hash = "sha256:156b5f9217d23cb0eb65e233b19c10f499f1ba3bcf1d7d65e3a463c034e3813a", size = 72608, upload-time = "2026-02-28T09:56:51.23Z" }, ] [[package]] @@ -15,18 +107,73 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] +[[package]] +name = "aiohttp" +version = "3.10.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "aiohappyeyeballs", marker = "python_full_version < '3.14.2'" }, + { name = "aiosignal", marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "frozenlist", marker = "python_full_version < '3.14.2'" }, + { name = "multidict", marker = "python_full_version < '3.14.2'" }, + { name = "yarl", version = "1.9.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/28/ca549838018140b92a19001a8628578b0f2a3b38c16826212cc6f706e6d4/aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", size = 7524360, upload-time = "2024-08-19T20:10:48.406Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/1c/74f9dad4a2fc4107e73456896283d915937f48177b99867b63381fadac6e/aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", size = 583468, upload-time = "2024-08-19T20:08:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/12/29/68d090551f2b58ce76c2b436ced8dd2dfd32115d41299bf0b0c308a5483c/aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", size = 394066, upload-time = "2024-08-19T20:08:59.569Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f7/971f88b4cdcaaa4622925ba7d86de47b48ec02a9040a143514b382f78da4/aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", size = 389098, upload-time = "2024-08-19T20:09:00.965Z" }, + { url = "https://files.pythonhosted.org/packages/f1/5a/fe3742efdce551667b2ddf1158b27c5b8eb1edc13d5e14e996e52e301025/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", size = 1332742, upload-time = "2024-08-19T20:09:02.29Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/a25c0334a1845eb4967dff279151b67ca32a948145a5812ed660ed900868/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", size = 1372134, upload-time = "2024-08-19T20:09:03.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/3d/33c1d8efc2d8ec36bff9a8eca2df9fdf8a45269c6e24a88e74f2aa4f16bd/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", size = 1414413, upload-time = "2024-08-19T20:09:05.74Z" }, + { url = "https://files.pythonhosted.org/packages/64/74/0f1ddaa5f0caba1d946f0dd0c31f5744116e4a029beec454ec3726d3311f/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", size = 1328107, upload-time = "2024-08-19T20:09:07.476Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/c10118f0ad50e4093227234f71fd0abec6982c29367f65f32ee74ed652c4/aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", size = 1280126, upload-time = "2024-08-19T20:09:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c9/77e3d648d97c03a42acfe843d03e97be3c5ef1b4d9de52e5bd2d28eed8e7/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", size = 1292660, upload-time = "2024-08-19T20:09:10.853Z" }, + { url = "https://files.pythonhosted.org/packages/7e/5d/99c71f8e5c8b64295be421b4c42d472766b263a1fe32e91b64bf77005bf2/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", size = 1300988, upload-time = "2024-08-19T20:09:12.341Z" }, + { url = "https://files.pythonhosted.org/packages/8f/2c/76d2377dd947f52fbe8afb19b18a3b816d66c7966755c04030f93b1f7b2d/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", size = 1339268, upload-time = "2024-08-19T20:09:14.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e6/3d9d935cc705d57ed524d82ec5d6b678a53ac1552720ae41282caa273584/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", size = 1366993, upload-time = "2024-08-19T20:09:15.725Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c2/f7eed4d602f3f224600d03ab2e1a7734999b0901b1c49b94dc5891340433/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", size = 1329459, upload-time = "2024-08-19T20:09:17.556Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8f/27f205b76531fc592abe29e1ad265a16bf934a9f609509c02d765e6a8055/aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", size = 356968, upload-time = "2024-08-19T20:09:19.14Z" }, + { url = "https://files.pythonhosted.org/packages/39/8c/4f6c0b2b3629f6be6c81ab84d9d577590f74f01d4412bfc4067958eaa1e1/aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", size = 377650, upload-time = "2024-08-19T20:09:20.502Z" }, + { url = "https://files.pythonhosted.org/packages/7b/b9/03b4327897a5b5d29338fa9b514f1c2f66a3e4fc88a4e40fad478739314d/aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", size = 576994, upload-time = "2024-08-19T20:09:22.439Z" }, + { url = "https://files.pythonhosted.org/packages/67/1b/20c2e159cd07b8ed6dde71c2258233902fdf415b2fe6174bd2364ba63107/aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", size = 390684, upload-time = "2024-08-19T20:09:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6b/ff83b34f157e370431d8081c5d1741963f4fb12f9aaddb2cacbf50305225/aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", size = 386176, upload-time = "2024-08-19T20:09:25.259Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/6e92817eb657de287560962df4959b7ddd22859c4b23a0309e2d3de12538/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", size = 1303310, upload-time = "2024-08-19T20:09:27.098Z" }, + { url = "https://files.pythonhosted.org/packages/04/29/200518dc7a39c30ae6d5bc232d7207446536e93d3d9299b8e95db6e79c54/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", size = 1340445, upload-time = "2024-08-19T20:09:29.228Z" }, + { url = "https://files.pythonhosted.org/packages/8e/20/53f7bba841ba7b5bb5dea580fea01c65524879ba39cb917d08c845524717/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", size = 1385121, upload-time = "2024-08-19T20:09:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b4/d99354ad614c48dd38fb1ee880a1a54bd9ab2c3bcad3013048d4a1797d3a/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", size = 1299669, upload-time = "2024-08-19T20:09:32.383Z" }, + { url = "https://files.pythonhosted.org/packages/51/39/ca1de675f2a5729c71c327e52ac6344e63f036bd37281686ae5c3fb13bfb/aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", size = 1252638, upload-time = "2024-08-19T20:09:34.287Z" }, + { url = "https://files.pythonhosted.org/packages/54/cf/a3ae7ff43138422d477348e309ef8275779701bf305ff6054831ef98b782/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", size = 1266889, upload-time = "2024-08-19T20:09:36.427Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7a/c6027ad70d9fb23cf254a26144de2723821dade1a624446aa22cd0b6d012/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", size = 1266249, upload-time = "2024-08-19T20:09:38.545Z" }, + { url = "https://files.pythonhosted.org/packages/64/fd/ed136d46bc2c7e3342fed24662b4827771d55ceb5a7687847aae977bfc17/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", size = 1311036, upload-time = "2024-08-19T20:09:40.501Z" }, + { url = "https://files.pythonhosted.org/packages/76/9a/43eeb0166f1119256d6f43468f900db1aed7fbe32069d2a71c82f987db4d/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", size = 1338756, upload-time = "2024-08-19T20:09:42.57Z" }, + { url = "https://files.pythonhosted.org/packages/d5/bc/d01ff0810b3f5e26896f76d44225ed78b088ddd33079b85cd1a23514318b/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", size = 1299976, upload-time = "2024-08-19T20:09:44.422Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c9/50a297c4f7ab57a949f4add2d3eafe5f3e68bb42f739e933f8b32a092bda/aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", size = 355609, upload-time = "2024-08-19T20:09:45.95Z" }, + { url = "https://files.pythonhosted.org/packages/65/28/aee9d04fb0b3b1f90622c338a08e54af5198e704a910e20947c473298fd0/aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", size = 375697, upload-time = "2024-08-19T20:09:47.63Z" }, +] + [[package]] name = "aiohttp" version = "3.13.5" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, + { name = "aiohappyeyeballs", marker = "python_full_version >= '3.14.2'" }, + { name = "aiosignal", marker = "python_full_version >= '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "frozenlist", marker = "python_full_version >= '3.14.2'" }, + { name = "multidict", marker = "python_full_version >= '3.14.2'" }, + { name = "propcache", marker = "python_full_version >= '3.14.2'" }, + { name = "yarl", version = "1.24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } wheels = [ @@ -100,6 +247,95 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, ] +[[package]] +name = "aiohttp-asyncmdnsresolver" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiodns", version = "4.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "zeroconf", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/86/1b94f5669df188f5267e26d48c4cc734b704310e53d695af5f39ee1af67d/aiohttp_asyncmdnsresolver-0.2.0.tar.gz", hash = "sha256:aab2cef7ef8ae45b94abf1fc92aa9f4d70cbef7ac0a3d495a86fb96fce357262", size = 37744, upload-time = "2026-05-20T20:18:27.183Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d5/c08b4e242d50d2aded314a16105ee708946fa3dfd37b837b1ca625d26397/aiohttp_asyncmdnsresolver-0.2.0-py3-none-any.whl", hash = "sha256:1e6dc01719dc563346ae051122fa7dc3a757317e3e6bf4ef8b23cfd034089c22", size = 10806, upload-time = "2026-05-20T20:18:25.58Z" }, +] + +[[package]] +name = "aiohttp-cors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/9e/6cdce7c3f346d8fd487adf68761728ad8cd5fbc296a7b07b92518350d31f/aiohttp-cors-0.7.0.tar.gz", hash = "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d", size = 35966, upload-time = "2018-03-06T15:45:42.936Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/e7/e436a0c0eb5127d8b491a9b83ecd2391c6ff7dcd5548dfaec2080a2340fd/aiohttp_cors-0.7.0-py3-none-any.whl", hash = "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e", size = 27564, upload-time = "2018-03-06T15:45:42.034Z" }, +] + +[[package]] +name = "aiohttp-cors" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/d89e846a5444b3d5eb8985a6ddb0daef3774928e1bfbce8e84ec97b0ffa7/aiohttp_cors-0.8.1.tar.gz", hash = "sha256:ccacf9cb84b64939ea15f859a146af1f662a6b1d68175754a07315e305fb1403", size = 38626, upload-time = "2025-03-31T14:16:20.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/3b/40a68de458904bcc143622015fff2352b6461cd92fd66d3527bf1c6f5716/aiohttp_cors-0.8.1-py3-none-any.whl", hash = "sha256:3180cf304c5c712d626b9162b195b1db7ddf976a2a25172b35bb2448b890a80d", size = 25231, upload-time = "2025-03-31T14:16:18.478Z" }, +] + +[[package]] +name = "aiohttp-fast-zlib" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/e8/225bd4c4db8a435de589e911ab27b2eeed50fa266e452adcf3d07421f81d/aiohttp_fast_zlib-0.1.1.tar.gz", hash = "sha256:085dd826523137487fb41fd262dcc9fdc5f53ea988fa449f9ef703d939f01e9f", size = 8507, upload-time = "2024-06-24T11:54:42.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/ab/33ea8dec4bc3a7371af87f899614bc698cbfde25a4133ab21e064555628d/aiohttp_fast_zlib-0.1.1-py3-none-any.whl", hash = "sha256:b576d03d8b31d3920463d53bdc293b8c631844e2d28fb2ad7cbc559092d4df86", size = 8235, upload-time = "2024-06-24T11:54:40.781Z" }, +] + +[[package]] +name = "aiohttp-fast-zlib" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/a6/982f3a013b42e914a2420631afcaecb729c49525cc6cc58e15d27ee4cb4b/aiohttp_fast_zlib-0.3.0.tar.gz", hash = "sha256:963a09de571b67fa0ef9cb44c5a32ede5cb1a51bc79fc21181b1cddd56b58b28", size = 8770, upload-time = "2025-06-07T12:41:49.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/11/ea9ecbcd6cf68c5de690fd39b66341405ab091aa0c3598277e687aa65901/aiohttp_fast_zlib-0.3.0-py3-none-any.whl", hash = "sha256:d4cb20760a3e1137c93cb42c13871cbc9cd1fdc069352f2712cd650d6c0e537e", size = 8615, upload-time = "2025-06-07T12:41:47.454Z" }, +] + +[[package]] +name = "aiooui" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b7/ad0f86010bbabc4e556e98dd2921a923677188223cc524432695966f14fa/aiooui-0.1.9.tar.gz", hash = "sha256:e8c8bc59ab352419e0747628b4cce7c4e04d492574c1971e223401126389c5d8", size = 369276, upload-time = "2025-01-19T00:12:44.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/fa/b1310457adbea7adb84d2c144159f3b41341c40c80df3c10ce6b266874b3/aiooui-0.1.9-py3-none-any.whl", hash = "sha256:737a5e62d8726540218c2b70e5f966d9912121e4644f3d490daf8f3c18b182e5", size = 367404, upload-time = "2025-01-19T00:12:42.57Z" }, +] + [[package]] name = "aiosignal" version = "1.4.0" @@ -125,6 +361,89 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, ] +[[package]] +name = "aiozoneinfo" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "tzdata", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/05/fe5c1f5f72ca7fbb88b05eb9d47b90bfd898d494a1099e1ec1c3d0e5d44b/aiozoneinfo-0.2.1.tar.gz", hash = "sha256:457e2c665a2c7e093119efb87cc5e0da29e6f59aac504a544bec822c5be1cb6b", size = 8472, upload-time = "2024-06-24T12:30:11.041Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/a1/7f94ff464f01a65d30ebdb00b815b2cf9613b3c4314828d2aad576b0ff21/aiozoneinfo-0.2.1-py3-none-any.whl", hash = "sha256:04579f855f030cd0edb1758659c513142ef1aaf7fcc97b59eb2262ed0c453cce", size = 8011, upload-time = "2024-06-24T12:30:10.017Z" }, +] + +[[package]] +name = "aiozoneinfo" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "tzdata", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/00/e437a179ab78ed24780ded10bbb5d7e10832c07f62eab1d44ee2f335c95c/aiozoneinfo-0.2.3.tar.gz", hash = "sha256:987ce2a7d5141f3f4c2e9d50606310d0bf60d688ad9f087aa7267433ba85fff3", size = 8381, upload-time = "2025-02-04T19:32:06.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/a4/99e13bb4006999de2a4d63cee7497c3eb7f616b0aefc660c4c316179af3a/aiozoneinfo-0.2.3-py3-none-any.whl", hash = "sha256:5423f0354c9eed982e3f1c35edeeef1458d4cc6a10f106616891a089a8455661", size = 8009, upload-time = "2025-02-04T19:32:04.74Z" }, +] + +[[package]] +name = "annotatedyaml" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "propcache", marker = "python_full_version >= '3.14.2'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/4b/973067092ee348e331d125acd60c45245f11663373c219650814b43d0025/annotatedyaml-1.0.2.tar.gz", hash = "sha256:f9a49952994ef1952ca17d27bb6478342eb1189d2c28e4c0ddbbb32065471fb0", size = 15366, upload-time = "2025-10-04T14:36:26.655Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/0f/4482333d679e7174b74655d17b3969ab3754ae4d581752bac1002fe316c0/annotatedyaml-1.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:359a964daf3fccbb4818e6f08478d2e6712a2417a261cbd6472826ce5e8f1503", size = 58944, upload-time = "2025-10-04T14:41:49.516Z" }, + { url = "https://files.pythonhosted.org/packages/c8/88/ab5f9c67dd13b54e0100e8a4cdfd371c45ecfea1ba776a971d7b728087fe/annotatedyaml-1.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6d2dcf741bdedf893d04f958f3f1ad0b5b12b1fe27746f9918a24e2f347eac1", size = 60030, upload-time = "2025-10-04T14:41:50.859Z" }, + { url = "https://files.pythonhosted.org/packages/47/3f/785a22acee2fc16049ac00a9f708f11b1354e40578ae4e5076b989dc5f82/annotatedyaml-1.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:139533a395301f219bfd4ba2265b7a8c55cb4931aac7f730a8ff204a465e76d3", size = 70701, upload-time = "2025-10-04T14:41:52.091Z" }, + { url = "https://files.pythonhosted.org/packages/e8/93/712a6170903b6dd2a30aa59f76e39569f260fde38e9277d3a40ddbdf53f4/annotatedyaml-1.0.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:623f571e0d3819a3cbadce592a2c691274ccd46b09ad770f9271201d7476ea88", size = 65011, upload-time = "2025-10-04T14:41:53.35Z" }, + { url = "https://files.pythonhosted.org/packages/34/a0/5f3e9b72d871d67f89d70166d0e2affdbcf0cf87cd20276c84b6db968a52/annotatedyaml-1.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75927ec682f188efe25309e259c115e3976b702900ce1be93a971b328c87a10a", size = 71370, upload-time = "2025-10-04T14:41:54.589Z" }, + { url = "https://files.pythonhosted.org/packages/a4/32/143d643d9f5d21f1b66666713d24adf68677790fb61700bc727078bdef2c/annotatedyaml-1.0.2-cp313-cp313-manylinux_2_36_x86_64.whl", hash = "sha256:106ac5eaa022df4dfa42e307932aa2a197a19151de3bb41e98840cfc7f1745e1", size = 69434, upload-time = "2025-10-04T14:36:24.962Z" }, + { url = "https://files.pythonhosted.org/packages/83/b0/1ce75b81e42e033914f94159f633b923e57c507690eb0bba966475cab9a1/annotatedyaml-1.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:139d2626fe8faccba9cc79b4d8dca25a4d59e4a274508612842d78945bddeebe", size = 71333, upload-time = "2025-10-04T14:41:55.836Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0b/89451246b115dfc5fcfb3b3ca966f9fcdfc647b10a13eb517fa11f2e3ffd/annotatedyaml-1.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6f2fb86c18064f0dcfb01e3d1096f0575cdff509a24b748c2994f97eb0b70156", size = 65905, upload-time = "2025-10-04T14:41:57.121Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/7008f39f1af0e0b36668cd9affe8a68846797ee1119ec36daac428ade742/annotatedyaml-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:56d7235147b58d155b4ee93f2a92920b4c0be6a6852dc3fd810c67f6e56f8c15", size = 72181, upload-time = "2025-10-04T14:41:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/18/48/abfed2c0d5ff9d09aff2bb85d5035b56066925826972e0f703f59c2c0cb5/annotatedyaml-1.0.2-cp313-cp313-win32.whl", hash = "sha256:e53c74051a82c4cbd68db1371918a6399650f165579a2bf1f7e0a2ed58300564", size = 56097, upload-time = "2025-10-04T14:41:59.016Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7e/24d0e04d148340950aa26e9e2bea1d4047d6bd3588d0db86d2107d7ca2a8/annotatedyaml-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:13ca5d3a325103fd0a0c5a27af05f22118935f3e731e2df26f620ee85b56e85b", size = 60283, upload-time = "2025-10-04T14:41:59.955Z" }, + { url = "https://files.pythonhosted.org/packages/1b/91/0acf5b74926c6964812d9ed752af77531ab4daa06fba1cb668d9006e9e1f/annotatedyaml-1.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c42f385c3f04f425d5948c16afbb94a876da867be276dbf2c2e7436b9a80792d", size = 58962, upload-time = "2025-10-04T14:42:01.183Z" }, + { url = "https://files.pythonhosted.org/packages/71/f6/5dac1ce125984db4cb99d883f234e6a8c0e49358a9136047a490bc2ba51a/annotatedyaml-1.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b5d9d24ba907fd2e905eac69c88e651310c480980a17aa57faf0599ff21f586f", size = 60252, upload-time = "2025-10-04T14:42:02.095Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/81dea3e4272927518abb9c96ab299b8c4346c40267740bfb8d6b0cdb317f/annotatedyaml-1.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2572b7c3c630dae1dd163d6c6ba847493a7f987437941b32d0ad8354615f358a", size = 71219, upload-time = "2025-10-04T14:42:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/42/fb/5aa3d7767cb92e8ba34cba582e5b088f42746e6d075f7d387fcdc4e5dd62/annotatedyaml-1.0.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b28fe13eb0014a0dd06c9a292466feed0cd298ab10525ef9a37089df01c7d333", size = 64459, upload-time = "2025-10-04T14:42:03.967Z" }, + { url = "https://files.pythonhosted.org/packages/a0/eb/b29b84eec6d3a1fc3278ff2959388f347e7853a3f82fc5275c591a523835/annotatedyaml-1.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:987f73a13f121c775bcdb082214c17f114447fee7dad37db2f86b035893ad58d", size = 71175, upload-time = "2025-10-04T14:42:05.22Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7c/4f4bf854f4b62cade7485a9572773d4440ee535e905f166b441b2d3f19a7/annotatedyaml-1.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5cb4ee79c08da2b8f4f24b1775732ca6c497682f3c9b3fd65dee4ea084fc925c", size = 71824, upload-time = "2025-10-04T14:42:06.398Z" }, + { url = "https://files.pythonhosted.org/packages/68/ac/1f903eeccde636723fcf664b372a6ab253b7f13c3de446ff5bce6852d696/annotatedyaml-1.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:da065a8c29556219fce1aa81b406e84f73bc2181067658e57428a8b2e662fc1b", size = 65343, upload-time = "2025-10-04T14:42:07.727Z" }, + { url = "https://files.pythonhosted.org/packages/e0/aa/43e83b50a42ad5c51abf1a335cfc249e182f66542d7c7306ee07397b1956/annotatedyaml-1.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ba9937418c1b189b267540b47fa0dc24c148292739d06a6ca31c2ca8482f16", size = 72328, upload-time = "2025-10-04T14:42:08.656Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6a/f5d9c29633c499973f10330af31a8b135a564a4a2e056c32a6ff2c901559/annotatedyaml-1.0.2-cp314-cp314-win32.whl", hash = "sha256:003e16e91b40176dd8fe77d56c6c936106b408b62953e88ce3506e8ba10bf4e1", size = 57286, upload-time = "2025-10-04T14:42:09.597Z" }, + { url = "https://files.pythonhosted.org/packages/b7/43/d8c7464676094658f1caaee6762536ab43867d7153f7c637207c63fc4c97/annotatedyaml-1.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:17e64a7dde47a678db8aa4e934c3ed8da9a52ab1bc6946d12be86f323e6bd8c7", size = 61363, upload-time = "2025-10-04T14:42:10.968Z" }, + { url = "https://files.pythonhosted.org/packages/c2/5d/7e384f4115a7bc113162f7b6eb5d561031e303f840f304b68e3f1b0541a1/annotatedyaml-1.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8698bbbd1d38f8c9ba95a107d7597f5af3f2ba295d1d14227f85b62377998ffc", size = 104776, upload-time = "2025-10-04T14:42:11.921Z" }, + { url = "https://files.pythonhosted.org/packages/26/7d/77bebdd30118c1e85f11d5a83a3bb5955409bba74d81cfb0f7b551273513/annotatedyaml-1.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cbc661dbc7c5f7ddf69fbf879da6a96745b8cd39ae1338dab3a0aa8eb208367", size = 107716, upload-time = "2025-10-04T14:42:14.151Z" }, + { url = "https://files.pythonhosted.org/packages/dc/19/bfc798abb154e398d5210304ba3beff9ad9c7b6ec4574ffb705493b8e2d5/annotatedyaml-1.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db1c3ca021bbd390354037ede5c255657afb2a7544b7cfa0e091b62b888aa462", size = 130361, upload-time = "2025-10-04T14:42:15.494Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + [[package]] name = "ast-serialize" version = "0.5.0" @@ -165,15 +484,287 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, ] +[[package]] +name = "astral" +version = "2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/c3/76dfe55a68c48a1a6f3d2eeab2793ebffa9db8adfba82774a7e0f5f43980/astral-2.2.tar.gz", hash = "sha256:e41d9967d5c48be421346552f0f4dedad43ff39a83574f5ff2ad32b6627b6fbe", size = 578223, upload-time = "2020-05-20T14:23:17.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/60/7cc241b9c3710ebadddcb323e77dd422c693183aec92449a1cf1fb59e1ba/astral-2.2-py2.py3-none-any.whl", hash = "sha256:b9ef70faf32e81a8ba174d21e8f29dc0b53b409ef035f27e0749ddc13cb5982a", size = 30775, upload-time = "2020-05-20T14:23:14.866Z" }, +] + +[[package]] +name = "async-interrupt" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/5a2d74465037b33ccdaf830e3d9ac008bccdbe4b0657983b90dc89191626/async_interrupt-1.2.0.tar.gz", hash = "sha256:d147559e2478501ad45ea43f52df23b246456715a7cb96e1aebdb4b71aed43d5", size = 8584, upload-time = "2024-08-21T13:23:54.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/f6/5638f86da774d30dae619a8d0d48df24cb17981b43948a8a3ee241b8b695/async_interrupt-1.2.0-py3-none-any.whl", hash = "sha256:a0126e882b9991d1c77839ab53e0e1b9f41f1b3d151a7032243f15011df5e4dc", size = 8898, upload-time = "2024-08-21T13:23:52.816Z" }, +] + +[[package]] +name = "async-interrupt" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/56/79/732a581e3ceb09f938d33ad8ab3419856181d95bb621aa2441a10f281e10/async_interrupt-1.2.2.tar.gz", hash = "sha256:be4331a029b8625777905376a6dc1370984c8c810f30b79703f3ee039d262bf7", size = 8484, upload-time = "2025-02-22T17:15:04.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/77/060b972fa7819fa9eea9a70acf8c7c0c58341a1e300ee5ccb063e757a4a7/async_interrupt-1.2.2-py3-none-any.whl", hash = "sha256:0a8deb884acfb5fe55188a693ae8a4381bbbd2cb6e670dac83869489513eec2c", size = 8907, upload-time = "2025-02-22T17:15:01.971Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "atomicwrites-homeassistant" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/5a/10ff0fd9aa04f78a0b31bb617c8d29796a12bea33f1e48aa54687d635e44/atomicwrites-homeassistant-1.4.1.tar.gz", hash = "sha256:256a672106f16745445228d966240b77b55f46a096d20305901a57aa5d1f4c2f", size = 12223, upload-time = "2022-07-08T20:56:46.35Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/1b/872dd3b11939edb4c0a27d2569a9b7e77d3b88995a45a331f376e13528c0/atomicwrites_homeassistant-1.4.1-py2.py3-none-any.whl", hash = "sha256:01457de800961db7d5b575f3c92e7fb56e435d88512c366afb0873f4f092bb0d", size = 7128, upload-time = "2022-07-08T20:56:44.186Z" }, +] + +[[package]] +name = "attrs" +version = "23.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820, upload-time = "2023-12-31T06:30:32.926Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752, upload-time = "2023-12-31T06:30:30.772Z" }, +] + [[package]] name = "attrs" version = "26.1.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "audioop-lts" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" }, + { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" }, + { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" }, + { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" }, + { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" }, + { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, + { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, + { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, +] + +[[package]] +name = "awesomeversion" +version = "24.6.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/e9/1baaf8619a3d66b467ba105976897e67b36dbad93b619753768357dbd475/awesomeversion-24.6.0.tar.gz", hash = "sha256:aee7ccbaed6f8d84e0f0364080c7734a0166d77ea6ccfcc4900b38917f1efc71", size = 11997, upload-time = "2024-06-24T11:09:27.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/a5/258ffce7048e8be24c6f402bcbf5d1b3933d5d63421d000a55e74248481b/awesomeversion-24.6.0-py3-none-any.whl", hash = "sha256:6768415b8954b379a25cebf21ed4f682cab10aebf3f82a6640aaaa15ec6821f2", size = 14716, upload-time = "2024-06-24T11:09:26.133Z" }, +] + +[[package]] +name = "awesomeversion" +version = "25.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/3a/c97ef69b8209aa9d7209b143345fe49c1e20126f62a775038ab6dcd78fd5/awesomeversion-25.8.0.tar.gz", hash = "sha256:e6cd08c90292a11f30b8de401863dcde7bc66a671d8173f9066ebd15d9310453", size = 70873, upload-time = "2025-08-03T08:54:07.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/b3/c6be343010721bfdd3058b708eb4868fa1a207534a3b6c80de74d35fb568/awesomeversion-25.8.0-py3-none-any.whl", hash = "sha256:1c314683abfcc3e26c62af9e609b585bbcbf2ec19568df2f60ff1034fb1dae28", size = 15919, upload-time = "2025-08-03T08:54:06.265Z" }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, +] + +[[package]] +name = "bcrypt" +version = "4.1.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/e9/0b36987abbcd8c9210c7b86673d88ff0a481b4610630710fb80ba5661356/bcrypt-4.1.3.tar.gz", hash = "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623", size = 26456, upload-time = "2024-05-04T04:12:51.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/4e/e424a74f0749998d8465c162c5cb9d9f210a5b60444f4120eff0af3fa800/bcrypt-4.1.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74", size = 506501, upload-time = "2024-05-04T04:12:07.711Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8d/ad2efe0ec57ed3c25e588c4543d946a1c72f8ee357a121c0e382d8aaa93f/bcrypt-4.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455", size = 284345, upload-time = "2024-05-04T04:12:09.243Z" }, + { url = "https://files.pythonhosted.org/packages/2f/f6/9c0a6de7ef78d573e10d0b7de3ef82454e2e6eb6fada453ea6c2b8fb3f0a/bcrypt-4.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a", size = 283395, upload-time = "2024-05-04T04:12:11.116Z" }, + { url = "https://files.pythonhosted.org/packages/63/56/45312e49c195cd30e1bf4b7f0e039e8b3c46802cd55485947ddcb96caa27/bcrypt-4.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05", size = 284794, upload-time = "2024-05-04T04:12:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/4c/6a/ce950d4350c734bc5d9b7196a58fedbdc94f564c00b495a1222984431e03/bcrypt-4.1.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3", size = 283689, upload-time = "2024-05-04T04:12:14.289Z" }, + { url = "https://files.pythonhosted.org/packages/af/a1/36aa84027ef45558b30a485bc5b0606d5e7357b27b10cc49dce3944f4d1d/bcrypt-4.1.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15", size = 318065, upload-time = "2024-05-04T04:12:15.641Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e8/183ead5dd8124e463d0946dfaf86c658225adde036aede8384d21d1794d0/bcrypt-4.1.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d", size = 315556, upload-time = "2024-05-04T04:12:17.921Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5e/edcb4ec57b056ca9d5f9fde31fcda10cc635def48867edff5cc09a348a4f/bcrypt-4.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286", size = 324438, upload-time = "2024-05-04T04:12:19.823Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5d/121130cc85009070fe4e4f5937b213a00db143147bc6c8677b3fd03deec8/bcrypt-4.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64", size = 335368, upload-time = "2024-05-04T04:12:21.055Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ac/bcb7d3ac8a1107b103f4a95c5be088b984d8045d4150294459a657870bd9/bcrypt-4.1.3-cp37-abi3-win32.whl", hash = "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf", size = 167120, upload-time = "2024-05-04T04:12:23.021Z" }, + { url = "https://files.pythonhosted.org/packages/69/57/3856b1728018f5ce85bb678a76e939cb154a2e1f9c5aa69b83ec5652b111/bcrypt-4.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978", size = 158059, upload-time = "2024-05-04T04:12:25.256Z" }, + { url = "https://files.pythonhosted.org/packages/a8/eb/fbea8d2b370a4cc7f5f0aff9f492177a5813e130edeab9dd388ddd3ef1dc/bcrypt-4.1.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c", size = 506522, upload-time = "2024-05-04T04:12:27.206Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9a/4aa31d1de9369737cfa734a60c3d125ecbd1b3ae2c6499986d0ac160ea8b/bcrypt-4.1.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a", size = 284401, upload-time = "2024-05-04T04:12:29.098Z" }, + { url = "https://files.pythonhosted.org/packages/12/d4/13b86b1bb2969a804c2347d0ad72fc3d3d9f5cf0d876c84451c6480e19bc/bcrypt-4.1.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84", size = 283414, upload-time = "2024-05-04T04:12:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/29/3c/6e478265f68eff764571676c0773086d15378fdf5347ddf53e5025c8b956/bcrypt-4.1.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08", size = 284951, upload-time = "2024-05-04T04:12:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/97/00/21e34b365b895e6faf9cc5d4e7b97dd419e08f8a7df119792ec206b4a3fa/bcrypt-4.1.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611", size = 283703, upload-time = "2024-05-04T04:12:34.794Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c9/069b0c3683ce969b328b7b3e3218f9d5981d0629f6091b3b1dfa72928f75/bcrypt-4.1.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6", size = 317876, upload-time = "2024-05-04T04:12:36.108Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fd/0d2d7cc6fc816010f6c6273b778e2f147e2eca1144975b6b71e344b26ca0/bcrypt-4.1.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834", size = 315555, upload-time = "2024-05-04T04:12:37.536Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/283450ee672719e216a5e1b0e80cb0c8f225bc0814cbb893155ee4fdbb9e/bcrypt-4.1.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73", size = 324408, upload-time = "2024-05-04T04:12:38.882Z" }, + { url = "https://files.pythonhosted.org/packages/9c/64/a016d23b6f513282d8b7f9dd91342929a2e970b2e2c2576d9b76f8f2ee5a/bcrypt-4.1.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d", size = 335334, upload-time = "2024-05-04T04:12:40.442Z" }, + { url = "https://files.pythonhosted.org/packages/75/35/036d69e46c60394f2ffb474c9a4b3783e84395bf4ad55176771f603069ca/bcrypt-4.1.3-cp39-abi3-win32.whl", hash = "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2", size = 167071, upload-time = "2024-05-04T04:12:42.256Z" }, + { url = "https://files.pythonhosted.org/packages/b1/46/fada28872f3f3e121868f4cd2d61dcdc7085a07821debf1320cafeabc0db/bcrypt-4.1.3-cp39-abi3-win_amd64.whl", hash = "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991", size = 158124, upload-time = "2024-05-04T04:12:44.085Z" }, +] + +[[package]] +name = "bcrypt" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, + { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, + { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, + { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, + { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, + { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, + { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, + { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, + { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, + { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, + { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, + { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, + { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, + { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, + { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, + { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, + { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, + { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, + { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, + { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, + { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, + { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, + { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, + { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, + { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, + { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, + { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, + { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, +] + [[package]] name = "bellows" version = "0.49.2" @@ -189,6 +780,195 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/20/a6b70ac1a9cb3f0d0de4d77cca2131b0882c7cd55d7fc5202189a7b9b137/bellows-0.49.2-py3-none-any.whl", hash = "sha256:bd24178de09a3f0cc32bb7b028e7da02eeb2c2b358414ff75d8484021f2576db", size = 156513, upload-time = "2026-05-27T02:17:31.269Z" }, ] +[[package]] +name = "bleak" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dbus-fast", marker = "sys_platform == 'linux'" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corebluetooth", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-libdispatch", marker = "sys_platform == 'darwin'" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth-advertisement", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth-genericattributeprofile", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-enumeration", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-radios", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-foundation", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-foundation-collections", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-storage-streams", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/df/05a3f80ca8e3f7f5b0dba68a9e618147c909ccdba1468f07487dc8d72a9d/bleak-3.0.2.tar.gz", hash = "sha256:c2229cb8238d5876b4bd05c74bf7a1aea1f88da39d2e51ac9dfd5cc319d5265f", size = 125293, upload-time = "2026-05-02T23:01:04.066Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/54/05aceb9cd80073805b3ed8522e3196e8cb22f70e741873fa51406c31f4e7/bleak-3.0.2-py3-none-any.whl", hash = "sha256:39092feb9e83f1df5ad2f88e837723c7211c982ce9e9cda6235104bc2ebe0d0d", size = 146490, upload-time = "2026-05-02T23:01:02.592Z" }, +] + +[[package]] +name = "bleak-retry-connector" +version = "4.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bleak" }, + { name = "bluetooth-adapters", marker = "sys_platform == 'linux'" }, + { name = "dbus-fast", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/fc/5de052d5995bfea6f5237b4e848e3fd2978777e5371d92dbce725d21181a/bleak_retry_connector-4.6.1.tar.gz", hash = "sha256:ac2d19362f96757708dff2b0fedfefd5a8d8efb724027a777e54cc8ac2fc5a3d", size = 18827, upload-time = "2026-05-21T21:28:38.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/7c/4490be0547296648e4dfb77c0a1949c3cdc8963c9b621aecc168f1d4cb76/bleak_retry_connector-4.6.1-py3-none-any.whl", hash = "sha256:018ba421babe05785e5a6497c73f3894772fad0f7fa5b054d48beb3d180ce0c4", size = 18759, upload-time = "2026-05-21T21:28:36.032Z" }, +] + +[[package]] +name = "bluetooth-adapters" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiooui" }, + { name = "bleak" }, + { name = "dbus-fast", marker = "sys_platform == 'linux'" }, + { name = "uart-devices" }, + { name = "usb-devices" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/e0/6f7d10ffd8e5b62fc0998c285991925aaa9b727521bede0e29a0b0084a94/bluetooth_adapters-2.4.0.tar.gz", hash = "sha256:e8a61935a867deb8af981e986bdf504c4398895ff254eac626790184a4fdd1e3", size = 17501, upload-time = "2026-05-26T14:28:58.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/ba/f11151a72f782bf24908c2b57163a2ff854024594c4ff3d04f1c47f40ed0/bluetooth_adapters-2.4.0-py3-none-any.whl", hash = "sha256:f2841c49e8750658ecb8c1d46d2c05a9de4740362eb83e3e7e501d3092c0377e", size = 20604, upload-time = "2026-05-26T14:28:57.107Z" }, +] + +[[package]] +name = "bluetooth-auto-recovery" +version = "1.6.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bluetooth-adapters" }, + { name = "btsocket" }, + { name = "pyric" }, + { name = "usb-devices" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/4e/14070e4e938069ef96fd1bfb949ea257ce7d807920f43b945bebde46c458/bluetooth_auto_recovery-1.6.4.tar.gz", hash = "sha256:c69a9f3b5e00239ab005d808aa5e7afa3c36a82f86e9531b6c7682bde1bc3ecc", size = 14827, upload-time = "2026-05-25T02:37:03.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/89/0dc9270c3c8640ea74fb35eef20e00c5fbdf929b4aeb6797cfa3e2f29750/bluetooth_auto_recovery-1.6.4-py3-none-any.whl", hash = "sha256:39485c41e17a2d4887c1fbf04b4e2fd37f0f3c7898db388753e026ca3addf055", size = 13471, upload-time = "2026-05-25T02:37:01.788Z" }, +] + +[[package]] +name = "bluetooth-data-tools" +version = "1.28.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/90/46dfa84798ca4e5c2f66d9a756bb207ed21d89a32b8ef8d3ea89e079455f/bluetooth_data_tools-1.28.4.tar.gz", hash = "sha256:0617a879c30e0410c3506e263ee9e9bd51b06d64db13b4ad0bfd765f794b756f", size = 16488, upload-time = "2025-10-28T15:23:05.289Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/ed/9bb3a560ff073b5ea6f1fe9ef66d4af0fb8071cd3f569ba58769217114bb/bluetooth_data_tools-1.28.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e3184f43c52ed1e39f9ad412c586c84b4e0841f052608e6ed7ef81daf656fb64", size = 385017, upload-time = "2025-10-28T15:35:58.534Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/8237718607f1daeec1f4aefb3dfdd7b7b56bc1422fdc4e5f9ef991e3a9b2/bluetooth_data_tools-1.28.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1c5e524df9afae40142c3a3dcf128983df99e73158a2bc98f1709024ff185a22", size = 386609, upload-time = "2025-10-28T15:35:59.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/8e/e6136f790c68261610160c0b8dfadd874d38bff8e3da0feb4bf1428b89fd/bluetooth_data_tools-1.28.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bf7eb8b41995466af3401db3387726afda42487b291b94ab90e7d26aadb72ac", size = 414363, upload-time = "2025-10-28T15:36:00.975Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/af729a472e5e9274480b34c8218fda915b3d7add9247d766dee79143eebf/bluetooth_data_tools-1.28.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b3a1e9838f147d6e80b5d9cf7e33c9e736f1f1bda9db00b4ea5ed45fd57d2e8", size = 132276, upload-time = "2025-10-28T15:36:02.622Z" }, + { url = "https://files.pythonhosted.org/packages/70/eb/111c66cc73fd4ec29071641ba6f8b68db033f3d6b9611aa332565c0e3286/bluetooth_data_tools-1.28.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8688f54fd344f17f0c04bca6c2b4351c9fcb211d16becada60f5656305c04238", size = 414494, upload-time = "2025-10-28T15:36:03.709Z" }, + { url = "https://files.pythonhosted.org/packages/dc/72/0cb024304121380d374c62cf119647b77c88be3bea435291a71d98e956d4/bluetooth_data_tools-1.28.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f4233a9d8983ede1d4b319783266b5ae89dbd0f8ac48dcc9b0c2a1d6a60a0ca", size = 414311, upload-time = "2025-10-28T15:36:04.921Z" }, + { url = "https://files.pythonhosted.org/packages/53/00/e11af70293608c36507f14bf893f20b072d753ab1c929dc8103209cf9555/bluetooth_data_tools-1.28.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:df2948eae3bd32242322d7f1a7f0d74e2d2f79e5e3254c7e06ec2ddbcacabe7b", size = 135039, upload-time = "2025-10-28T15:36:06.483Z" }, + { url = "https://files.pythonhosted.org/packages/a4/24/47344c86c8abef13a7b39240c3ee5789e0c5fde16ea65c97f3a9a669c121/bluetooth_data_tools-1.28.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e98f7bcd491711f5be161a0400721c9ecb782308f0eeb030f3bd450450f53d0", size = 416584, upload-time = "2025-10-28T15:36:07.925Z" }, + { url = "https://files.pythonhosted.org/packages/b8/26/45358ddee23e4eed23c3e39959cb070351bcdce6698818334e029af7ab8f/bluetooth_data_tools-1.28.4-cp312-cp312-win32.whl", hash = "sha256:324fc45aad6e9a3115a5612959460eb82156579f5925009cb482427a0931207b", size = 287103, upload-time = "2025-10-28T15:36:09.101Z" }, + { url = "https://files.pythonhosted.org/packages/9e/bc/171fabeff862a94f9926eebd5f9722cdfcd2b7134e2162c471d115cd31cf/bluetooth_data_tools-1.28.4-cp312-cp312-win_amd64.whl", hash = "sha256:c9192bcca07a926599a8221e6354a3ef628a8ecb3d904e437ea216a3aafcbcc7", size = 287106, upload-time = "2025-10-28T15:36:10.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/cd/fa868c3bed326976813c04bd89e833cb0032a6a18ffc03f843947caa29d3/bluetooth_data_tools-1.28.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ade5a22f394cee6b428474f5c23f8ce086ebc618b30fa478fc53703b5dc1bf09", size = 383602, upload-time = "2025-10-28T15:36:11.963Z" }, + { url = "https://files.pythonhosted.org/packages/d3/37/ef120dcce334ba8e3d97c06c9d46ab1db3b7474fad1fb867097b7c0a9355/bluetooth_data_tools-1.28.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ea8569f42699e94e18a1be32e45c737f2795c7509f09fa27dd5d342a7855473c", size = 385073, upload-time = "2025-10-28T15:36:13.522Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/1ce2f4d2ce6a04a6a1be490cd2b975777fb76f5230818cefe24b7ed7ba9d/bluetooth_data_tools-1.28.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dfaecb4269bc4830a7bd6f823e8a0a4c368d9135ee6805e6db5eecf1211a2e4", size = 412028, upload-time = "2025-10-28T15:36:14.709Z" }, + { url = "https://files.pythonhosted.org/packages/d6/aa/f525cc4d4da3555f820a6ce79a3877424ba73f69f4d44a4389b19f7aaf15/bluetooth_data_tools-1.28.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ff3d43804f3510bd11a267268c567b7fe5653b10243be48527ac01d8e15b3faa", size = 130572, upload-time = "2025-10-28T15:36:16.184Z" }, + { url = "https://files.pythonhosted.org/packages/6d/01/2c4b89de730e71c94f3552948aa8adf0a0b5a4dc21e642805bc8e014f41d/bluetooth_data_tools-1.28.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e99be62bdcd2b94778eb230c6d73f4da4ad1493ccc33c09efc8432c5a242c071", size = 412805, upload-time = "2025-10-28T15:36:17.69Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/6ca04f0225b51ef27e79f369e9b9fff4bf104025a4e51d6fb2d943c38645/bluetooth_data_tools-1.28.4-cp313-cp313-manylinux_2_36_x86_64.whl", hash = "sha256:f85fbc0c540c3e64b5fc925f6b60d8c96d521548c7bfa3b1e8998ea4e5a59054", size = 140133, upload-time = "2025-10-28T15:23:03.49Z" }, + { url = "https://files.pythonhosted.org/packages/c8/3e/675f9037c7b23df43229d95e8627fe10759f8c3c4a1ef6919b8d1683d4df/bluetooth_data_tools-1.28.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c9dd29f39bddbcfa1dcfca13dcfd2a1111d5a0fbba708a8c98feb98bca10b7a", size = 411910, upload-time = "2025-10-28T15:36:19.207Z" }, + { url = "https://files.pythonhosted.org/packages/a1/12/4f2086f879c0595e065e62dd1bfbe8d371336308654e466ca10b6cf61d86/bluetooth_data_tools-1.28.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8bbcd287a1d5b249639fc1ba99c7ab8f0d7257d43104cf349fab2c747b84b3cd", size = 132814, upload-time = "2025-10-28T15:36:20.789Z" }, + { url = "https://files.pythonhosted.org/packages/e2/72/56a3b3a15cd6c601c3c22cf8c58db788ea59e669db1af123a4113983302b/bluetooth_data_tools-1.28.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7decde5838ccccf71ec626c3f0421a6265054cb1e5ced121bb6448434a0bb72f", size = 414926, upload-time = "2025-10-28T15:36:21.897Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7f/280e0fb57c8569ee04057231e17bc5b47fb037470d62af72b9e7d908fdd2/bluetooth_data_tools-1.28.4-cp313-cp313-win32.whl", hash = "sha256:7d4d65ee4cb3c0616d411f2352b9da8c97f789a42fe9c14a68b6d4b458d62d9a", size = 287105, upload-time = "2025-10-28T15:36:23.089Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/bcccc7fcaabd74a717663dbe4f4909c4f37edef6c95bfcde7b2548b04ec1/bluetooth_data_tools-1.28.4-cp313-cp313-win_amd64.whl", hash = "sha256:5f3bb83e8755d0ce2e3d62e70a35b73c569ddc63d7200658740e311042c60777", size = 287107, upload-time = "2025-10-28T15:36:24.252Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/03ba322c36376532f3133c7d56bc80dd2859df9c78aa52de19ed7627b9fb/bluetooth_data_tools-1.28.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:81c6c2b7c844d30a0fd1527e38e47cdb0f350c0297fb11516bfa255b37241fbf", size = 383677, upload-time = "2025-10-28T15:36:33.905Z" }, + { url = "https://files.pythonhosted.org/packages/a4/2e/74e7b4857ba10a524cd00177fbd78764c50810fb523020b7d5cbf0fdbac8/bluetooth_data_tools-1.28.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:99896987f48d762694cdea7a8a7091031cdf40dc65e8e934a7422746264865ba", size = 385890, upload-time = "2025-10-28T15:36:35.196Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8d/35bc257ed1935e55ac7bfb56172a290f094f8b982f65f68aadb0f03ceab5/bluetooth_data_tools-1.28.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebac9d60786bd7c403f472fcda871cb74d0aef0d4e713715af2e5e095d15a625", size = 412966, upload-time = "2025-10-28T15:36:36.398Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2c/2ed3dff30e85029e631a211d93e11aab7dc4a899d9c96a15eca18541e66e/bluetooth_data_tools-1.28.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:06a2750e49fed2310ddd7b51388b891cbd4457ee7392f3a17c387591cbb74ace", size = 129887, upload-time = "2025-10-28T15:36:38.429Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ff/824f3b34b0fab4e57efd457ea8b9bdf41d279a44eb19cfde5ede159d90b3/bluetooth_data_tools-1.28.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5dccfe237237463c3d74fa425aaf8a9d78b26a5177e6777b10039699313a335", size = 412909, upload-time = "2025-10-28T15:36:39.552Z" }, + { url = "https://files.pythonhosted.org/packages/eb/88/f2217b88c32b470e5f9dc9fbce38f24b9548c0776be7c5e0db1249c42ae9/bluetooth_data_tools-1.28.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4a071d7af2614af9a00f65063adaacda94f4357cc2dfedda7057c005f437dacd", size = 413005, upload-time = "2025-10-28T15:36:41.572Z" }, + { url = "https://files.pythonhosted.org/packages/6d/da/cde7557972e50cbb8a92291cc34e5de07f0e2bbc28a388151e738e9efe84/bluetooth_data_tools-1.28.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:bd84c4f2d24103ff43044ccd3cf8c0e05ee285bd6f9eddc9772b2069cfb6c271", size = 131426, upload-time = "2025-10-28T15:36:42.645Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7f/925fd28e2695ba810b1f7f02f2d5ab8635a11d6e415ac4039446145f9e48/bluetooth_data_tools-1.28.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e3895dbbdad2a39de5a7b36a4ddb5e2f8ad38029628e3eddfde31a5c56d81b5", size = 414955, upload-time = "2025-10-28T15:36:43.775Z" }, + { url = "https://files.pythonhosted.org/packages/03/b1/cbf3a2c8404862605e487200d45aefb130c0c0ce3df219230155eeb95199/bluetooth_data_tools-1.28.4-cp314-cp314-win32.whl", hash = "sha256:1d9b22827144329e3ca1348b8473fe6b48127707a81539848232847c4cb08e1d", size = 286157, upload-time = "2025-10-28T15:36:45.171Z" }, + { url = "https://files.pythonhosted.org/packages/c7/68/eb168b986eebc0c98fb0a6a521719a33d218bafc46c48c5279322d15e9b2/bluetooth_data_tools-1.28.4-cp314-cp314-win_amd64.whl", hash = "sha256:04c91b6f2dfaa419652356488fa50dfb0f54cb20b1f90f9e5e1d6911430d9688", size = 286151, upload-time = "2025-10-28T15:36:46.414Z" }, + { url = "https://files.pythonhosted.org/packages/57/37/f2ce46cf82b32d6a62171753a2d6550d633af5b27f0ad2c2ff5fef1980a4/bluetooth_data_tools-1.28.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a44c48bf163606a2915d12ffb3ac1b022548e566c062907f98266e8a19c6173c", size = 488264, upload-time = "2025-10-28T15:36:47.582Z" }, + { url = "https://files.pythonhosted.org/packages/ba/32/c3bbee5b7c66190f0729e71fefe44adb49e7bb94407b110d972d817561a2/bluetooth_data_tools-1.28.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b76a6c8c6d610844c8712cecf207c16373cad3361fb29e6dbcdcb12f2700bcb9", size = 492846, upload-time = "2025-10-28T15:36:48.846Z" }, + { url = "https://files.pythonhosted.org/packages/71/5c/751028e7fab907c0c2fc7749f088d19bf2b938e5cdd7d0e68ddbcacb7b79/bluetooth_data_tools-1.28.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61b827616075ecee12c374b04b14d81575403849435bf915c9a3812138f046b7", size = 548041, upload-time = "2025-10-28T15:36:50.066Z" }, + { url = "https://files.pythonhosted.org/packages/77/02/4d8f4a9cb2a2beaaedda71fb3017f6bb5eb3de08656adfb9a8a773ec7912/bluetooth_data_tools-1.28.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:525646baaf5f741ea071aa4babd8313e4e9bae75b46757c4b0f6aeadfa71b52a", size = 517778, upload-time = "2025-10-28T15:36:51.628Z" }, + { url = "https://files.pythonhosted.org/packages/89/9b/90d65fed47b531b0f0f4c8be012d35c97950c97fb7b74501bfe938c7f7ca/bluetooth_data_tools-1.28.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c06b66ef406c68a95052a87640fa34d402d31120a8b0b62f99080169621697a", size = 546643, upload-time = "2025-10-28T15:36:52.971Z" }, + { url = "https://files.pythonhosted.org/packages/d3/6b/c15363ccfc208a34cd6d627610350c72633e2a6764d37d04a1340fb13844/bluetooth_data_tools-1.28.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:152232c157f2f6d8265c0141e56423bbedd9e84044fb815e69d786a73fb195c7", size = 548872, upload-time = "2025-10-28T15:36:54.332Z" }, + { url = "https://files.pythonhosted.org/packages/85/2a/b649eeea14e6330da34f42dc1407424cd929af3ae1298b5651459d0c4bb8/bluetooth_data_tools-1.28.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:243163028565955e73f19c0c462b619fd0f56e31875c30f5f3af2a48b43adb67", size = 524783, upload-time = "2025-10-28T15:36:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/0e/6e/96c762f8a49f65348748d72c515c5a79c9179c685d3e02694c380bdafa72/bluetooth_data_tools-1.28.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0a1608bca00e24b6ca3b98ed7d797a03988a44285d74286e045446c8161a62ea", size = 551318, upload-time = "2025-10-28T15:36:57.062Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7d/796cbb679d19425ff381ebbe7a5238217b3f3e5c65b9a46e7be57ba105fc/bluetooth_data_tools-1.28.4-cp314-cp314t-win32.whl", hash = "sha256:25918d7ece36f29ebde21aaf70f3c1e1c63501206dd1c7713bbd8911d43d0dce", size = 286158, upload-time = "2025-10-28T15:36:58.717Z" }, + { url = "https://files.pythonhosted.org/packages/c3/74/639329ba05947018ba928162042dfb162a31b85757e27591bb6aa96c1f42/bluetooth_data_tools-1.28.4-cp314-cp314t-win_amd64.whl", hash = "sha256:276528d7ea2419ccab14ddf044ee7f65a5b6bc35c49264625560ad0c184dc67a", size = 286163, upload-time = "2025-10-28T15:36:59.861Z" }, +] + +[[package]] +name = "bluetooth-data-tools" +version = "1.29.18" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/a1/5bf655e600d7d482107a26bb5862d5d33ad93407d476d35f8817b7a0a83d/bluetooth_data_tools-1.29.18.tar.gz", hash = "sha256:87f678cc7b4963cb3ba73064dd72155f915bec4b21f22acd997848ddc0b1c67b", size = 20277, upload-time = "2026-05-22T16:37:46.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/6a/6ed739bb678dc06d339f66588ee9102fc586f63fa5841215fa882eb3b360/bluetooth_data_tools-1.29.18-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:fd5408d54eac9f5ecf34193a40e1badb905ae8b3a1801b92252e365064888c11", size = 151002, upload-time = "2026-05-22T16:37:44.419Z" }, +] + +[[package]] +name = "boto3" +version = "1.43.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/d0/9154ca8bd6051a55102a5c8bb077b15959681a5f4dcf614a423afaefdf83/boto3-1.43.25.tar.gz", hash = "sha256:91f3deb0dfc32403a187f57c19a78dc8e38d25ee2967a511fa8905acf00cc074", size = 113169, upload-time = "2026-06-08T19:49:27.877Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/89/d27be8bb3dc72a6d1d1d8f9e039e89ae86827c05c47385d78a1d964571f4/boto3-1.43.25-py3-none-any.whl", hash = "sha256:e4e2515533a1593eb2302e9eda5baf4e1510abb219b41dd0db4696cc21bc5039", size = 140538, upload-time = "2026-06-08T19:49:25.427Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/03/9dc102506c3ebc3758a9e8602e7dafb78789993bcac5daa82398b56ae884/botocore-1.43.25.tar.gz", hash = "sha256:faab543ca6ae6f8fdc5f6318240bebfb8c05cd25823715fe02aad7edf0c4b383", size = 15478403, upload-time = "2026-06-08T19:49:13.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/a5/6ceef332b18c348be9ec26aeff0da5b3f7ea046bf3fc654feecf6974c4d9/botocore-1.43.25-py3-none-any.whl", hash = "sha256:ef1d210ac9085ea0e5fc6ad2e63f0c7e97103c74f52156bf944289aaf6278d1b", size = 15161721, upload-time = "2026-06-08T19:49:08.751Z" }, +] + +[[package]] +name = "btsocket" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/b1/0ae262ecf936f5d2472ff7387087ca674e3b88d8c76b3e0e55fbc0c6e956/btsocket-0.3.0.tar.gz", hash = "sha256:7ea495de0ff883f0d9f8eea59c72ca7fed492994df668fe476b84d814a147a0d", size = 19563, upload-time = "2024-06-10T07:05:27.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/2b/9bf3481131a24cb29350d69469448349362f6102bed9ae4a0a5bb228d731/btsocket-0.3.0-py2.py3-none-any.whl", hash = "sha256:949821c1b580a88e73804ad610f5173d6ae258e7b4e389da4f94d614344f1a9c", size = 14807, upload-time = "2024-06-10T07:05:26.381Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + [[package]] name = "cffi" version = "2.0.0" @@ -255,6 +1035,135 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "ciso8601" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/bc/cf42c1b0042f91c90a6b00244f63b6fb137af15e43e29f07bb72cf955be8/ciso8601-2.3.1.tar.gz", hash = "sha256:3212c7ffe5d8080270548b5f2692ffd2039683b6628a8d2ad456122cc5793c4c", size = 31225, upload-time = "2023-10-30T19:54:34.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/3d/f6496a260ba6e58135fb3dd1108799f6dd9cadf634372e020bfbf0d27fea/ciso8601-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f39bb5936debf21c52e5d52b89f26857c303da80c43a72883946096a6ef5e561", size = 24211, upload-time = "2023-10-30T19:53:42.011Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a9/24ffa848a5878a50009d6177826c36b60e2e8807e4d54ee94817e790897b/ciso8601-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:21cf83ca945bb26ecd95364ae2c9ed0276378e5fe35ce1b64d4c6d5b33038ea3", size = 15591, upload-time = "2023-10-30T19:53:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/ea/74/77fd6e67a2a3489a1ac449570142e2f4137289be25027e235d4688470d56/ciso8601-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:013410263cba46748d2de29e9894341ae41223356cde7970478c32bd0984d10c", size = 15643, upload-time = "2023-10-30T19:53:44.806Z" }, + { url = "https://files.pythonhosted.org/packages/5b/bd/fef5524974e3ba376e16df35e4197152edffdf7ac9d5d99bc173a9fcf256/ciso8601-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b26935687ef1837b56997d8c61f1d789e698be58b261410e629eda9c89812141", size = 39805, upload-time = "2023-10-30T19:53:46.036Z" }, + { url = "https://files.pythonhosted.org/packages/50/1c/d1cb5b2d2173abfc4d5a068981acddff763cce318e896ec87c140412c72d/ciso8601-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d980a2a88030d4d8b2434623c250866a75b4979d289eba69bec445c51ace99f", size = 48335, upload-time = "2023-10-30T19:53:47.275Z" }, + { url = "https://files.pythonhosted.org/packages/76/ef/5ef06ccb6f6c023573634119f93df237687e3f4263e2f307b51a7208d103/ciso8601-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87721de54e008fb1c4c3978553b05a9c417aa25b76ddf5702d6f7e8d9b109288", size = 49449, upload-time = "2023-10-30T19:53:48.646Z" }, + { url = "https://files.pythonhosted.org/packages/e6/45/f981bbd51f1c9fa3d730c3fbcb74b1f8928463e6ea8283257f6b164847e6/ciso8601-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f107a4c051e7c0416824279264d94f4ed3da0fbd82bd96ec3c3293426826de4", size = 17084, upload-time = "2023-10-30T19:53:49.745Z" }, +] + +[[package]] +name = "ciso8601" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/8a/075724aea06c98626109bfd670c27c248c87b9ba33e637f069bf46e8c4c3/ciso8601-2.3.3.tar.gz", hash = "sha256:db5d78d9fb0de8686fbad1c1c2d168ed52efb6e8bf8774ae26226e5034a46dae", size = 31909, upload-time = "2025-08-20T16:31:33.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/aa/b723a6981cfc42bbe992da23179f5dd1556e9054067985108ec6cbe34dd3/ciso8601-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e7ef14610446211c4102bf6c67f32619ab341e56db15bad6884385b43c12b064", size = 16111, upload-time = "2025-08-20T16:30:36.781Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e9/e547ec4dd75f28d8d217488130fa07767bc42fd643d61a18870487133c0e/ciso8601-2.3.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:523901aec6b0ccdf255c863ef161f476197f177c5cd33f2fbb35955c5f97fdb4", size = 24193, upload-time = "2025-08-20T16:30:38.067Z" }, + { url = "https://files.pythonhosted.org/packages/14/c8/801b78e30667cb31b4524e9dc26cbc2c03c012f9aa3f5ae21676461dc622/ciso8601-2.3.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:45f8254d1fb0a41e20f98e93075db7b56504adddf65e4c8b397671feba4861ca", size = 15917, upload-time = "2025-08-20T16:30:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/44/6b/dfc56a2a4e572a2a3f8c88a66dea6a9186a8e10da7c36cc84abc31bf795c/ciso8601-2.3.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:202ca99077577683e6a84d394ff2677ec19d9f406fbf35734f68be85d2bcd3f1", size = 41324, upload-time = "2025-08-20T16:30:40.321Z" }, + { url = "https://files.pythonhosted.org/packages/7c/57/cf66171cb5807fe345b03ce9e32fd91b3a8b6e5bd95710618a9a1b0f3fab/ciso8601-2.3.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7cec4e31c363e87221f2561e7083ce055a82de041e822e7c3775f8ce6250a7e", size = 41804, upload-time = "2025-08-20T16:30:41.204Z" }, + { url = "https://files.pythonhosted.org/packages/75/91/15e8871d7ae2ff0f756128e246348bdede58c08edba13cd886450ceeb304/ciso8601-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:389fef3ccc3065fa21cb6ef7d03aee63ab980591b5d87b9f0bbe349f52b16bdc", size = 41209, upload-time = "2025-08-20T16:30:42.46Z" }, + { url = "https://files.pythonhosted.org/packages/30/54/7563e20a158a4bdf3e8d13c63e02b71f9b73c662edc83cb4d5ab67171a7d/ciso8601-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4499cfbe4da092dea95ab81aefc78b98e2d7464518e6e80107cf2b9b1f65fa2", size = 41368, upload-time = "2025-08-20T16:30:43.397Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d5/6182006dd86365bb21d1f658f70c41e266ce0f97eaf353f9d7069c51851f/ciso8601-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:1df1ca3791c6f2d543f091d88e728a60a31681ff900d9eb02f1403cf31e9c177", size = 17566, upload-time = "2025-08-20T16:30:44.706Z" }, + { url = "https://files.pythonhosted.org/packages/01/16/88154fe8247e4dcfdbaed8c6b8ccf32b1dd4389c6c95b1986bf31649eb00/ciso8601-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8afa073802c926c3244e1e5fcc5818afd3acb90fb7826a90f91ddbda0636ea70", size = 16109, upload-time = "2025-08-20T16:30:45.655Z" }, + { url = "https://files.pythonhosted.org/packages/be/46/8d46372b3802c7201c20c8b316569f27253aaafba0cdd2cd033985e8b77e/ciso8601-2.3.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:8a04e518b4adf8e35e030feaecdb4a835d39b9bb44d207e926aea8ce3447ad7c", size = 24189, upload-time = "2025-08-20T16:30:46.958Z" }, + { url = "https://files.pythonhosted.org/packages/13/80/1890e097cb76e41995de82f29c0289ca590d7135e0be3707e5b78f54350d/ciso8601-2.3.3-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:f79ad8372463ba4265981016d1648bc05f4922bc8044c4243fcbaef7a12ee9f7", size = 15925, upload-time = "2025-08-20T16:30:48.082Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e9/690a2a6beefd9d982c20adde3f09ff54a23291a699b0df7cf0c59027d9cf/ciso8601-2.3.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d5894a33f119b5ac1082df187dc58c74fe13c9c092e19ba36495c2b7cee3540b", size = 41352, upload-time = "2025-08-20T16:30:49.294Z" }, + { url = "https://files.pythonhosted.org/packages/2f/34/9a498ceb0ebd23f538e6685721c9fc4666701372c651874ed22ec46b1423/ciso8601-2.3.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09deebf3e326ec59d80019b4ad35175c90b99cde789c644b1496811fe3340587", size = 41866, upload-time = "2025-08-20T16:30:50.262Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0a/ee0981502aa1c9f28f7e89cf6cee08bdff2c6ed9d4289b00cceb8a1c500e/ciso8601-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3aa43ed59b2117baccc5bb760e5e53dad77cacba671d757c1e82e0a367b1f42a", size = 41271, upload-time = "2025-08-20T16:30:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/fb/65/24a888240324188d8350bc24fb58a6d759c0ca43adfa77210f3d60370b56/ciso8601-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:289515aa3a3b86a9c3450bf482f634138b98788332d136751507bfdfe46e6031", size = 41411, upload-time = "2025-08-20T16:30:52.439Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1f/febc9de191acb461e02e616e5366bc2b7757277a11b4bf215d4fb79516a8/ciso8601-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:e7288068a5bffbcc50cbe9cdaf3971f541fcd209c194fa6a59ad06066a3dcff0", size = 17573, upload-time = "2025-08-20T16:30:53.759Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3a/54ad0ae2257870076b4990545a8f16221470fecea0aa7a4e1f39506db8c5/ciso8601-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82db4047d74d8b1d129e7a8da578518729912c3bd19cb71541b147e41f426381", size = 16115, upload-time = "2025-08-20T16:30:54.971Z" }, + { url = "https://files.pythonhosted.org/packages/23/fb/9fe767d44520691e2b706769466852fbdeb44a82dc294c2766bce1049d22/ciso8601-2.3.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a553f3fc03a2ed5ca6f5716de0b314fa166461df01b45d8b36043ccac3a5e79f", size = 24214, upload-time = "2025-08-20T16:30:56.359Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ac/984fd3948f372c46c436a2b48da43f4fb7bc6f156a6f4bc858adaab79d42/ciso8601-2.3.3-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:ff59c26083b7bef6df4f0d96e4b649b484806d3d7bcc2de14ad43147c3aafb04", size = 15929, upload-time = "2025-08-20T16:30:58.352Z" }, + { url = "https://files.pythonhosted.org/packages/de/3a/5572917d4e0bec2c1ef0eda8652f9dc8d1850d29d3eef9e5e82ffe5d6791/ciso8601-2.3.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99a1fa5a730790431d0bfcd1f3a6387f60cddc6853d8dcc5c2e140cd4d67a928", size = 41578, upload-time = "2025-08-20T16:30:59.351Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cf/07321ce5cf099b98de0c02cd4bab4818610da69743003e94c8fb6e8a59cb/ciso8601-2.3.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c35265c1b0bd2ac30ed29b49818dd38b0d1dfda43086af605d8b91722727dec0", size = 42085, upload-time = "2025-08-20T16:31:00.338Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c7/3c521d6779ee433d9596eb3fcded79549bbe371843f25e62006c04f74dc9/ciso8601-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aa9df2f84ab25454f14df92b2dd4f9aae03dbfa581565a716b3e89b8e2110c03", size = 41313, upload-time = "2025-08-20T16:31:01.313Z" }, + { url = "https://files.pythonhosted.org/packages/f9/93/efd40db0d6b512be1cbe4e7e750882c2e88f580e17f35b3e9cc9c23004b5/ciso8601-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32e06a35eb251cfc4bbe01a858c598da0a160e4ad7f42ff52477157ceaf48061", size = 41443, upload-time = "2025-08-20T16:31:02.357Z" }, + { url = "https://files.pythonhosted.org/packages/21/8e/515f9404faa39af8df5e2b899cafbca5dbe7cd2ffe5cc124ef393ffdaf1c/ciso8601-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:7657ba9730dc1340d73b9e61eca14f341c41dd308128c808b8b084d2b85bc03e", size = 17977, upload-time = "2025-08-20T16:31:03.429Z" }, +] + [[package]] name = "click" version = "8.4.0" @@ -426,12 +1335,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/b5/68a9054be852e61f31de1be4e8d95802646b93344ce17c2380a1748706fa/crccheck-1.3.1-py3-none-any.whl", hash = "sha256:1680c9a7bb1ca4bec45fa19b8ca64319f10d2ce4eb8b0d25d51cb99a20ca0108", size = 24608, upload-time = "2025-07-10T07:01:07.25Z" }, ] +[[package]] +name = "cronsim" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/1a/02f105147f7f2e06ed4f734ff5a6439590bb275a53dd91fc73df6312298a/cronsim-2.7-py3-none-any.whl", hash = "sha256:1e1431fa08c51dc7f72e67e571c7c7a09af26420169b607badd4ca9677ffad1e", size = 14213, upload-time = "2025-10-21T16:38:20.431Z" }, +] + +[[package]] +name = "cryptography" +version = "43.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "cffi", marker = "python_full_version < '3.14.2' and platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/ec/9fb9dcf4f91f0e5e76de597256c43eedefd8423aa59be95c70c4c3db426a/cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e", size = 686873, upload-time = "2024-07-20T16:39:26.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/46/dcd2eb6840b9452e7fbc52720f3dc54a85eb41e68414733379e8f98e3275/cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74", size = 6239718, upload-time = "2024-07-20T16:38:48.159Z" }, + { url = "https://files.pythonhosted.org/packages/e8/23/b0713319edff1d8633775b354f8b34a476e4dd5f4cd4b91e488baec3361a/cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895", size = 3808466, upload-time = "2024-07-20T16:39:03.912Z" }, + { url = "https://files.pythonhosted.org/packages/77/9d/0b98c73cebfd41e4fb0439fe9ce08022e8d059f51caa7afc8934fc1edcd9/cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22", size = 3998060, upload-time = "2024-07-20T16:39:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/ae/71/e073795d0d1624847f323481f7d84855f699172a632aa37646464b0e1712/cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47", size = 3792596, upload-time = "2024-07-20T16:38:16.338Z" }, + { url = "https://files.pythonhosted.org/packages/83/25/439a8ddd8058e7f898b7d27c36f94b66c8c8a2d60e1855d725845f4be0bc/cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf", size = 4008355, upload-time = "2024-07-20T16:38:18.882Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a2/1607f1295eb2c30fcf2c07d7fd0c3772d21dcdb827de2b2730b02df0af51/cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55", size = 3899133, upload-time = "2024-07-20T16:38:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/5e/64/f41f42ddc9c583737c9df0093affb92c61de7d5b0d299bf644524afe31c1/cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431", size = 4096946, upload-time = "2024-07-20T16:38:51.136Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cd/d165adcf3e707d6a049d44ade6ca89973549bed0ab3686fa49efdeefea53/cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc", size = 2616826, upload-time = "2024-07-20T16:38:59.195Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b7/38924229e84c41b0e88d7a5eed8a29d05a44364f85fbb9ddb3984b746fd2/cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778", size = 3078700, upload-time = "2024-07-20T16:38:11.026Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/397515233e6a861f921bd0365b162b38e0cc513fcf4f1bdd9cc7bc5a3384/cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66", size = 6242814, upload-time = "2024-07-20T16:38:43.504Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/99b2c00a4f54c60d210d6d1759c720ecf28305aa32d6fb1bb1853f415be6/cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5", size = 3809467, upload-time = "2024-07-20T16:38:45.781Z" }, + { url = "https://files.pythonhosted.org/packages/76/eb/ab783b47b3b9b55371b4361c7ec695144bde1a3343ff2b7a8c1d8fe617bb/cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e", size = 3998617, upload-time = "2024-07-20T16:39:06.257Z" }, + { url = "https://files.pythonhosted.org/packages/a3/62/62770f34290ebb1b6542bd3f13b3b102875b90aed4804e296f8d2a5ac6d7/cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5", size = 3794003, upload-time = "2024-07-20T16:38:34.888Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6c/b42660b3075ff543065b2c1c5a3d9bedaadcff8ebce2ee981be2babc2934/cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f", size = 4008774, upload-time = "2024-07-20T16:38:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/f7/74/028cea86db9315ba3f991e307adabf9f0aa15067011137c38b2fb2aa16eb/cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0", size = 3900098, upload-time = "2024-07-20T16:38:40.67Z" }, + { url = "https://files.pythonhosted.org/packages/bd/f6/e4387edb55563e2546028ba4c634522fe727693d3cdd9ec0ecacedc75411/cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b", size = 4096867, upload-time = "2024-07-20T16:38:13.527Z" }, + { url = "https://files.pythonhosted.org/packages/ce/61/55560405e75432bdd9f6cf72fa516cab623b83a3f6d230791bc8fc4afeee/cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf", size = 2616481, upload-time = "2024-07-20T16:39:09.014Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3d/696e7a0f04555c58a2813d47aaa78cb5ba863c1f453c74a4f45ae772b054/cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709", size = 3081462, upload-time = "2024-07-20T16:38:32.225Z" }, +] + [[package]] name = "cryptography" version = "48.0.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "cffi", marker = "python_full_version >= '3.14.2' and platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } wheels = [ @@ -480,30 +1435,174 @@ wheels = [ ] [[package]] -name = "distlib" -version = "0.4.0" +name = "dbus-fast" +version = "5.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/db/b621610e50b1bc46ff63534d75239553c1bf33256de6096b58214fd9808a/dbus_fast-5.0.22.tar.gz", hash = "sha256:34dc67d7d21a12399828dd13e63b352750580beea54ea7c729e708f2d2905fef", size = 83224, upload-time = "2026-06-05T18:47:59.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/06/233b0bc13919474f70320bf389cb81ce02811956d6cf86c45e84679b63c3/dbus_fast-5.0.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f0bcad7f71d2304a68a5b0bc0d24c3fcc14710a2ffcf5f2a27521e3aece71ca", size = 799464, upload-time = "2026-06-05T18:56:02.617Z" }, + { url = "https://files.pythonhosted.org/packages/68/e9/77bc23a6f5aebfb8f2c34489795e8517aed7eca31738438e1a4c4a4891d3/dbus_fast-5.0.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ffcf16034f71a801bd2108aeffb6337d104c9459e8b1a218d16a917c8a2d2e9", size = 852687, upload-time = "2026-06-05T18:56:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/56769d0936d1273d1801ef574ec426ccb3f61f4b0a7a0eeb9eb2b8ccafa5/dbus_fast-5.0.22-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98de6d2c200d8182e1fd0bdde3206fa556b8fa14ebb752a044cd8daa87b4658c", size = 833814, upload-time = "2026-06-05T18:56:05.87Z" }, + { url = "https://files.pythonhosted.org/packages/06/ca/964f0d39a3be03b12a98f39519d34ad95b74360c7e3adf4cd4907dc25fd6/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b013437b66dc22b8d9aca5e0b0d46bf1980208a143409469fe482d9684a2a717", size = 806891, upload-time = "2026-06-05T18:56:07.623Z" }, + { url = "https://files.pythonhosted.org/packages/f4/25/57fe6ab509ad9da2e190498fa9c37f868e38ac521d940a9edb9ba6b6c657/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:855f15b7f7805171da2b82de1c317d01cfbb9fb8ac61fcc1e8dec54d8c69fab7", size = 830867, upload-time = "2026-06-05T18:56:09.473Z" }, + { url = "https://files.pythonhosted.org/packages/be/2b/da036e9f4aeb776833139575fe0774544aa6cd13ba997eea7fbc4ab99852/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7d1c42963235cfc015a2d2b8c5fe42b65387493b4ad4ce0ec122601c805e6742", size = 860651, upload-time = "2026-06-05T18:56:10.952Z" }, + { url = "https://files.pythonhosted.org/packages/76/5a/fa81a6685c763ea488ad93228cb6e036adc9af6a560f4c31643691f4cfd8/dbus_fast-5.0.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de10ff3b3cb2acb1c09fe17158a470519000d37bb5ee5fd69c4075e81ce8dcf5", size = 798472, upload-time = "2026-06-05T18:56:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/6a/34/6b272e6df60be1aa4d575aa30220175a52c002a649c951d9950bfa3a72d6/dbus_fast-5.0.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:979761985fe343c701f2b7575285d6e370123f7231d4656209ef7824bb686bbb", size = 850312, upload-time = "2026-06-05T18:56:16.36Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5c/9045c3595ddcd4069e6b5d051df06bf11a2b022592f721c9acea1e0e4d22/dbus_fast-5.0.22-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fb73f1d8374253b7c17d69e902cf2ded1bfb089cb6ae67c10b4e0bdfe1b8fe08", size = 828366, upload-time = "2026-06-05T18:56:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/a7/78/ca6881442b8fa29edbe6d99bec4b535b0b2e2f423075d015ff5b719c4e2c/dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b67a02037eb58bcf9e445df60ea0d9d7346fd334abde3aa62e03c75823b53979", size = 806036, upload-time = "2026-06-05T18:56:19.501Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c6/458728eb1caa26171e6a8ae1d0d99bd29aaeac67ad7824bbd95d7f854a41/dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:83940ea00d7ee2f0c5bcb5d19d7d05e7949e52d467616a0b735d72e7285402ec", size = 828353, upload-time = "2026-06-05T18:56:21.346Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1d/830b1569264780210d44898e5b0d95cffe2830b952c2ee21ea481274cd81/dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:279d212e9fb262d595af2e4b5b9e951bc00c73a5c8eeb50f158caa13705b9c84", size = 857743, upload-time = "2026-06-05T18:56:22.9Z" }, + { url = "https://files.pythonhosted.org/packages/b3/8c/4eefaabdf538882528164060ae83d9a34f1172b019c32c3254436834e9b1/dbus_fast-5.0.22-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:703e0f8f9af52e8e053394ee2b578042be0c3d8ea2b1488f9db8cb14393cc13f", size = 810835, upload-time = "2026-06-05T18:56:26.356Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cf/fd327dbb40ee67a9331fb587bf78aff2ab1500b35979978a5cacb10d7f8c/dbus_fast-5.0.22-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb1d7e8e65561d0fd438004fd9e0f981c8a862912fed58dd4e29db1936c39d73", size = 855498, upload-time = "2026-06-05T18:56:28.009Z" }, + { url = "https://files.pythonhosted.org/packages/56/33/1709ebc16a4d353ddc4fcd29252e2b9d93bded6422a45fd6df170e0911c1/dbus_fast-5.0.22-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:959fab6420897ab99410e67d6f9f9a7f6f4cedb6014700768f5e2d71dbff5dc6", size = 833510, upload-time = "2026-06-05T18:56:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fd/89d7c34152900d986b9c78e39cc62aa73eefc22b57b3a8c946d945a85540/dbus_fast-5.0.22-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:eb31c5ff339a7071b914617a69d5b7c6ba7d411da4b01a5f9b5b2fe51e9d1301", size = 853669, upload-time = "2026-06-05T18:47:56.747Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a1/031cc4a89d947f1fe110f663f93dcce9230213b7accaf719790d813def04/dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:856f0543c593f3480e93e67bcd1aa4ddc1d94a6076cfd3ad4e0f5e2b01b33dc3", size = 818486, upload-time = "2026-06-05T18:56:31.72Z" }, + { url = "https://files.pythonhosted.org/packages/36/e2/de8b764fdb947314fb8c2e079b556510194fd100983776845e234a107cc9/dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:96d231d128c1f46f263790335897195dde9dac2f38571782db8ae1d8647bd548", size = 833582, upload-time = "2026-06-05T18:56:33.325Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1f/e5f0dd28d07c4b3f7bafd3357bfa424c8dace355a3dad921fec05db4634b/dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:595bd3ccfd8318cbafff79f33a15709fee3728724fd61d5fa220080d73b574cb", size = 862291, upload-time = "2026-06-05T18:56:35.102Z" }, + { url = "https://files.pythonhosted.org/packages/26/69/5b54654f598ef98e8f94fd5a40929668b1f8fcd76e7fb50de0db73d329da/dbus_fast-5.0.22-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04bac97d0cb754a4d13037d0132517f1df28192d6e0568a0bf6df06623062285", size = 1534804, upload-time = "2026-06-05T18:56:38.804Z" }, + { url = "https://files.pythonhosted.org/packages/24/b7/c00d01699dc87ffc35f143226d3b296372840e2e2bc15101d35df7c74949/dbus_fast-5.0.22-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3eb57d592d84b0bb90e0c077db7ecb61562f49cc9b86a3ef08cbe17243e9cc4f", size = 1613316, upload-time = "2026-06-05T18:56:40.461Z" }, + { url = "https://files.pythonhosted.org/packages/f3/94/ea0db4c1aa6409cb16551b50aa8573e72f64407ca5281b042919ef81ca1c/dbus_fast-5.0.22-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:de4d235d1282ebb3ab65b6cddab84e914c045d92ceb381ddcbdbaf66bf1fb132", size = 822053, upload-time = "2026-06-05T18:56:42.519Z" }, + { url = "https://files.pythonhosted.org/packages/40/e4/a3bb52185b8a8c76bd8aaba3ff4fa8395eea19fbc142122b43dc377b275c/dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:048f34299fbe82d7b87c56f47e8bd83f62339a4517685abc6671d603a55d2c89", size = 1549996, upload-time = "2026-06-05T18:56:44.307Z" }, + { url = "https://files.pythonhosted.org/packages/37/2b/6e405ba92e87d78a689a387809d975f97f8c8748b98efccfacd2b4e1d9f5/dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:92df9fb6d8adeb17b534621c2ee730295bbe1d0c2584d5c82b1db478e3f04e8f", size = 823004, upload-time = "2026-06-05T18:56:46.023Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8f/77135ab8d690030cdb0ebeca879640b5945c4cbf5344ecbc507b4628da24/dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7be4271e38251f1ad726962dec60da887c8ed352d157352e4fc27f56aece5c5d", size = 1629160, upload-time = "2026-06-05T18:56:47.688Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "envs" +version = "1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/7f/2098df91ff1499860935b4276ea0c27d3234170b03f803a8b9c97e42f0e9/envs-1.4.tar.gz", hash = "sha256:9d8435c6985d1cdd68299e04c58e2bdb8ae6cf66b2596a8079e6f9a93f2a0398", size = 9230, upload-time = "2021-12-09T22:16:52.616Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/bc/f8c625a084b6074c2295f7eab967f868d424bb8ca30c7a656024b26fe04e/envs-1.4-py3-none-any.whl", hash = "sha256:4a1fcf85e4d4443e77c348ff7cdd3bfc4c0178b181d447057de342e4172e5ed1", size = 10988, upload-time = "2021-12-09T22:16:51.127Z" }, +] + +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "fnv-hash-fast" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "fnvhash", version = "0.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/35/a0f2baec714caa5e865dc284382bac7f959c643a03d7f2c2f8c38573b2b2/fnv_hash_fast-1.0.2.tar.gz", hash = "sha256:d4c528bfb0daa751afb17419a244b913b094b9f0634f9bd19aeffcdc60192589", size = 5808, upload-time = "2024-08-23T13:00:08.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/76/08c20543421f368f5d0215381b745268f5ba51916a286896fed62ed583e2/fnv_hash_fast-1.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d306b606c1686f7902f2da3193535e3523934ddf10cc540427d5a1d96a9818c4", size = 65081, upload-time = "2024-08-23T13:05:34.981Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ba/4cadf8a40268bed1c777f9d8e6cfe18a38056d46c2cc844ce34560bb9796/fnv_hash_fast-1.0.2-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:d1dfd66728c70b6b3184729a8e2b98cf8d3548b65bc09ab49fff156d86095e62", size = 149973, upload-time = "2024-08-23T13:05:35.891Z" }, + { url = "https://files.pythonhosted.org/packages/40/9b/0b27c3116dcbde287961c413d9cddc9bfaa62783f5c43cf6a8a6743bd1da/fnv_hash_fast-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdeaed747d4af60c0ae4cd336ee349db0bba2e1bd46d7d94c8c6a1a7cf3ecbf4", size = 154213, upload-time = "2024-08-23T13:05:37.305Z" }, + { url = "https://files.pythonhosted.org/packages/14/ee/6b6079fd618a4ae9e1762ff525d204d7689aa8e7e378d24ca5df7e02a4c9/fnv_hash_fast-1.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e9f303ce7c394119cb205fe54124f956b3feefd388700f2268b209d78fa9a88c", size = 1237178, upload-time = "2024-08-23T13:05:39.099Z" }, + { url = "https://files.pythonhosted.org/packages/78/36/60431380699e98371a985bdbfc181a4adb32317d5ada1c5f10c84087afaa/fnv_hash_fast-1.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55b9ccbfb87aafc76ef133c70e76a5061a48432f6ba846263ef122a774bce09c", size = 1147938, upload-time = "2024-08-23T13:05:40.686Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d5/d2ef0509776ba09378f90ea87657b8c33112c2c24429e8596439472eb6ea/fnv_hash_fast-1.0.2-cp312-cp312-win32.whl", hash = "sha256:57507e52829dd463f2f755ca22fc9dc4a8d9a9c5d8cf1b0d5ec4eeddf90c9c48", size = 64267, upload-time = "2024-08-23T13:05:42.121Z" }, + { url = "https://files.pythonhosted.org/packages/1f/46/eb073c248b42b8137d47dff385b30c0ebfb3542536fc4581a944372a0c82/fnv_hash_fast-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ef4118d57d27a13271feb47b0ffef95a5122aaa2c4e15b4979cc8bf1bc81c14b", size = 66293, upload-time = "2024-08-23T13:05:43.392Z" }, + { url = "https://files.pythonhosted.org/packages/54/af/9bb74b23610d3f10b64ac41c6ac9a59402619ef13a4d880975d1dc07dfd3/fnv_hash_fast-1.0.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ea6d4fb666684d7e15f2eb1aa92235b25286ea3081cdfb469ffcc7ee34c74b67", size = 64342, upload-time = "2024-08-23T13:05:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/a6/47/327e75a72d6f0de00491b8867953878a4c72c058e0f9ee37c0c64111a297/fnv_hash_fast-1.0.2-cp313-cp313-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:f91ec27fbe3fc43440a250d3b8dac3f0ebd8cea91ffa432bea40ef611b089eeb", size = 145037, upload-time = "2024-08-23T13:05:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5f/90f485875dee1a7fd2252aacef6c10a7dcf991fcfee44f4e65ba13ab477e/fnv_hash_fast-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fb40cc6685a81595434c6cf1ef79d92d4d899e8bc823d9ad6a30287d612de0d", size = 149134, upload-time = "2024-08-23T13:05:46.624Z" }, + { url = "https://files.pythonhosted.org/packages/05/d3/79bc1b00223aada2bf906c99bb619d730bdbff293433f08ef3ba153ef198/fnv_hash_fast-1.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7b7f491a7c3cd679bda8fcd693234812a488bcb3dae0952ba312366b6f69796d", size = 1232050, upload-time = "2024-08-23T13:05:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0d/b523ee18b8dea412f0b1d711917b030a32edc1e183b36d7419d83030b7f5/fnv_hash_fast-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a318cb86ea4a91c95eb42bd49e9144fbdc83e0bb91a1e6f672f197f15a450d01", size = 1143237, upload-time = "2024-08-23T13:05:49.209Z" }, + { url = "https://files.pythonhosted.org/packages/1a/aa/6cc3d9da7ca14341be9a9effe32b2bf105f6d08b3971b007c51f18b22d9a/fnv_hash_fast-1.0.2-cp313-cp313-win32.whl", hash = "sha256:0ac9b5da8fbb9f670a7ce877dfa9bccc942f6499e25801d63427e0f55e1aa902", size = 63690, upload-time = "2024-08-23T13:05:50.556Z" }, + { url = "https://files.pythonhosted.org/packages/87/f6/1d7f2fd7c2163e661774c07f8cceb1db434f2c5440ab644379d6de76753b/fnv_hash_fast-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5935a91ae5cc9edd2bd7a9028b0e5b1e371e5a383034357541b559a2e235e57", size = 65514, upload-time = "2024-08-23T13:05:51.41Z" }, +] + +[[package]] +name = "fnv-hash-fast" +version = "2.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "fnvhash", version = "0.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/ef/7d94271729042f4905800f9034adb149f2a716af26f2cfe9c46343637322/fnv_hash_fast-2.0.3.tar.gz", hash = "sha256:57b499a80ea8f7daf901aff047377264ef21577b40575183807dba37bcc00d6f", size = 5765, upload-time = "2026-05-16T23:52:11.417Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://files.pythonhosted.org/packages/85/6b/ac753d14ef3852da3de7fdddd4058467d57bff637396ab2d5cf17f08e68b/fnv_hash_fast-2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:574d2b1d5b00ec026eb25f2cfe3f7150813e6f12938ae6f14e8e5305e464ac94", size = 7461, upload-time = "2026-05-16T23:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/6c/27/ee32a7c8ed191defa9b74e4db339d2027600a340f5dce04d7d884af2caef/fnv_hash_fast-2.0.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3a6ba0e54d761edc19785829ce82b3947f90c17d6b9a910b0aecd980ece16a2", size = 8368, upload-time = "2026-05-16T23:58:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/f5/28/e2f7fa8f776aaef75a6bfc1db0e58a4b2eca1ac28add2b6272d4056d8d74/fnv_hash_fast-2.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d57c1da0aeb33260a8e2de00d1e0f6f2a2657a547ead57998f1620bca4f42dc8", size = 9207, upload-time = "2026-05-16T23:58:23.433Z" }, + { url = "https://files.pythonhosted.org/packages/5d/59/6c8b94d0fde410ff9ae0f88b189922e79e55792ffcd71d66867a2b66f3e6/fnv_hash_fast-2.0.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66ce1a96bc1fa42efb1eb16ba5a9a29e36c0f1ba1fc4cbcb959c49b58bf4b2f4", size = 8253, upload-time = "2026-05-16T23:58:24.495Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/3c97d2358351c9d41f4c6ee2ebe76fe899c0364b54757af172b265b4258c/fnv_hash_fast-2.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd2d3eb2e276c650ad6a075016f79d54dde02e902130e7b05914de3b9f241dfe", size = 9113, upload-time = "2026-05-16T23:58:25.536Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e6/da93460fe3092843f7273b8b3957fc219945ce43260785bfab744235102c/fnv_hash_fast-2.0.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f0e2516fdf3160e206c150827e27984c06eec78903afa633120fa8930e72f4ed", size = 8706, upload-time = "2026-05-16T23:58:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b6/5567282409210195146abd788f1249ac516d0e07618208bf48d1ca500bd6/fnv_hash_fast-2.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e5c27949b2bea394d205bd0907b0ecc7abb3e1a380adbd77109b5d3b719f538", size = 8608, upload-time = "2026-05-16T23:58:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/2e/65/1b3a28371ea40426f1c200893206640c7cb550e18dc4e2ddd7f12e9ad2e5/fnv_hash_fast-2.0.3-cp312-cp312-win32.whl", hash = "sha256:092d393c72639010d2b47337c0b563e3b6553bdf7e18191e00e7448077b8ee93", size = 9699, upload-time = "2026-05-16T23:58:28.606Z" }, + { url = "https://files.pythonhosted.org/packages/5a/13/573d413f4579c6fef70d2a8f581f9fc48e9e31c89cb0f2a24ea691880838/fnv_hash_fast-2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:7fbe6b919ecadbb149f7db91b36f6d6c87e44101372d607a5cd93c671dd4adb8", size = 9907, upload-time = "2026-05-16T23:58:30.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/65/e20276bf80cfdd974e40908a2c531edcee059a89f104b22e801145b5f88e/fnv_hash_fast-2.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b5c085ce57003b356eb1b4ea8cd9479a8c2451682f2fccb2ea63b872534cb7b9", size = 7456, upload-time = "2026-05-16T23:58:31.484Z" }, + { url = "https://files.pythonhosted.org/packages/28/67/28968a253f69d3ebfb33545193949614fc8f69e8c812354bc4759718e3a7/fnv_hash_fast-2.0.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:02de40e6547205ff29bed36ccff36488e9a5404fcb8cd6fdcaf982ec09b49de1", size = 8372, upload-time = "2026-05-16T23:58:32.501Z" }, + { url = "https://files.pythonhosted.org/packages/74/1e/fd5370641259ad142f6e1d0340be2485dc465fc605f9ca9d7474635ed146/fnv_hash_fast-2.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dec92c363e6a969dd4e3b921c0ae18533a679c0d9275c361e9da06f46720590f", size = 9208, upload-time = "2026-05-16T23:58:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a6/8509512e51f63e9195aab85949e9e167309aaa7121fedd7dcfbab661767d/fnv_hash_fast-2.0.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4e1dcd0ffd6f56272c6a74600f649a8edf52a7a3158f84bd3df70373535921d5", size = 8257, upload-time = "2026-05-16T23:58:35.239Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ae/a655b308a4b80e79762798b47818bedc440d3a946ac283b8b7f4f29cc744/fnv_hash_fast-2.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:95915eb03222ff482765d66bf47eb17457e864b05136da6be429099a0b7c0927", size = 9117, upload-time = "2026-05-16T23:58:36.552Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9e/59408db77b2e6436628fb03e25f1d9c25d68bfc4535851ec8b1e43090db8/fnv_hash_fast-2.0.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c38164d10e99bf5a0c0758662fd8c0849c6efb981af43590b40b1f129c94b90c", size = 8711, upload-time = "2026-05-16T23:58:37.571Z" }, + { url = "https://files.pythonhosted.org/packages/da/54/b55d9392740d2fc40e68000ccc9c2709cec4f69167bb6765522b443bdd33/fnv_hash_fast-2.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26375d14b1523879447ffd7b5a5a6096b94bf226307c1dcdad373f8e96ff1fc1", size = 8608, upload-time = "2026-05-16T23:58:38.868Z" }, + { url = "https://files.pythonhosted.org/packages/14/ab/7b69aea439cca59fb7aa5441a97d1d1bb29662c7ea576f112bd5137f0680/fnv_hash_fast-2.0.3-cp313-cp313-win32.whl", hash = "sha256:0c0ecf9be946f2df6a8d2f6f4fd5aea2590f86929176fd913c3c9fdb641a5901", size = 9699, upload-time = "2026-05-16T23:58:39.901Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cf/97323eed94aaadc0c69b0f28dbb513bca9dc62543454f3150c7cd96dbdc7/fnv_hash_fast-2.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea2bdc562870637842dce2a7048a21db3d6809ab4a8fde1cdfd5dd4f8a628eb1", size = 9913, upload-time = "2026-05-16T23:58:40.933Z" }, + { url = "https://files.pythonhosted.org/packages/03/35/7c68c9f8d51e31200a1371b396b772da4d32f269e02cdff7cada22594656/fnv_hash_fast-2.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:381df4fd4891fb20483673d7ff1973395a5a81c4fa736a685ad05e74653c4dbf", size = 7457, upload-time = "2026-05-16T23:58:42.243Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/5b5cea95c30a51bb76b84661be9de3cc7625fb0edc3252b7509864d36f4d/fnv_hash_fast-2.0.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2f50c18835bdc7c92a62801778dd5fdc6593c603b4cbadd7adae9d5f17305a52", size = 8374, upload-time = "2026-05-16T23:58:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/00/c9/1d0a2299ddcbcc9168c3bdc99dcae08de1dae276705baddab323b912468a/fnv_hash_fast-2.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c45838d0383438696d815b6de92c841215d4f9f39b8dbaf58071a1af84ea35fc", size = 9207, upload-time = "2026-05-16T23:58:44.324Z" }, + { url = "https://files.pythonhosted.org/packages/18/8b/356df1dc62dd05cfdc3fec24e8e66101be1544f03a388a8043f7eb6d9577/fnv_hash_fast-2.0.3-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:cabf3cc36b0f5eb26eb19716842edf709b032a54e1d18a72981c21458c52c632", size = 7745, upload-time = "2026-05-16T23:52:09.972Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3c/c740792c230608204a4dac1ba2aaaec1615115778ba33836143a937c7996/fnv_hash_fast-2.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe4da9446b554365a24d026c0f08b6ee96e30f31b8db7eb683ea167c923755e6", size = 9117, upload-time = "2026-05-16T23:58:45.351Z" }, + { url = "https://files.pythonhosted.org/packages/f0/09/f859b56caab1e3b564d9ec05771f380f7965e39a1b8c2e3fdaa341655a26/fnv_hash_fast-2.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c89aa84df2bfe2cc280694a8cf9fca3955640ce5f5c6f30f6b1bb020b30f2175", size = 8609, upload-time = "2026-05-16T23:58:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/1e/fa/3f7aeae26885762d7ad20c77a643c8671f7ae63069dc70cddc77483eb5ee/fnv_hash_fast-2.0.3-cp314-cp314-win32.whl", hash = "sha256:f25e37526b2898677f0b6a99a441bf0a86eab9e354d5d9222c4960f49e3795d7", size = 9813, upload-time = "2026-05-16T23:58:47.436Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/c9ed5f7e54889103de1754f53d8d294510d7e7f04c1075ec5dcaeee1562a/fnv_hash_fast-2.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:7a45f7050c4e99e2f04122508ec4efb568c93fcbece8482371a177ac89e6db8c", size = 10052, upload-time = "2026-05-16T23:58:48.47Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a3/a54bdc9a843e3d2988c8c35224ae51cdcffd8e55d9031039ccbf51b8e2f6/fnv_hash_fast-2.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:642bc26dbc906f86ee7a888cb66c149455c1a2a841a7ea72f19ffb969d0f0b14", size = 9085, upload-time = "2026-05-16T23:58:49.54Z" }, + { url = "https://files.pythonhosted.org/packages/75/45/bbb6e0741d945569ae13e91d774b613e06fb184841e25bfc3f188b98888f/fnv_hash_fast-2.0.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d63dcae2fd15c307f8a333f77aa6a5146538e9e9ef606b1883c8b5f06d5f86e8", size = 10910, upload-time = "2026-05-16T23:58:50.572Z" }, + { url = "https://files.pythonhosted.org/packages/37/84/e95767cbba632270b6d7f14108c800bd9883b3596b939c52c457b671b779/fnv_hash_fast-2.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a852d5c3278153378c04dcf5e45514477beeccd4268ceb775a3cf553dc5102b0", size = 12580, upload-time = "2026-05-16T23:58:51.691Z" }, + { url = "https://files.pythonhosted.org/packages/ef/37/51d20e8444eb87a6396762d0bc160c6698963753e3cff6f765f76a58dbec/fnv_hash_fast-2.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a805cc43c45e3452ff4a4b76e77fb40b452c795bf04d0f6f76c06fb7b02f755", size = 12409, upload-time = "2026-05-16T23:58:52.827Z" }, + { url = "https://files.pythonhosted.org/packages/28/37/0d6a033e957cc2976cc453db5271976107168ddf0a49361f4b4187a1fc47/fnv_hash_fast-2.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd413f1b171188cbf8ea6bb10f1e8c3aed3a88d239a682870e3727021b246700", size = 11380, upload-time = "2026-05-16T23:58:53.86Z" }, + { url = "https://files.pythonhosted.org/packages/93/fa/962407e8d475f8b502c5df7f8f9bcc4d73386628b76cf836f6a739d9982a/fnv_hash_fast-2.0.3-cp314-cp314t-win32.whl", hash = "sha256:13f4284c5521df53b2fc54e2f595e8534fba03656ef3d92f099503980c09b15b", size = 14180, upload-time = "2026-05-16T23:58:54.894Z" }, + { url = "https://files.pythonhosted.org/packages/84/19/e0b9e51d9f69efb6a6d155c2a5aa489f4366e34211ca2e843954c9053b44/fnv_hash_fast-2.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b4e44c9764bf0fa8387251bb806538976b0c641b89bfb6de3c8f242eb6bffdda", size = 14645, upload-time = "2026-05-16T23:58:56.033Z" }, ] [[package]] -name = "execnet" -version = "2.1.2" +name = "fnvhash" +version = "0.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", ] +sdist = { url = "https://files.pythonhosted.org/packages/2f/01/14ef74ea03ac12e8a80d43bbad5356ae809b125cd2072766e459bcc7d388/fnvhash-0.1.0.tar.gz", hash = "sha256:3e82d505054f9f3987b2b5b649f7e7b6f48349f6af8a1b8e4d66779699c85a8e", size = 1902, upload-time = "2015-11-28T12:21:00.722Z" } [[package]] -name = "filelock" -version = "3.29.0" +name = "fnvhash" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/43/30d2dd2b14621b2004f658ba5335e5a6f5a9c1338ed37678d7fd247b7a9c/fnvhash-0.2.1.tar.gz", hash = "sha256:0c7e885f44c8f06de07f442befebc590ee9ca0cc88846681f608496284ce9cd5", size = 19057, upload-time = "2025-05-05T16:59:10.819Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, + { url = "https://files.pythonhosted.org/packages/b9/92/7c8abc21a1de7159013c0b0bd2ecf06530959bb14fd5c3bf0045e788c6d9/fnvhash-0.2.1-py3-none-any.whl", hash = "sha256:00fab14bec841e4cb29b4fd2ed9358f8bf9f4600d9d8149cde27a191193a33e8", size = 18115, upload-time = "2025-05-05T16:59:09.269Z" }, ] [[package]] @@ -628,6 +1727,493 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/eb/6518a1b00488d48995034226846653c382d676cf5f04be62b3c3fae2c6a1/gpiozero-2.0.1-py3-none-any.whl", hash = "sha256:8f621de357171d574c0b7ea0e358cb66e560818a47b0eeedf41ce1cdbd20c70b", size = 150818, upload-time = "2024-02-15T11:07:00.451Z" }, ] +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579, upload-time = "2026-05-20T13:08:56.396Z" }, + { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106, upload-time = "2026-05-20T14:00:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478, upload-time = "2026-05-20T14:05:45.88Z" }, + { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800, upload-time = "2026-05-20T13:14:29.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518, upload-time = "2026-05-20T14:02:28.662Z" }, + { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593, upload-time = "2026-05-20T13:14:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800, upload-time = "2026-05-20T13:09:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862, upload-time = "2026-05-20T13:09:10.498Z" }, + { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637, upload-time = "2026-05-20T13:12:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840, upload-time = "2026-05-20T14:00:11.079Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416, upload-time = "2026-05-20T14:05:47.118Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397, upload-time = "2026-05-20T13:14:30.696Z" }, + { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469, upload-time = "2026-05-20T14:02:30.192Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115, upload-time = "2026-05-20T13:14:40.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246, upload-time = "2026-05-20T13:12:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575, upload-time = "2026-05-20T13:12:07.043Z" }, + { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428, upload-time = "2026-05-20T14:00:12.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064, upload-time = "2026-05-20T14:05:48.662Z" }, + { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697, upload-time = "2026-05-20T13:14:32.943Z" }, + { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256, upload-time = "2026-05-20T14:02:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956, upload-time = "2026-05-20T13:14:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802, upload-time = "2026-05-20T13:13:15.481Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853, upload-time = "2026-05-20T13:15:37.301Z" }, + { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877, upload-time = "2026-05-20T13:10:19.078Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333, upload-time = "2026-05-20T14:00:14.758Z" }, + { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443, upload-time = "2026-05-20T14:05:50.159Z" }, + { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356, upload-time = "2026-05-20T13:14:35.091Z" }, + { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216, upload-time = "2026-05-20T14:02:33.403Z" }, + { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427, upload-time = "2026-05-20T13:14:43.71Z" }, + { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225, upload-time = "2026-05-20T13:13:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590, upload-time = "2026-05-20T13:13:37.382Z" }, +] + +[[package]] +name = "grpcio" +version = "1.81.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/f3/23f47b24f8d8c2028eba501db3acfbb2f592cbb5995eaa6e363a627b74d7/grpcio-1.81.0.tar.gz", hash = "sha256:a5acd7efd3b1fe9b4eb0bcaaa1507eed68a0ad0678b654c3f7b464df9ba9dca5", size = 13032272, upload-time = "2026-06-01T05:56:22.827Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/d5/896a3aaf07068d707d88b282a04914b872db4d32d3c7e6d88e43a3b911fa/grpcio-1.81.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:57b3b0e73a518fa286959b40c3eddd02703504ca186e8b7b2945954519bd8b2c", size = 6053538, upload-time = "2026-06-01T05:54:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/68/6a/7e3eafa4727cd405ff917605ed2949e2af162f233f5cbdd773723a5fea7d/grpcio-1.81.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8bb1789c94322a13336a2b6c58d9c14d68f8628b6e24205a799c69f5bf8516ce", size = 12053447, upload-time = "2026-06-01T05:55:01.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/79/a4302aa82428de48a922421f522b027a1a727ab4d0926368454aa953d36d/grpcio-1.81.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e4d053900a0d24b75d7521139a3872150301b3d6bde3bed5e12318fb25791e4d", size = 6595872, upload-time = "2026-06-01T05:55:04.946Z" }, + { url = "https://files.pythonhosted.org/packages/b4/1f/7ff2850eaefbecf99af3f624dbb28dd1ad6c5fd4c1d8c26909ed6482673b/grpcio-1.81.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:db217c2e52931719f9937bd12082cd4d7b495b35803d5760686975c285924bf8", size = 7303857, upload-time = "2026-06-01T05:55:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/1f3896a9baae1f2aedf4e99c55291d6fa1f30ad9603d63bc18bda967b53e/grpcio-1.81.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19f201da7b4e5c0559198abe5a97157e726f3abe6e8f5e832d4a50740f6dcc22", size = 6809676, upload-time = "2026-06-01T05:55:09.513Z" }, + { url = "https://files.pythonhosted.org/packages/34/8b/3441983718095208c5d797fd3239882e97ea89a629f41c8df94b4eef4df9/grpcio-1.81.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:275144b0115353339dbb8a6f28a9cf8997b5bf40e37f8f66ac0b0ea57e95b43f", size = 7412654, upload-time = "2026-06-01T05:55:12.777Z" }, + { url = "https://files.pythonhosted.org/packages/3c/98/1eddf07df6e4fe85cf67502a793f7b05468b2dca3d1ef35b972cf5d54468/grpcio-1.81.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5192857589f223e5a98ff0e31f6e551b19040e647d17bfe10116c8a2ce3b8696", size = 8408026, upload-time = "2026-06-01T05:55:15.514Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/3860341e6a1f5347be6ab35c6c0e1e3a8eb59d010388207fd561dcf01a88/grpcio-1.81.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6ff087cb1f563f47b504b4e29e684129fc5ae4863faf3ebca08a327764ee6cb", size = 7849498, upload-time = "2026-06-01T05:55:18.078Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3f/0ea06bd85c701966aa3f8f37314f2ed83520d2b7590f42d643d445d8bc8b/grpcio-1.81.0-cp312-cp312-win32.whl", hash = "sha256:98c6240f563178fc5877bd50e6ff274463e53e1472128f4110742450739659fa", size = 4184161, upload-time = "2026-06-01T05:55:20.127Z" }, + { url = "https://files.pythonhosted.org/packages/39/e3/a7c387406827a86f99ad7838b995bf9b4a182ffe2d2c439ed2873efec952/grpcio-1.81.0-cp312-cp312-win_amd64.whl", hash = "sha256:87e33b7afcfb3585121b5f007d2c52b8c534104d18f556e840d35193ca2a9141", size = 4929958, upload-time = "2026-06-01T05:55:22.736Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/779ee53c931d0fd55c1d459fde43e485172caa3ac87cbd43d003a13a0185/grpcio-1.81.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:62bbe463c9f0f2ff24e31bd25f8dd8b4bae78900e315915a3195a0ef1471a855", size = 6054973, upload-time = "2026-06-01T05:55:25.043Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b6/7211807926b5a17f8d9a5d47c739a163d6812fefe3e4714e81cf92945ed7/grpcio-1.81.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43c121e135ae44d1559b430db2b2dfad7421cbbe40e1deba506c7dc62b439719", size = 12048662, upload-time = "2026-06-01T05:55:28.453Z" }, + { url = "https://files.pythonhosted.org/packages/64/89/b1b93ef6b34bd20bbaf707fa99133bc9cc302139d5ec6f77a165c7169796/grpcio-1.81.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f345de40ef2e65f63645d53d251824e6070e07804827c5b00ec2e44555f9f901", size = 6599116, upload-time = "2026-06-01T05:55:31.185Z" }, + { url = "https://files.pythonhosted.org/packages/eb/bc/c89f9b9d1c22895715356a1e009554dae66319e97826bb4d30bcda7d29e8/grpcio-1.81.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8c0855a350886f713b9e458e2a10d208009dcaa849f574e39cd6067db1fe1279", size = 7307591, upload-time = "2026-06-01T05:55:33.463Z" }, + { url = "https://files.pythonhosted.org/packages/65/4a/1df2a4cb4a1386e066ab7e4175e34bb884b35ccb60d3621c09c84af6aabb/grpcio-1.81.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a524cd530900bd24511fcb7f2ed144da4ea37711c4b094475d0bceca7a93a170", size = 6811797, upload-time = "2026-06-01T05:55:36.731Z" }, + { url = "https://files.pythonhosted.org/packages/8d/dc/fa189d20601a1be25b08850cfb733879bbb1047b62a8feec3a60e3e1a87b/grpcio-1.81.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e7746ba3e6efc9e2b748eff59470a2b8684d5a9ec607c6580bcaa5be175820bc", size = 7415131, upload-time = "2026-06-01T05:55:39.451Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a3/5625c48cb48d23c6631b3e5294f88e4c751f22a52591ae78859fab96dca1/grpcio-1.81.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:aaaa4f7f2057d795952e4eacf3f342be8b5b156992f6ac85023c8b98794ebd47", size = 8408398, upload-time = "2026-06-01T05:55:42.219Z" }, + { url = "https://files.pythonhosted.org/packages/75/34/0f8202c6809a46c2b4d69125ef3667c40b1c211f8e19930e5fa1f1197039/grpcio-1.81.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0fba53cb96004b2b7fb758b46b2288cb49d0b658316a4e73f3ef67230616ee65", size = 7844481, upload-time = "2026-06-01T05:55:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/c0/95/c3366b5b5edf4c4adc90f2e29ca16e57965a8e56dc8d2ee89565ba1905bb/grpcio-1.81.0-cp313-cp313-win32.whl", hash = "sha256:c197e2ef75a442528072b29e9755da299110e8610e8bcbb59a6b4cf55384f005", size = 4182777, upload-time = "2026-06-01T05:55:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/a9/a7/932f2f748511a32e641a2aba0d30dded3ed6e8bc330e0924e4d5d86853e6/grpcio-1.81.0-cp313-cp313-win_amd64.whl", hash = "sha256:194eddfacc84d80f50512e9fd4ee851d5f2499f18f299c95aa8fb4748f0537e0", size = 4928085, upload-time = "2026-06-01T05:55:50.158Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1d/28b231333857deb840bc3d182ae087510170ea6d68f21393aeb0fe499530/grpcio-1.81.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:a9351055f52660b58f3d4890ea66188b5134399f82b11aa0c55bd4b99eff5390", size = 6055712, upload-time = "2026-06-01T05:55:52.889Z" }, + { url = "https://files.pythonhosted.org/packages/e8/b8/999c14f9dff0fc47549d2e827cba1343ddc18e1d1bf0d06d2cf628eecbd9/grpcio-1.81.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:300f3337b6425fd16ead9a4f9b2ac25801acb64aa5bc0b99eb69901645b2b1d2", size = 12057189, upload-time = "2026-06-01T05:55:55.952Z" }, + { url = "https://files.pythonhosted.org/packages/1e/3d/1fbde079572562af65351151d840525a13879eb7b481d35b55cd64c6127a/grpcio-1.81.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:97bbd623f7ded558fd4f7cb5a4f600c4d4de65c5dd364c83a5b14b2a10a2d3b5", size = 6608136, upload-time = "2026-06-01T05:55:59.069Z" }, + { url = "https://files.pythonhosted.org/packages/32/89/1f17cb6882abfd8e5a303a25d5d1665abef5a8c499a96198c65a651d1b85/grpcio-1.81.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ff83d889e3ebf6341c8c7864ad8031591ad5ca61599072fc511644d1eb962d2b", size = 7307045, upload-time = "2026-06-01T05:56:02.376Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/f98e91b2e755652e637ea2144318b0229b290062199f761b445fe1fa6015/grpcio-1.81.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c4fe218c5a35e1d87a5a26544237f1fa41dfd9cbd3c856b0810a30061f8b0aaf", size = 6812794, upload-time = "2026-06-01T05:56:05.777Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0c/77892d715ac41e7ec0ace2a50080ffb64e189188056f607a66fe0014d1ee/grpcio-1.81.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b8b025b6af43ee0ad4a70307025d77bcab5adde7c4597786010d802c203e9fc5", size = 7422767, upload-time = "2026-06-01T05:56:08.524Z" }, + { url = "https://files.pythonhosted.org/packages/3f/b8/aa04590c6564714d94954515f15a236e59d4b9b3ad01e615f1b706d7792d/grpcio-1.81.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:3d4e0ce5a40a998cf608c8ba60ecfe18fdf364a9aa193ae4ac3faeecd0e86757", size = 8408551, upload-time = "2026-06-01T05:56:11.283Z" }, + { url = "https://files.pythonhosted.org/packages/43/3d/4f4a3450a1973568910c6909cb74abbf2126f68aefae5976962f9f7ad50d/grpcio-1.81.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aa948712c8e5fa40ec250870bda14bc7578e1bb832a8912d9d2a0f720518edbe", size = 7846468, upload-time = "2026-06-01T05:56:14.536Z" }, + { url = "https://files.pythonhosted.org/packages/88/f4/5827fd248221ad3b44161c23ce9b5f4ee405b04fc6da5fd402a9aa87a84a/grpcio-1.81.0-cp314-cp314-win32.whl", hash = "sha256:fbbe81314a9d92156abce8b62c09364eb8bafc0ca2a19919a45ec64b5c6cb664", size = 4264427, upload-time = "2026-06-01T05:56:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/127dc2b246096ad50ef7c8d9b7b31d757787aeb796368bcdd4454e4204c4/grpcio-1.81.0-cp314-cp314-win_amd64.whl", hash = "sha256:b93cee313cae4e113fbb3a0ce1ea5633db6f63cfde2b2dc1d817429026b2a50b", size = 5070848, upload-time = "2026-06-01T05:56:19.735Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "habluetooth" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "async-interrupt", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "bleak", marker = "python_full_version < '3.14.2'" }, + { name = "bleak-retry-connector", marker = "python_full_version < '3.14.2'" }, + { name = "bluetooth-adapters", marker = "python_full_version < '3.14.2'" }, + { name = "bluetooth-auto-recovery", marker = "python_full_version < '3.14.2'" }, + { name = "bluetooth-data-tools", version = "1.28.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "btsocket", marker = "python_full_version < '3.14.2'" }, + { name = "dbus-fast", marker = "python_full_version < '3.14.2' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/94/fcf28cd5bb9c427eb67feb0d3c0a31b7c2821be31e0d88406be9d53d4428/habluetooth-6.1.0.tar.gz", hash = "sha256:9b5ac9cb9a07bb9690f04e8587abdb5ffebb69e66163f55fbedf99d90fc554c9", size = 50852, upload-time = "2026-04-19T22:11:47.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/21/c7d525ee46607a5e9d6cf3b0952ff42f5ff7bc0c21cf59f3a787d80102ec/habluetooth-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c04f3fbbfbc5cdb20ef16f21832361ccce7335d45e880721e9bf4ba0d4f4534", size = 575352, upload-time = "2026-04-19T22:27:51.895Z" }, + { url = "https://files.pythonhosted.org/packages/e0/66/7376c967d2ea5cc78b5eb4ac698f6b5235fa691a7ef938d30f28a9b49322/habluetooth-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07679d93c79b69b4afb23e8911c2ac5c76c2edc6463983e820fbde63efae76c1", size = 669703, upload-time = "2026-04-19T22:27:53.255Z" }, + { url = "https://files.pythonhosted.org/packages/07/a7/0ab45f3ced85fd09dcc8885a5b3cdbf24c2bd942cbc3360e3a857f9f4cc2/habluetooth-6.1.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:11cc30319be5703fe34f27bac0ba1898919c3abafb6c264533121701ffba7c5e", size = 656119, upload-time = "2026-04-19T22:27:54.903Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/f15ae52eb8184fb53b6ff86123b9c32d2b1cab558bd12a27e3f0e0964d0b/habluetooth-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2888297e0b3224b1631954fe1b5de6c81d2bae49b77afcf58d19c2b51aea558", size = 715636, upload-time = "2026-04-19T22:27:57.066Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b5/b66076857e6ffe357ce61d6e58312565f655a621c0c8ea8daad363088ca2/habluetooth-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c31e0cea67bac57ff019e3c7cbae3d86c4568dc921ac4d9e77233d6dc577cc61", size = 677843, upload-time = "2026-04-19T22:27:58.572Z" }, + { url = "https://files.pythonhosted.org/packages/e8/27/ea0c20aeeef35de67e47cc79dfda0e172322a45bd5173a0f9c8c7ee57609/habluetooth-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:78d97426c29db072cf5b1bcd8b7791f33c848a5387234e9dfd4569d4dedcf1e1", size = 662165, upload-time = "2026-04-19T22:28:00.319Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c5/75d3babe92fd3f01134591bed90e39a4e67c5e3cff97cc4d0b4c9075088b/habluetooth-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8b302174f0b1bb31300bab98205d5cf31869c6d1c435b71868b2c122dffa8e0a", size = 720485, upload-time = "2026-04-19T22:28:01.781Z" }, + { url = "https://files.pythonhosted.org/packages/41/cb/b13917984ca669b1e04e89a601438a02811331e65f88a500ed0893714a3e/habluetooth-6.1.0-cp312-cp312-win32.whl", hash = "sha256:4d720ae1ded1a28db8f57f382c7141604cb70b30f1e9407ecb77083212ffefb7", size = 462054, upload-time = "2026-04-19T22:28:03.614Z" }, + { url = "https://files.pythonhosted.org/packages/17/c7/a5078ee0ad8391515202a7cf0438133220d48542a01eae2c77a5f8172fca/habluetooth-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:00f424c8fe12dd51ba6f05f0c20d218ee4386ce5fcabc98bb51db0072e30d2bf", size = 533373, upload-time = "2026-04-19T22:28:05.214Z" }, + { url = "https://files.pythonhosted.org/packages/4f/2e/8d8390d5b038b65ea011923f7b066c89eaed589cc7e73faf22715935411d/habluetooth-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2069531cca6e1777437061c148eee999ffff219e50a7b466ad5c8bc5c0f5cbb2", size = 571365, upload-time = "2026-04-19T22:28:06.987Z" }, + { url = "https://files.pythonhosted.org/packages/25/e5/a5f4f073f1b74007914523171b2623d2836ea52eb7911857dda3ce7740de/habluetooth-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbb53d32d6305961fe501f9c6b10becddcdd9681b01faf8cbb11bf3027a92848", size = 668218, upload-time = "2026-04-19T22:28:08.878Z" }, + { url = "https://files.pythonhosted.org/packages/13/ae/a98bf2f400993330e2a4b5450d79c3e0ca9d9c50042665f45fe5bd0e7882/habluetooth-6.1.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:48a4dbe7ca33ee77ad067fe9528be535f67fb217e008984aa1125ef69eb0688f", size = 654660, upload-time = "2026-04-19T22:28:10.33Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2a/c3e6a9a4c7d81cba6796d112483b2e5e9eec06f5859b7f174e6d94bb930f/habluetooth-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b8eb489e7b97d18646d24fa77797a6220c20c68e769b18e5f5277b4cdd2ee2d", size = 714057, upload-time = "2026-04-19T22:28:11.934Z" }, + { url = "https://files.pythonhosted.org/packages/35/0c/dd64b278e1293f2e0e0927ce77484ea2f03af3ccb194ecb94fb5bb237a17/habluetooth-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2fe472287b1f71b2319e78863bb640f4e6c46d2fda9a0a17eeb6c717db87fb2", size = 677460, upload-time = "2026-04-19T22:28:13.702Z" }, + { url = "https://files.pythonhosted.org/packages/63/2c/ef1d3aa3ea3d3e6206eb8da1cb2bdb065bee43276a609cf55d034f71e311/habluetooth-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cc43cce7506310625ae6545e4ce892b23dd36a8a61acb6900f8c8bc0bc99a309", size = 661541, upload-time = "2026-04-19T22:28:15.412Z" }, + { url = "https://files.pythonhosted.org/packages/60/8f/e55bd2afec4227340d9f7148a98564bb9045a80348330d3a99edba80505d/habluetooth-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6dd9108395690cd5582dbcc92d59f150e3549aaffc0ee0619e9aca04e752bbd8", size = 719763, upload-time = "2026-04-19T22:28:17.442Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7d/2e8b94906fb700ca6ce1ec5688d5d673c049b6621a8d19e61034de409f95/habluetooth-6.1.0-cp313-cp313-win32.whl", hash = "sha256:8b6b5edad7fee3aef6f270a97795e99f13983ca3b21a99bf35cec5447a5ce62f", size = 461456, upload-time = "2026-04-19T22:28:19.157Z" }, + { url = "https://files.pythonhosted.org/packages/93/d0/9509417e03c76c7ca33a4d4bc8b28e9aa19e31842830b89419cee16ddf44/habluetooth-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:ec8e626ea58a5109070ca4d5ef529bce73f55ba91a75300391f3c9439206a922", size = 533512, upload-time = "2026-04-19T22:28:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/fd/10/fbe581101510d5c1d04f045d40d332e2294f1aaf85b75c8d90f363994216/habluetooth-6.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a7e03e19ca1406d031bf3569849a3a41ed0d99f109dd58eddf77f30c525a0890", size = 577925, upload-time = "2026-04-19T22:28:22.461Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5d/53ebb861788379f49c7f628f3400f39efbf7655c6d5c140d5956f4b20cd7/habluetooth-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:81a6c739a4f31c89855ef41a555e3c1034e6bd3f85702a78469d32d350166d53", size = 679647, upload-time = "2026-04-19T22:28:24.003Z" }, + { url = "https://files.pythonhosted.org/packages/da/00/06affc9630c21da46d8aecafda55de05b55248b1a1ad7fa483b75251c7ec/habluetooth-6.1.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:729679ed08b1c589e89cb5d54d7579f0a484a377ca06ff028078e1607cbd8c98", size = 647972, upload-time = "2026-04-19T22:28:26.036Z" }, + { url = "https://files.pythonhosted.org/packages/ac/47/5b3c8ff9611bf6946828a1bf54112cbba2e491db9f837eb7f128d3d031b1/habluetooth-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76ebcc33b81d8990ff13d8fa7797402c422fa68618d70217136b5b6e4bd1697", size = 719028, upload-time = "2026-04-19T22:28:27.746Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/da31a80d5c0f547a50ae1b55302eafbbd659b53bcb00dd8801609f805fe0/habluetooth-6.1.0-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:7fd1aebff1f3888e731e6fa470cc13a8cf0450932f5202885793c8e5bcb9d17d", size = 717793, upload-time = "2026-04-19T22:11:46.296Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f2/821ae8cbfd84a98ec5d2ddab9b6e35ab88a1a66e261527a376038b91aa2f/habluetooth-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f5bcbf145957d39358ae99c07ee04db5b303f4dd4c9cb3e210b14597726da47", size = 689008, upload-time = "2026-04-19T22:28:29.579Z" }, + { url = "https://files.pythonhosted.org/packages/05/51/22c96fffc0f8454d2ab79600c106e9fff00511241e71373c509d40f09e6f/habluetooth-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:e35ed0d81731f434ba0c8c8bdac73570ad5336b3f86b66a276338391da299da0", size = 652662, upload-time = "2026-04-19T22:28:31.446Z" }, + { url = "https://files.pythonhosted.org/packages/15/17/414ca5cf3972bca7c183a361725f7175cbfd113e7594725095a2d59189bb/habluetooth-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4e7766ce743e42eea2820fbacebc688cd3b9ed90cdb61676f37c5d92ec7b458e", size = 725110, upload-time = "2026-04-19T22:28:33.16Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ab/5d89e71c8abbb73f2e7cfde63381341fd802aad3d315e3b24c465f37e665/habluetooth-6.1.0-cp314-cp314-win32.whl", hash = "sha256:415e5ba996bfbffc571a751a6dc5a13f55846be24fc07f7d6ac310a0bd84fc05", size = 470707, upload-time = "2026-04-19T22:28:34.645Z" }, + { url = "https://files.pythonhosted.org/packages/de/db/697c3e97aa0a298ecc12afc287d244001b019aef226e3a3781328ac33ce1/habluetooth-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4b31f6a827f12665d0ccd13b16b709740c66b060f4b7cdd294288a731b58eca1", size = 544957, upload-time = "2026-04-19T22:28:36.218Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a8/e6dc190a5008cd099961a2ded31e09061722b9d416ccfd4d185730da4ea3/habluetooth-6.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cf41c1eb278968e73d00c4f229e3963f3595087f2568e7454645a4a34327442d", size = 1146040, upload-time = "2026-04-19T22:28:38.318Z" }, + { url = "https://files.pythonhosted.org/packages/62/91/deec7d3b9d32f3471e25b580f1182acde6799de599619de3e568f788f912/habluetooth-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b47141ca110f97c6f9d60673056faa8d49ee1d43700e81ce3cfc8a80f6414ff9", size = 1301043, upload-time = "2026-04-19T22:28:40.305Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d5/d5df26aa19237a16be3df52a447e24ea949e5273394088e9f3bc2b7e0764/habluetooth-6.1.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:553cb8cbfdd50a42abf68b92b1625a9beb0b5d19d78480199c14440ca787288a", size = 1220296, upload-time = "2026-04-19T22:28:42.05Z" }, + { url = "https://files.pythonhosted.org/packages/f9/20/907061cbe491dfb53ba1c23622460e317f50ad7935747045e69d690345b9/habluetooth-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48b8a2fe8a903ffe656d94b6337b43dc1d3d3f0965550fff9a1294f0df3c0c0", size = 1362816, upload-time = "2026-04-19T22:28:43.808Z" }, + { url = "https://files.pythonhosted.org/packages/8a/51/a433154052d282ac753a5f5146fa13a3cfbf677412733a276c3ffbf41199/habluetooth-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c62c552f6778ddbac0310a153a08e5059aa876bf4b73d66073d660e140286f6c", size = 1320933, upload-time = "2026-04-19T22:28:45.318Z" }, + { url = "https://files.pythonhosted.org/packages/17/53/e2ff9e460f38f4bf95c45cc90ae5b8de874a8957ff6a28b9352e41198ed2/habluetooth-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3435d77d447cce135594cdde0d9579c7de507d3fb82d77d551eeb5ef9c8ee290", size = 1242264, upload-time = "2026-04-19T22:28:47.028Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c2/220dd1ccca28e312f4ca83453c0d72de09d1a9cd1b3460d0b4373714a72e/habluetooth-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f5682c7a269acd41ee78cc7abb8b0225c70e39c22c1db888bcd26629d0a3abff", size = 1378101, upload-time = "2026-04-19T22:28:48.708Z" }, + { url = "https://files.pythonhosted.org/packages/60/1a/b28fc02284d3e0155843441fea353ec916b362e14d726a564b8fdea2a84e/habluetooth-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:aa5d791584572f08ff5e5141de0ed368909d62d34ce1c739e8d9ddaa346474fe", size = 973884, upload-time = "2026-04-19T22:28:50.634Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ff/0a3dd553a7d9dc67f29b0ac4627a43aeb69703bf3ffb4d2476ea9605a51a/habluetooth-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c625f1e1afac4bd7f834a3b29c742789d1199029d84bff183799e06a0334eb9b", size = 1147734, upload-time = "2026-04-19T22:28:52.331Z" }, +] + +[[package]] +name = "habluetooth" +version = "6.8.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "async-interrupt", version = "1.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "bleak", marker = "python_full_version >= '3.14.2'" }, + { name = "bleak-retry-connector", marker = "python_full_version >= '3.14.2'" }, + { name = "bluetooth-adapters", marker = "python_full_version >= '3.14.2'" }, + { name = "bluetooth-auto-recovery", marker = "python_full_version >= '3.14.2'" }, + { name = "bluetooth-data-tools", version = "1.29.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "btsocket", marker = "python_full_version >= '3.14.2'" }, + { name = "dbus-fast", marker = "python_full_version >= '3.14.2' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/4f/8e1cfbf4c465e2ce2004dacb50b7d8f8c3921fa6d477b9265f23dc2e0af3/habluetooth-6.8.3.tar.gz", hash = "sha256:e2bc83250683fce51ab9c7e4f24e680d0e63ddcf946509218893f0c5e194ab52", size = 79803, upload-time = "2026-06-07T05:14:29.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/71/c0c495ece4f8410280f96469867837dd310aceda88d9b03c36e3486a6ea2/habluetooth-6.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58f32acf524e5477328e395a038d9461027804e197e9f2a64b49ebb48375b634", size = 788483, upload-time = "2026-06-07T05:24:57.134Z" }, + { url = "https://files.pythonhosted.org/packages/62/21/dd911211b1ebed877b67d0708dac16542ae2ff846582efa12c86ee75fe9e/habluetooth-6.8.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c89cf66586b806397e55489651d43e5bdc03436e6618d4f9e0e18db814bf72", size = 915108, upload-time = "2026-06-07T05:24:58.693Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c7/14e11de4dd32ad0a9a7cdb036baf2e6d98011a1dec90f0b300e93371b5b0/habluetooth-6.8.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53e29d10f6bf13e37dc26fd39a1d3b392fb85df22cb3efd54b7d40af6c4b1901", size = 900944, upload-time = "2026-06-07T05:25:00.269Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a2/b9487d7a47de0a0516f199aa2448044127d769070886a143ee62dbfd7454/habluetooth-6.8.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8e7a333e8a2aaa16c1e39a302ed4fd6cd15f2f5c698c5a78556de981e8c12bb", size = 979512, upload-time = "2026-06-07T05:25:01.973Z" }, + { url = "https://files.pythonhosted.org/packages/02/8f/2a0dce2d1cb7cbfc041b9c28003390c65e06beecd458912fb82d66a21c01/habluetooth-6.8.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02b8dfce36ec13139da12da3dde3b42f3e99bf4476c184d6678fe642733f2815", size = 928052, upload-time = "2026-06-07T05:25:03.477Z" }, + { url = "https://files.pythonhosted.org/packages/da/63/f3317833db1e235ea9b188eb87808f4c4841876190adb9c7f0b21779cb18/habluetooth-6.8.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:310019e686f892650a4f21cce52bbf0517619eda7f1d4c9be5b9a67282d978c5", size = 911470, upload-time = "2026-06-07T05:25:04.79Z" }, + { url = "https://files.pythonhosted.org/packages/95/7b/bba8740ccb2ba4763210aebd5fbe3e0ae9fe456831194fbeff0e4fe8030c/habluetooth-6.8.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b449e14401b70bd3f5aaf19fcb8cd2f9fa59d8aafd09baf57c70cfe644019d36", size = 987407, upload-time = "2026-06-07T05:25:06.461Z" }, + { url = "https://files.pythonhosted.org/packages/de/45/9cfb2becd89547e2ae870ed048ff8ca61dc3b42701b598befdd2e87ab382/habluetooth-6.8.3-cp312-cp312-win32.whl", hash = "sha256:4523a265d911946673bf513ca38dd98ffcab006b8fdc763bac7068d5578d9afc", size = 629646, upload-time = "2026-06-07T05:25:08.028Z" }, + { url = "https://files.pythonhosted.org/packages/1f/72/60f92a3356f446212a7f70680e00a3ca420b793a97c0594494701bc96ddc/habluetooth-6.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:3881afed89b5ea1c87d766d150f3dbe4165415e3f4093a985ccb3f0117298054", size = 728548, upload-time = "2026-06-07T05:25:09.721Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c7/a940858506f03849e2d8fb10ef141d5a6a14e5f52490eb08e75a56e38fab/habluetooth-6.8.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:589df7d94d30814dfa53f9f5797fc37e281d546fe77ac99bfadc4dc4f140b1d9", size = 784135, upload-time = "2026-06-07T05:25:11.291Z" }, + { url = "https://files.pythonhosted.org/packages/77/8f/4ba734b0908e343389a495693a7e7f857f6fc8addeab6b7ede2f009c652e/habluetooth-6.8.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bd3dee4684c5b7095d60a4023916f71e122878e4c72d372ead4b7c3a3258ed", size = 912493, upload-time = "2026-06-07T05:25:13.149Z" }, + { url = "https://files.pythonhosted.org/packages/29/25/a07497ed33079642a84429a697fc1d43f0ca2d34f4bfa2fb2377187898e2/habluetooth-6.8.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1b078df6ee2d9e59d0ef1ee219ac4f8037b2d6c4a3ad6f2e37577f088ec0a153", size = 898821, upload-time = "2026-06-07T05:25:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e3/d08b21c6e7eee521bb91a54f7b2053651f0f99da8f014b4d6f529c790df5/habluetooth-6.8.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc084df3af41be07f34d230f4950980ed774c0ab85919bf20eb7d30547fdcdc1", size = 975855, upload-time = "2026-06-07T05:25:16.373Z" }, + { url = "https://files.pythonhosted.org/packages/f6/87/5dd2c5fd6d5427dc2fe744aadedced5493bf8eaec9893ddc5896c2bad4dd/habluetooth-6.8.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4737893d057246a1ee9e54d394622ebb0af2f47c2deead04bb4e3e9d6411140e", size = 925747, upload-time = "2026-06-07T05:25:17.917Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/d845ba242408c925f39975bd80f729a4e1bd8d1063d2a4841f33f781aa10/habluetooth-6.8.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7424030939d79ca504838bca81c9eed2b49de9135a1418c4e0a84232e9aaa730", size = 909907, upload-time = "2026-06-07T05:25:19.468Z" }, + { url = "https://files.pythonhosted.org/packages/dc/6d/27351f6c94807704c42d55585cca5b911c3cd6c89245110ab3900282db61/habluetooth-6.8.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d39d7f17e2c1ea7bdb3cfdd9c88f4c3b9fea3804c6ac2cc3674848e209d1898", size = 985073, upload-time = "2026-06-07T05:25:21.228Z" }, + { url = "https://files.pythonhosted.org/packages/53/33/f80defafd0b040c6e7ba0eb10a66a784f866b4ca4245082a5003ed655f7e/habluetooth-6.8.3-cp313-cp313-win32.whl", hash = "sha256:339b8e712ed7e876817531b4f5041e5fcb43e8f5785c67171b5b7eaee08dbe0e", size = 628479, upload-time = "2026-06-07T05:25:22.966Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cc/2c7614b22d9dbfca67ad2ef799340b1102998d3558794e97562551e95a8d/habluetooth-6.8.3-cp313-cp313-win_amd64.whl", hash = "sha256:9fb79bdf6edd3eaa45ff23c3595a4ecaa228b683b7639015ca1711ad7d123ad7", size = 728368, upload-time = "2026-06-07T05:25:24.5Z" }, + { url = "https://files.pythonhosted.org/packages/43/e7/a2d98fde9a63197d54915bfef604c3e70d01d17c99e5573edd04e2f0530d/habluetooth-6.8.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:088098d414754e71e8c0cf39d719248284179a48abd1ce8cc0808759daabe41b", size = 791368, upload-time = "2026-06-07T05:25:26.187Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fe/bac085cd50fa1937971c970166450f6e7df7aa7046af249999502e4ee451/habluetooth-6.8.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6079ada3ba1010c125f71475280b98d6cc4d090dc751c22942ed5dd38efcab3b", size = 926700, upload-time = "2026-06-07T05:25:27.576Z" }, + { url = "https://files.pythonhosted.org/packages/da/12/cdf4d8be0215e72f3c909c1ed6994b055af9dd84f1525139ecbc743d4d62/habluetooth-6.8.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6830be2cdbbb8e9f1a905f5c20623f735148a44ec27a353d45fca8f6e8c157e", size = 889075, upload-time = "2026-06-07T05:25:29.174Z" }, + { url = "https://files.pythonhosted.org/packages/82/24/c9821a676de2795042e799d57fd84a048f0ad3befb3c2c2b0697606fa1ec/habluetooth-6.8.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a375068d65445bc374b2f04ff35887a321fe23d0dacb675800fd11ac0ec9f4e", size = 981334, upload-time = "2026-06-07T05:25:30.595Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b0/61bc923c670b571b4bdb73b25c235aa1f4f0a17e1849ad196e6a34b44d37/habluetooth-6.8.3-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:cba7a43be63320020c24a549a2b4f0de574c9105c52237b82be5a5276ca763d5", size = 979939, upload-time = "2026-06-07T05:14:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/ba/47/e8a76fedf2671e1e3907fe22d54e46babcb30cba6fd5f042257af45f3205/habluetooth-6.8.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bc8f81a2669d9c008506f3783583dd749082f11031a83361f8dd6a4404583277", size = 940414, upload-time = "2026-06-07T05:25:32.505Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e4/0321b8d54335875564310ee770a0c22f825328e8eacaf7c198a9743963b3/habluetooth-6.8.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d13c1de26d683123e8d1f58c683c28cb7833167b2efd5f5acb602d614bfeb3", size = 896598, upload-time = "2026-06-07T05:25:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9c/716b066c8bb005d425340f9919e0b4aa61d044a472cfe6ea518b8f2ac816/habluetooth-6.8.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:09ac6205f294a9c6ffc9e58be93e5719f91852092f7eb76c159e17aece720815", size = 990726, upload-time = "2026-06-07T05:25:35.808Z" }, + { url = "https://files.pythonhosted.org/packages/ac/98/250c3577f7e07f8cf61118790dafdc49a5b72feb0fa55913622f575ef167/habluetooth-6.8.3-cp314-cp314-win32.whl", hash = "sha256:cf9cc826ca1c3bb49349a52541648902e9c09af6407788b1c4d7f60fbc1c0661", size = 642562, upload-time = "2026-06-07T05:25:37.491Z" }, + { url = "https://files.pythonhosted.org/packages/cb/cb/d9874e5fd0826bde146c027ed88f45453526062882b02ab05f045b4a48f5/habluetooth-6.8.3-cp314-cp314-win_amd64.whl", hash = "sha256:ae0bff9f7004c57834648e97621ba164a25a126080f66fb9625f91564f6cb0d2", size = 743530, upload-time = "2026-06-07T05:25:38.885Z" }, + { url = "https://files.pythonhosted.org/packages/a4/af/468ffecb717166c65a54a7543482d5223c9e05adff373037b0cf651b865c/habluetooth-6.8.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3eb387a7b6aad3906592251dd5fa9292342e4c8079f3305c9de8e6e735dc497b", size = 1558524, upload-time = "2026-06-07T05:25:40.331Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/54fc22ab2916dec3d15c3c6a700b610258a30fa4052dc8d67df9c9d7be2f/habluetooth-6.8.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77322f033d9e0dcb873c8a6fb341bc8f38002160f5fa8ff066cade412a538266", size = 1766234, upload-time = "2026-06-07T05:25:42.157Z" }, + { url = "https://files.pythonhosted.org/packages/08/d2/74b163584923a0414f691b3ec2a6364786d9672d11a745a81fc9e36afb8b/habluetooth-6.8.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e13a75ab773d47f13a1ae977cff760802148cc01839d21c79628210f6e8a6784", size = 855221, upload-time = "2026-06-07T05:25:43.797Z" }, + { url = "https://files.pythonhosted.org/packages/78/0b/f8834156e4b0ada50894f8340e34e0a0798f695990f9ffa3fc220776e953/habluetooth-6.8.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e899e6930d1d898bf64c7ae44580b86c58432c24850e5d09b2b72ba06a9ebc7", size = 1856395, upload-time = "2026-06-07T05:25:45.528Z" }, + { url = "https://files.pythonhosted.org/packages/17/cb/c5ae4095adb70b5d457ac50f1477037603242a458bcd1149eb0e21e0cd1e/habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e5f668cc71b5dc937045febc23f90fef16b39c367f42185b7903d8ac8c264806", size = 1795243, upload-time = "2026-06-07T05:25:47.204Z" }, + { url = "https://files.pythonhosted.org/packages/60/a3/9f00ba920efd40b5f019f310a38ce4929c347b7d4420e652aa7ac7497cef/habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:776ac4ce53fb4652d8f671e303363ebbbf0544096941707d9d09fccb683328cb", size = 890346, upload-time = "2026-06-07T05:25:48.978Z" }, + { url = "https://files.pythonhosted.org/packages/3c/be/9053436ffd2d0f49f2374b73ef2dc5471d059b14d862c8a085968c9a588d/habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ddcadf929276053e3cb2652ed18e032b029fcff88672cb5d1cdd045b9d6f700", size = 1877654, upload-time = "2026-06-07T05:25:50.734Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d8/9e0a3556127b42ff8cef70d2ae6544f03d207384b989d4e1274182a040ee/habluetooth-6.8.3-cp314-cp314t-win32.whl", hash = "sha256:2cb0bfb61212bd738ef7300050770d9bed41e67bf029b7968e0498f3209cbad7", size = 1250467, upload-time = "2026-06-07T05:25:53.06Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/78671bd9e53d7a3a6733ec9ed766e62437adac0bdb338e406e671fb2671b/habluetooth-6.8.3-cp314-cp314t-win_amd64.whl", hash = "sha256:67bb922f6856cd983f4f388126d492badc4ece7f2940e19416e0c046afa73844", size = 1460190, upload-time = "2026-06-07T05:25:54.772Z" }, +] + +[[package]] +name = "hass-nabucasa" +version = "0.81.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "acme", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "atomicwrites-homeassistant", marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "ciso8601", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "pycognito", marker = "python_full_version < '3.14.2'" }, + { name = "pyjwt", version = "2.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "snitun", version = "0.39.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/09/360586c672375587e5692be9e4b5e7c3b5510aa65ca4bd81fe81cf7e9ff9/hass_nabucasa-0.81.1.tar.gz", hash = "sha256:5be7430e6eda80f27e58d67f66a81d2bfebdcf3dfdf8615a0dc816daeaa6585d", size = 62710, upload-time = "2024-06-04T03:25:30.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/bf/8dd3af6ca8f35d60ad8c47b47a6f94a2157a7c2420796d69a885f5d05cd5/hass_nabucasa-0.81.1-py3-none-any.whl", hash = "sha256:604618c25ec53ac2b5e06c08242e0299c24e87975185f4af119b7659ac3a01a4", size = 54003, upload-time = "2024-06-04T03:25:28.292Z" }, +] + +[[package]] +name = "hass-nabucasa" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "acme", version = "5.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "atomicwrites-homeassistant", marker = "python_full_version >= '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "ciso8601", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "grpcio", marker = "python_full_version >= '3.14.2'" }, + { name = "icmplib", marker = "python_full_version >= '3.14.2'" }, + { name = "josepy", marker = "python_full_version >= '3.14.2'" }, + { name = "pycognito", marker = "python_full_version >= '3.14.2'" }, + { name = "pyjwt", version = "2.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "sentence-stream", marker = "python_full_version >= '3.14.2'" }, + { name = "snitun", version = "0.45.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, + { name = "webrtc-models", marker = "python_full_version >= '3.14.2'" }, + { name = "yarl", version = "1.24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/40/53b290cecc71e7b12a4d8ce8b07c6a6c2da5c93ada8e19432cca0d6dde1f/hass_nabucasa-2.2.0.tar.gz", hash = "sha256:7bfaca35cf854197cdecfd2c1e41b263e3224e1abafbb58457552021bbbed6fc", size = 118895, upload-time = "2026-03-23T09:53:53.368Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/d2/997801ecfe37c620b5f980516b5b2be18b6e74f406e8e401273c777dc299/hass_nabucasa-2.2.0-py3-none-any.whl", hash = "sha256:366620278baed52e5fbf078d5262e84d401a938e14ca5c30d1aa280848ba865d", size = 90827, upload-time = "2026-03-23T09:53:51.865Z" }, +] + +[[package]] +name = "home-assistant-bluetooth" +version = "1.12.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "habluetooth", version = "6.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/18/0a8fcf4a8c52ed0ffd9cade5616d2bc336a72b53d8914d4e93527b3b4bf2/home_assistant_bluetooth-1.12.2.tar.gz", hash = "sha256:40d6c26a409c6a490f4ebc6d6b816e70743e6e0cad81942649998a8b281e5532", size = 7763, upload-time = "2024-06-24T13:48:28.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/b0/c5dd30073261268acfd20853481a3ea848f2180d2864c346bf5a382f4f8e/home_assistant_bluetooth-1.12.2-py3-none-any.whl", hash = "sha256:ded84c2f621255d2ed3bfa9a1ad824e2aadfbbd43c093da700fdc294f0c39202", size = 7915, upload-time = "2024-06-24T13:48:26.563Z" }, +] + +[[package]] +name = "home-assistant-bluetooth" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "habluetooth", version = "6.8.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/6f/914fd8086f502748907751766e26e3f7fe5204347a679703ee9954e55e4d/home_assistant_bluetooth-2.0.0.tar.gz", hash = "sha256:3febd16b194812b156024d9bc8ce8ec7622cdd8d159047cac504cc77ca034ea1", size = 7604, upload-time = "2025-07-09T18:01:47.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/3e/2ed9e59da8fc26c43cbd9d6abbd27217138a31c608b1d334cc5e680e4069/home_assistant_bluetooth-2.0.0-py3-none-any.whl", hash = "sha256:8d7c2ac48c303adacad73cb6cdfe7710f2fccd2a367d4c3106acf301b0d22b91", size = 7907, upload-time = "2025-07-09T18:01:46.506Z" }, +] + +[[package]] +name = "homeassistant" +version = "2024.9.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "aiodns", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiohttp-cors", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiohttp-fast-zlib", version = "0.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiozoneinfo", version = "0.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "astral", marker = "python_full_version < '3.14.2'" }, + { name = "async-interrupt", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "atomicwrites-homeassistant", marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "awesomeversion", version = "24.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "bcrypt", version = "4.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "certifi", marker = "python_full_version < '3.14.2'" }, + { name = "ciso8601", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "fnv-hash-fast", version = "1.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "hass-nabucasa", version = "0.81.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "home-assistant-bluetooth", version = "1.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "httpx", version = "0.27.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "ifaddr", marker = "python_full_version < '3.14.2'" }, + { name = "jinja2", version = "3.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "lru-dict", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "orjson", version = "3.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "packaging", marker = "python_full_version < '3.14.2'" }, + { name = "pillow", version = "10.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "pip", marker = "python_full_version < '3.14.2'" }, + { name = "psutil-home-assistant", marker = "python_full_version < '3.14.2'" }, + { name = "pyjwt", version = "2.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "pyopenssl", version = "24.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "python-slugify", marker = "python_full_version < '3.14.2'" }, + { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "sqlalchemy", version = "2.0.31", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "typing-extensions", marker = "python_full_version < '3.14.2'" }, + { name = "ulid-transform", version = "1.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "voluptuous", marker = "python_full_version < '3.14.2'" }, + { name = "voluptuous-openapi", version = "0.0.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "voluptuous-serialize", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "yarl", version = "1.9.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/e9/68752c6c4881144e60d5aa7f66862d7d51fe73dfaba7e7aa721a70aef39a/homeassistant-2024.9.3.tar.gz", hash = "sha256:429d40881f3db2ad4eac0551eea2c47d7d63f245bc2f695be19d71e33e00ccfd", size = 22557091, upload-time = "2024-09-24T07:24:00.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/a8/8a819f6eaf7a744c55e5abccaf61956de0a453302ab407a54a68102e34bf/homeassistant-2024.9.3-py3-none-any.whl", hash = "sha256:d175f3ceffa9d2ad3f965d934e9b6a1d70ec9f154b056884dbe057cdff612180", size = 38775069, upload-time = "2024-09-24T07:23:55.811Z" }, +] + +[[package]] +name = "homeassistant" +version = "2026.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "aiodns", version = "4.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiogithubapi", marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp-asyncmdnsresolver", marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp-cors", version = "0.8.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiohttp-fast-zlib", version = "0.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "aiozoneinfo", version = "0.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "annotatedyaml", marker = "python_full_version >= '3.14.2'" }, + { name = "astral", marker = "python_full_version >= '3.14.2'" }, + { name = "async-interrupt", version = "1.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "atomicwrites-homeassistant", marker = "python_full_version >= '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "audioop-lts", marker = "python_full_version >= '3.14.2'" }, + { name = "awesomeversion", version = "25.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "bcrypt", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "certifi", marker = "python_full_version >= '3.14.2'" }, + { name = "ciso8601", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "cronsim", marker = "python_full_version >= '3.14.2'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "fnv-hash-fast", version = "2.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "hass-nabucasa", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "home-assistant-bluetooth", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "httpx", version = "0.28.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "ifaddr", marker = "python_full_version >= '3.14.2'" }, + { name = "jinja2", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "lru-dict", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "orjson", version = "3.11.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "packaging", marker = "python_full_version >= '3.14.2'" }, + { name = "pillow", version = "12.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "propcache", marker = "python_full_version >= '3.14.2'" }, + { name = "psutil-home-assistant", marker = "python_full_version >= '3.14.2'" }, + { name = "pyjwt", version = "2.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "pyopenssl", version = "26.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "python-slugify", marker = "python_full_version >= '3.14.2'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "securetar", marker = "python_full_version >= '3.14.2'" }, + { name = "sqlalchemy", version = "2.0.49", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "standard-aifc", marker = "python_full_version >= '3.14.2'" }, + { name = "standard-telnetlib", marker = "python_full_version >= '3.14.2'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14.2'" }, + { name = "ulid-transform", version = "2.2.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "uv", marker = "python_full_version >= '3.14.2'" }, + { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, + { name = "voluptuous-openapi", version = "0.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "voluptuous-serialize", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "webrtc-models", marker = "python_full_version >= '3.14.2'" }, + { name = "yarl", version = "1.24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "zeroconf", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/8a/4f5461a4d9a1d36eeeb3ce23c9591919180347f7a163c2fc5c8f7cee9e78/homeassistant-2026.6.1.tar.gz", hash = "sha256:82d9c8b0eac95b22e18c473d76cb6f46255de0baea27a5b8f2a5cdc1a57c6255", size = 34328936, upload-time = "2026-06-05T20:40:59.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/0a/8f4b36861857ef74cb20da0333258a64e806d78f71211c2db5d1fd9fe242/homeassistant-2026.6.1-py3-none-any.whl", hash = "sha256:9d48ee5d267fba31df80b560fa698b884fe67e83d4271a1d74519498979aedbf", size = 56223888, upload-time = "2026-06-05T20:40:51.208Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.27.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "anyio", marker = "python_full_version < '3.14.2'" }, + { name = "certifi", marker = "python_full_version < '3.14.2'" }, + { name = "httpcore", marker = "python_full_version < '3.14.2'" }, + { name = "idna", marker = "python_full_version < '3.14.2'" }, + { name = "sniffio", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5", size = 126413, upload-time = "2024-02-21T13:07:52.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/7b/ddacf6dcebb42466abd03f368782142baa82e08fc0c1f8eaa05b4bae87d5/httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5", size = 75590, upload-time = "2024-02-21T13:07:50.455Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "anyio", marker = "python_full_version >= '3.14.2'" }, + { name = "certifi", marker = "python_full_version >= '3.14.2'" }, + { name = "httpcore", marker = "python_full_version >= '3.14.2'" }, + { name = "idna", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + [[package]] name = "humanfriendly" version = "10.0" @@ -640,6 +2226,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] +[[package]] +name = "icmplib" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/78/ca07444be85ec718d4a7617f43fdb5b4eaae40bc15a04a5c888b64f3e35f/icmplib-3.0.4.tar.gz", hash = "sha256:57868f2cdb011418c0e1d5586b16d1fabd206569fe9652654c27b6b2d6a316de", size = 26744, upload-time = "2023-10-10T17:05:12.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/ab/a47a2fdcf930e986914c642242ce2823753d7b08fda485f52323132f1240/icmplib-3.0.4-py3-none-any.whl", hash = "sha256:336b75c6c23c5ce99ddec33f718fab09661f6ad698e35b6f1fc7cc0ecf809398", size = 30561, upload-time = "2023-10-10T17:05:10.092Z" }, +] + [[package]] name = "identify" version = "2.6.19" @@ -658,6 +2253,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, ] +[[package]] +name = "ifaddr" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/fb4c578f4a3256561548cd825646680edcadb9440f3f68add95ade1eb791/ifaddr-0.2.0.tar.gz", hash = "sha256:cc0cbfcaabf765d44595825fb96a99bb12c79716b73b44330ea38ee2b0c4aed4", size = 10485, upload-time = "2022-06-15T21:40:27.561Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/1f/19ebc343cc71a7ffa78f17018535adc5cbdd87afb31d7c34874680148b32/ifaddr-0.2.0-py3-none-any.whl", hash = "sha256:085e0305cfe6f16ab12d72e2024030f5d52674afad6911bb1eee207177b8a748", size = 12314, upload-time = "2022-06-15T21:40:25.756Z" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -667,12 +2271,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "markupsafe", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245, upload-time = "2024-05-05T23:42:02.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271, upload-time = "2024-05-05T23:41:59.928Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "markupsafe", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "josepy" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/ad/6f520aee9cc9618d33430380741e9ef859b2c560b1e7915e755c084f6bc0/josepy-2.2.0.tar.gz", hash = "sha256:74c033151337c854f83efe5305a291686cef723b4b970c43cfe7270cf4a677a9", size = 56500, upload-time = "2025-10-14T14:54:42.108Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/b2/b5caed897fbb1cc286c62c01feca977e08d99a17230ff3055b9a98eccf1d/josepy-2.2.0-py3-none-any.whl", hash = "sha256:63e9dd116d4078778c25ca88f880cc5d95f1cab0099bebe3a34c2e299f65d10b", size = 29211, upload-time = "2025-10-14T14:54:41.144Z" }, +] + [[package]] name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "jsonschema-specifications" }, { name = "referencing" }, { name = "rpds-py" }, @@ -763,6 +2423,171 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/24/3c/2bec447bf2486dfe8ed9c99d01892d57388b3620883d2374b975f36f1963/looptime-0.7-py3-none-any.whl", hash = "sha256:efb3916196dbbe943a7f2853d79647e4476a5dd149306ce2736607d814930327", size = 21890, upload-time = "2026-01-03T13:06:24.785Z" }, ] +[[package]] +name = "lru-dict" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/96/e3/42c87871920602a3c8300915bd0292f76eccc66c38f782397acbf8a62088/lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b", size = 13123, upload-time = "2023-11-06T01:40:12.951Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/5c/385f080747eb3083af87d8e4c9068f3c4cab89035f6982134889940dafd8/lru_dict-1.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c279068f68af3b46a5d649855e1fb87f5705fe1f744a529d82b2885c0e1fc69d", size = 17174, upload-time = "2023-11-06T01:39:07.923Z" }, + { url = "https://files.pythonhosted.org/packages/3c/de/5ef2ed75ce55d7059d1b96177ba04fa7ee1f35564f97bdfcd28fccfbe9d2/lru_dict-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:350e2233cfee9f326a0d7a08e309372d87186565e43a691b120006285a0ac549", size = 10742, upload-time = "2023-11-06T01:39:08.871Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/f69a6abb0062d2cf2ce0aaf0284b105b97d1da024ca6d3d0730e6151242e/lru_dict-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4eafb188a84483b3231259bf19030859f070321b00326dcb8e8c6cbf7db4b12f", size = 11079, upload-time = "2023-11-06T01:39:09.766Z" }, + { url = "https://files.pythonhosted.org/packages/ea/59/cf891143abe58a455b8eaa9175f0e80f624a146a2bf9a1ca842ee0ef930a/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73593791047e36b37fdc0b67b76aeed439fcea80959c7d46201240f9ec3b2563", size = 32469, upload-time = "2023-11-06T01:39:11.091Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/d5976e9f70107ce11e45d93c6f0c2d5eaa1fc30bb3c8f57525eda4510dff/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1958cb70b9542773d6241974646e5410e41ef32e5c9e437d44040d59bd80daf2", size = 33496, upload-time = "2023-11-06T01:39:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/94d6e910d54fc1fa05c0ee1cd608c39401866a18cf5e5aff238449b33c11/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1cd3ed2cee78a47f11f3b70be053903bda197a873fd146e25c60c8e5a32cd6", size = 29914, upload-time = "2023-11-06T01:39:13.395Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b9/9db79780c8a3cfd66bba6847773061e5cf8a3746950273b9985d47bbfe53/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82eb230d48eaebd6977a92ddaa6d788f14cf4f4bcf5bbffa4ddfd60d051aa9d4", size = 32241, upload-time = "2023-11-06T01:39:14.612Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b6/08a623019daec22a40c4d6d2c40851dfa3d129a53b2f9469db8eb13666c1/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5ad659cbc349d0c9ba8e536b5f40f96a70c360f43323c29f4257f340d891531c", size = 37320, upload-time = "2023-11-06T01:39:15.875Z" }, + { url = "https://files.pythonhosted.org/packages/70/0b/d3717159c26155ff77679cee1b077d22e1008bf45f19921e193319cd8e46/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba490b8972531d153ac0d4e421f60d793d71a2f4adbe2f7740b3c55dce0a12f1", size = 35054, upload-time = "2023-11-06T01:39:17.063Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f2ae00de7c27984a19b88d2b09ac877031c525b01199d7841ec8fa657fd6/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:c0131351b8a7226c69f1eba5814cbc9d1d8daaf0fdec1ae3f30508e3de5262d4", size = 38613, upload-time = "2023-11-06T01:39:18.136Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0b/e30236aafe31b4247aa9ae61ba8aac6dde75c3ea0e47a8fb7eef53f6d5ce/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e88dba16695f17f41701269fa046197a3fd7b34a8dba744c8749303ddaa18df", size = 37143, upload-time = "2023-11-06T01:39:19.571Z" }, + { url = "https://files.pythonhosted.org/packages/1c/28/b59bcebb8d76ba8147a784a8be7eab6a4ad3395b9236e73740ff675a5a52/lru_dict-1.3.0-cp312-cp312-win32.whl", hash = "sha256:6ffaf595e625b388babc8e7d79b40f26c7485f61f16efe76764e32dce9ea17fc", size = 12653, upload-time = "2023-11-06T01:39:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/bd/18/06d9710cb0a0d3634f8501e4bdcc07abe64a32e404d82895a6a36fab97f6/lru_dict-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf9da32ef2582434842ab6ba6e67290debfae72771255a8e8ab16f3e006de0aa", size = 13811, upload-time = "2023-11-06T01:39:21.599Z" }, +] + +[[package]] +name = "lru-dict" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/06/0a/dec86efe38b350314c49a8d39ef01ba7cf8bbbef1d177646320eedea7159/lru_dict-1.4.1.tar.gz", hash = "sha256:cc518ff2d38cc7a8ab56f9a6ae557f91e2e1524b57ed8e598e97f45a2bd708fc", size = 13439, upload-time = "2025-11-02T10:02:13.548Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/a8/89e4c26e0e751321b41b0a3007384f97d9eae7a863c49af1c68c43005ca3/lru_dict-1.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7fa342c6e6bc811ee6a17eb569d37b149340d5aa5a637a53438e316a95783838", size = 16683, upload-time = "2025-11-02T10:01:15.891Z" }, + { url = "https://files.pythonhosted.org/packages/f1/34/b3c6fdd120af68b6eeb524d0de3293ff27918ec57f45eed6bef1789fd085/lru_dict-1.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd86bd202a7c1585d9dc7e5b0c3d52cf76dc56b261b4bbecfeefbbae31a5c97d", size = 11216, upload-time = "2025-11-02T10:01:16.867Z" }, + { url = "https://files.pythonhosted.org/packages/e9/7e/280267ae23f1ec1074ddaab787c5e041e090220e8e37828d51ff4e681dfd/lru_dict-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4617554f3e42a8f520c8494842c23b98f5b7f4d5e0410e91a4c3ad0ea5f7e094", size = 11687, upload-time = "2025-11-02T10:01:17.485Z" }, + { url = "https://files.pythonhosted.org/packages/ca/18/fec42416ceff98ae2760067ec72b0b9fc02840e729bbc18059c6a02cb01f/lru_dict-1.4.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:40927a6a4284d437047f547e652b15f6f0f40210deb6b9e5b77e556ff0faea0f", size = 31960, upload-time = "2025-11-02T10:01:18.158Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ef/38e7ee1a5d32b9b1629d045fa5a495375383aacfb2945f4d9535b9af9630/lru_dict-1.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2c07ecb6d42494e45d00c2541e6b0ae7659fc3cf89681521ba94b15c682d4fe", size = 32882, upload-time = "2025-11-02T10:01:18.841Z" }, + { url = "https://files.pythonhosted.org/packages/72/82/d56653ca144c291ab37bea5f23c5078ffbe64f7f5b466f91d400590b9106/lru_dict-1.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85b28aa2de7c5f1f6c68221857accd084438df98edbd4f57595795734225770c", size = 34268, upload-time = "2025-11-02T10:01:19.564Z" }, + { url = "https://files.pythonhosted.org/packages/94/ae/382651533d60f0b598757efda56dc87cad5ac311fba8e61f86fb916bf236/lru_dict-1.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cbbbb4b51e2529ccf7ee8a3c3b834052dbd54871a216cfd229dd2b1194ff293a", size = 32156, upload-time = "2025-11-02T10:01:20.22Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d1/d9df7e9272ccbc96f04c477dfb9abb91fa8fabde86b7fa190cb7b3c7a024/lru_dict-1.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e47040421a13de8bc6404557b3700c33f1f2683cbcce22fe5cacec4c938ce54b", size = 33395, upload-time = "2025-11-02T10:01:20.901Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6e/dafe0f5943a7b3ab24d3429032ff85873acd626087934b8161b55340c13a/lru_dict-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:451f7249866cb9564bb40d73bec7ac865574dafd0a4cc91627bbf35be7e99291", size = 31591, upload-time = "2025-11-02T10:01:21.606Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4d/9dd35444592bfb6805548e15971cfce821400966a51130b78dc021ee8f03/lru_dict-1.4.1-cp312-cp312-win32.whl", hash = "sha256:e8996f3f94870ecb236c55d280839390edae7f201858fee770267eac27b8b47d", size = 13119, upload-time = "2025-11-02T10:01:22.61Z" }, + { url = "https://files.pythonhosted.org/packages/8d/82/7e72e30d6c15d65466b3baca87cce15e20848ba6a488868aa54e901141a6/lru_dict-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:d90774db1b60c0d5c829cfa5d7fda6db96ed1519296f626575598f9f170cca37", size = 14109, upload-time = "2025-11-02T10:01:23.322Z" }, + { url = "https://files.pythonhosted.org/packages/85/95/ee171a68ae381ab988c50e3b7b136b1c598f5f683ba4a1e10c51e2480408/lru_dict-1.4.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2a5644bb1db0514abdad5e2f3d8f1beb6f7560c8cceb62079c40a4269de34b3c", size = 12248, upload-time = "2025-11-02T10:01:24.291Z" }, + { url = "https://files.pythonhosted.org/packages/a1/82/8de8e8fd96c44d46891415834ceb9f51c552840bda2d118394aca5e3153a/lru_dict-1.4.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:4209864be09ec20f6059fef8544697eb3d3729d63a983bf66457054bf3e40601", size = 12243, upload-time = "2025-11-02T10:01:25.254Z" }, + { url = "https://files.pythonhosted.org/packages/53/97/251cfb357c547a8fd06c2bc40db8a7f7eed7dbacef30d8d7e543522360e1/lru_dict-1.4.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8fef8dd72484b4280799c502c116acfdfcf0dedf3508bc9d0d19e684a6a23267", size = 10938, upload-time = "2025-11-02T10:01:25.89Z" }, + { url = "https://files.pythonhosted.org/packages/58/14/602791d219bc87197ae80f5fa0f77ca0af8e83e9a06c7cdb89db5575839e/lru_dict-1.4.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d64ddbe4c426fdc4cfc1abaea71d587d439397386a7b35d588f4fd64b695a83d", size = 11261, upload-time = "2025-11-02T10:01:26.879Z" }, + { url = "https://files.pythonhosted.org/packages/10/5d/a30a6fad150f20f084de8e243882a0488ad4929db41a2c8ce9be6cf56563/lru_dict-1.4.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:000ba9a2ab4dd1ad2d91764a6d5cce75a59de51534cdda478d1ddaa3cd8d5c48", size = 10801, upload-time = "2025-11-02T10:01:27.553Z" }, + { url = "https://files.pythonhosted.org/packages/64/4d/cee327e024d42972c598b7e0cd5063a1b1d7451efba31f7de7b6ca91e7d0/lru_dict-1.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ffad2758ce21d8fd6f0ae2628b31330732db8429a4b5994d2e107bed0ee11e68", size = 16688, upload-time = "2025-11-02T10:01:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/58/c8/2f86a1e448c5257b31424b96bf1385e7f96ec7841c2376db02811bbd395f/lru_dict-1.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1671e8d92fe35dfb38d3505a56338792d3e225032f8e94888b6e95b323120380", size = 11214, upload-time = "2025-11-02T10:01:28.888Z" }, + { url = "https://files.pythonhosted.org/packages/06/41/507c615cffaba67c35affd77dec25d3183bb87f404b41c8bb2b3053481ac/lru_dict-1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d5f01ada0cf0c1aa2bdc684e5ac0f6548be7eccc3ce8b4c0361db8445f867f04", size = 11689, upload-time = "2025-11-02T10:01:29.508Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/35aa1359f80174016b389f8be5fd48c4a5af0a04a73afb4906e5d4279f4a/lru_dict-1.4.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:74204239e30b8ec7976257c5b64565d7e3e8aea0cad0dd50a9b99e171aaf3898", size = 32034, upload-time = "2025-11-02T10:01:30.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/93/46301015bddd4552a1b76982ef788a7fb2a886efff83ad2c178cc7e68349/lru_dict-1.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7da0e451faa4d6dcae21c0f2527c540000b2f23ed8326a0bc1d870130fd12b1", size = 32919, upload-time = "2025-11-02T10:01:30.971Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cb/6d67145619d8ec3bba15fe145ff702ecf44991e33345d38c763501c1608a/lru_dict-1.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:071468a716768a9afca64659c390c1abb6d937b1897e07a0b70383f75637fce0", size = 34334, upload-time = "2025-11-02T10:01:31.663Z" }, + { url = "https://files.pythonhosted.org/packages/a5/44/50daaec6793ec2042079ed6a8b6a687b4be51b270b1d8ec5efd280116493/lru_dict-1.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77d209bcd396eb236c197bf4c95fab6848c61e0c1a5031cdde7f5c787e209f4", size = 32211, upload-time = "2025-11-02T10:01:32.468Z" }, + { url = "https://files.pythonhosted.org/packages/bd/53/355397949215e6b77b6771b973ee1dbc21fdd9f955925e47dce50d9d4727/lru_dict-1.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b21688fd7ece56d04c0c13b42fd9f904d46fc9ff21e3de87d98f3f5a14c67f74", size = 33461, upload-time = "2025-11-02T10:01:33.2Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/d660fa63144f38a0fd5b437a140517e3cff482d955ef6b9b4cf7651b9d85/lru_dict-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:989ef7352b347c82e5d5047f3b7ddf34b5a938e3f7b08775cacc9f28e97dd2a8", size = 31651, upload-time = "2025-11-02T10:01:33.874Z" }, + { url = "https://files.pythonhosted.org/packages/2e/77/0fae8d0702f7546f436efe06a684b301aad5c8a167bb2df6e42b0f821de5/lru_dict-1.4.1-cp313-cp313-win32.whl", hash = "sha256:a36e6e95b5d474ef90d04a5e3ad81ca362b473ec9534ed964222f3c0444138b8", size = 13120, upload-time = "2025-11-02T10:01:34.595Z" }, + { url = "https://files.pythonhosted.org/packages/4a/20/56a3f0d74c8fe32c01d3978387f66c9fb180c7f15bfd9fcecaa01b4e7736/lru_dict-1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8e73a1ec2d0f476d666ce7c91464b22854086951b319544d1850c508f5ce381f", size = 14112, upload-time = "2025-11-02T10:01:35.268Z" }, + { url = "https://files.pythonhosted.org/packages/98/02/8e04a8d744b466d4153502e2d92b453c2e5a549d49bf7fabfdca1621828a/lru_dict-1.4.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b770c7db258625e57b6ea8e2e0503ba0fbbdcde374baacf9adb256eb9c5adfa", size = 11119, upload-time = "2025-11-02T10:01:36.239Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/ee96f30127ff47c29966603f040e0485700fe0ca0e7d7b1ecbc9bf999eea/lru_dict-1.4.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:45d4dc338237cedcbacedab1afd9707b8f9867d8b601ec04e0395ec73f57405c", size = 11435, upload-time = "2025-11-02T10:01:36.857Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5a/897b33ba1974b6487848cafa5de7e93a7c4f5d9d3f43319ee010f6882830/lru_dict-1.4.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:5b31e9b6636f8945ad69c630c1891d810d62a91d99e792ef0b9ca865b6c26745", size = 10988, upload-time = "2025-11-02T10:01:37.531Z" }, + { url = "https://files.pythonhosted.org/packages/19/8e/b87d0f2bfcad0169afc00e23e014bad9af252206ec2cbc6079f12bece58e/lru_dict-1.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:f9335d46c83882a1b5deffed8098a2dd9ad66d2bd6263f416fc4c73f63e26904", size = 16733, upload-time = "2025-11-02T10:01:38.194Z" }, + { url = "https://files.pythonhosted.org/packages/ab/19/d2384266864b1e5b1cc20527ae468550d3b23a71636371b40e4663276294/lru_dict-1.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:17844b4f8dd996144d53380395d73832e2508159ad49ed4fbcb62f1787a5feaf", size = 11222, upload-time = "2025-11-02T10:01:39.093Z" }, + { url = "https://files.pythonhosted.org/packages/66/8a/94dec42ae6b5c8bdc53a86867924fa22634516434f129dca187ccc0853b8/lru_dict-1.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2b569c7813adb753b7b631097c34e6dbc194cb1814f22299c2d2a94894779877", size = 11733, upload-time = "2025-11-02T10:01:39.739Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/0b6de1db804cf094e201c5541d58e6a96359eb5beed048fa64d0589b6520/lru_dict-1.4.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:33cf1eb368d3989b8f00945937cfbfc2095d8ad2b1d2274ce1bde0af6f6d1e66", size = 32251, upload-time = "2025-11-02T10:01:40.421Z" }, + { url = "https://files.pythonhosted.org/packages/97/38/89d9425dde436b9bd894234171988289b259aeeab5965bd2c21d5104cb41/lru_dict-1.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22d5879ec5d5955f9dde105997bdf7ec9e0522bf99612a80b55b09f356a08368", size = 33405, upload-time = "2025-11-02T10:01:41.917Z" }, + { url = "https://files.pythonhosted.org/packages/0e/24/f1a189399ee107a64f955c9d6c84d3b0aee9b64b31fc5684b1eaeb3a6fc0/lru_dict-1.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2084363e4488aa5b4f8b26bd3cc148d70a15be92e3d347621a5b830b2b1e0a82", size = 35135, upload-time = "2025-11-02T10:01:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/f8/57/58e9dcf0853d639e2995e5d9f84649ff8d6792a04a418628672a130137f4/lru_dict-1.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8198ab8ad7cc81b86340243ddd5cca882ead87daed0c9fa6cce377a10a7f2e47", size = 32620, upload-time = "2025-11-02T10:01:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/a9/bb/664922f0cf076b1e3c2e43e8258582d507b07c19bd441a72dd5547a483e9/lru_dict-1.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1f4ae6967d5873e684ce8b986e2e43985d0a1be735b09584737ad5634ff48f3", size = 34077, upload-time = "2025-11-02T10:01:44.694Z" }, + { url = "https://files.pythonhosted.org/packages/58/38/b7a6fa85b150232cada26a50c89dc4bcf9acd6ada00e987b074c3b4e57f2/lru_dict-1.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a9bb130b5eaddd6453ca3dc38ce4a75f743512ad135b6f3994999dde0680bd79", size = 31905, upload-time = "2025-11-02T10:01:45.668Z" }, + { url = "https://files.pythonhosted.org/packages/86/7d/9c86393946d621f4aec852d543df4023241d85106e9e1e2a0e4057861f71/lru_dict-1.4.1-cp314-cp314-win32.whl", hash = "sha256:5534c69a52add5757714456d08ce3831d36b86c98972394ba900493bb0bd97f8", size = 13435, upload-time = "2025-11-02T10:01:46.397Z" }, + { url = "https://files.pythonhosted.org/packages/20/3f/b017cbeea55a8a1d18037840a8a9c9cdae29554e9985b55d4e8694305035/lru_dict-1.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:96fd677b6d912229f2d02ba61a5a1210176963c4770c1bb765b8da937cec3834", size = 14423, upload-time = "2025-11-02T10:01:47.473Z" }, + { url = "https://files.pythonhosted.org/packages/aa/73/13132af7a5155edde66979b53eb509465304e6e5a2b00769246448479c73/lru_dict-1.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6699bfebbf11dd9ff1387be7996fac6d1009fe6a6f48091ef6e069e6f19c7bce", size = 17184, upload-time = "2025-11-02T10:01:48.161Z" }, + { url = "https://files.pythonhosted.org/packages/ef/82/094985beb3e49461bf65a3c40df8de2018b8484e4ef129295090460ca5d9/lru_dict-1.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a276f8f6f43861c3f05986824741d00e3133a973c3396598375310129535382d", size = 11459, upload-time = "2025-11-02T10:01:48.823Z" }, + { url = "https://files.pythonhosted.org/packages/14/29/836abc49f8c2b6c2efccd2ac2b2c0ad3e55b7d75a05a20cc061f17871e39/lru_dict-1.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:090c7b6a3d54fa7f3d69ba4802abe2f33c9583b16b33f52bcb521c701f7ea46c", size = 11938, upload-time = "2025-11-02T10:01:49.448Z" }, + { url = "https://files.pythonhosted.org/packages/c2/83/1bb4e8fbc0b753fea825564d9d96180813a71715d46a9b6bb30a6dea4ce0/lru_dict-1.4.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b21d06dec64fb1952385262d9fcefaec147921dc0b55210007091a79da440d93", size = 36389, upload-time = "2025-11-02T10:01:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/54/f2/7df3b6d0dbc66f3be9aa6261750967cdc5619c89a563420c52200d2dd547/lru_dict-1.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9613908a38cf8aa47f6c138ba031a8ac4ed38460299e84a2b07dba7b3b45aae", size = 38706, upload-time = "2025-11-02T10:01:51.197Z" }, + { url = "https://files.pythonhosted.org/packages/97/5f/e3ba3eeb9b864a09b92e24fbf179aef4ef48588e763a9d8d2bc10bd2c6f8/lru_dict-1.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7558302ce8bbfcd29f08e695e07bf7a0d799c2979636d6a6a0b4e207f840969f", size = 38892, upload-time = "2025-11-02T10:01:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e3/12e0888aab0bf3ab9ce35e9849f239994a0feff6fe49380859bf57124a17/lru_dict-1.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3910396142322fb2718546115bb2a56f50ebc9144b5140327053cca084e0d375", size = 37254, upload-time = "2025-11-02T10:01:52.587Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/06cd981718d039eb07a9c03263094aca6269721c470f765a292b24381a20/lru_dict-1.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f3f4fad5c4a9458954b275de6a6e31c67a26fbef7037c6a7354e22523a77db26", size = 37422, upload-time = "2025-11-02T10:01:53.271Z" }, + { url = "https://files.pythonhosted.org/packages/e8/82/ea88e618f39d78ff3c15b71f01a0b1a6c6ac2034ce5c6428ae41b1c30ea5/lru_dict-1.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85fc29363e2d3ba0a5f87b5e17f54b1078aea6d24c6dfc792725854b9d0f8d17", size = 35909, upload-time = "2025-11-02T10:01:54.356Z" }, + { url = "https://files.pythonhosted.org/packages/89/36/1dd91c602f623839cec24d6c77fa3fd1a8878bf2d716871197cd3bf084dc/lru_dict-1.4.1-cp314-cp314t-win32.whl", hash = "sha256:b3853518dfa50f28af0d6e2dcf8bb8b0a1687c5f4eb913c0b35b0da5c6d276ce", size = 13816, upload-time = "2025-11-02T10:01:55.417Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/113410f7b2e61e9d6f13f1f17c584dbd08b5796e65d772ecd5b063fab3af/lru_dict-1.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ff3af42922205620fdc920dcdf580c4c16b32c84a537a03b04b523e5c641a8a9", size = 15204, upload-time = "2025-11-02T10:01:56.06Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mashumaro" +version = "3.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/e3/a06dcd2e6df094c5e294721926f1f76da30f50823e2ac233a9198e804891/mashumaro-3.22.tar.gz", hash = "sha256:64538cc365204402a060ebde683a86505b5a4344acf6870d79021e9fbfe57360", size = 197845, upload-time = "2026-05-26T14:39:21.859Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/1c/92fd926c2e7763535454683250dbdd8d10aa2f2c62f58d6abbcce4d8b3fc/mashumaro-3.22-py3-none-any.whl", hash = "sha256:17dc4d7294c33ef380a8b929dda0608577aa2141988c00a0c4932310108fe71d", size = 95916, upload-time = "2026-05-26T14:39:20.233Z" }, +] + [[package]] name = "multidict" version = "6.7.1" @@ -924,6 +2749,92 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] +[[package]] +name = "orjson" +version = "3.10.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/03/821c8197d0515e46ea19439f5c5d5fd9a9889f76800613cfac947b5d7845/orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3", size = 5056450, upload-time = "2024-08-09T00:18:49.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/7c/b4ecc2069210489696a36e42862ccccef7e49e1454a3422030ef52881b01/orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f", size = 251409, upload-time = "2024-08-09T00:18:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/60/84/e495edb919ef0c98d054a9b6d05f2700fdeba3886edd58f1c4dfb25d514a/orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3", size = 147913, upload-time = "2024-08-09T00:18:03.245Z" }, + { url = "https://files.pythonhosted.org/packages/c5/27/e40bc7d79c4afb7e9264f22320c285d06d2c9574c9c682ba0f1be3012833/orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93", size = 147390, upload-time = "2024-08-09T00:18:04.959Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/fd646fb1a461de4958a6eacf4ecf064b8d5479c023e0e71cc89b28fa91ac/orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313", size = 152973, upload-time = "2024-08-09T00:18:07.019Z" }, + { url = "https://files.pythonhosted.org/packages/b1/00/414f8d4bc5ec3447e27b5c26b4e996e4ef08594d599e79b3648f64da060c/orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864", size = 164039, upload-time = "2024-08-09T00:18:08.428Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6b/34e6904ac99df811a06e42d8461d47b6e0c9b86e2fe7ee84934df6e35f0d/orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09", size = 142035, upload-time = "2024-08-09T03:05:37.596Z" }, + { url = "https://files.pythonhosted.org/packages/17/7e/254189d9b6df89660f65aec878d5eeaa5b1ae371bd2c458f85940445d36f/orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5", size = 169941, upload-time = "2024-08-09T00:18:10.271Z" }, + { url = "https://files.pythonhosted.org/packages/02/1a/d11805670c29d3a1b29fc4bd048dc90b094784779690592efe8c9f71249a/orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b", size = 167994, upload-time = "2024-08-09T00:18:12.337Z" }, + { url = "https://files.pythonhosted.org/packages/20/5f/03d89b007f9d6733dc11bc35d64812101c85d6c4e9c53af9fa7e7689cb11/orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb", size = 143130, upload-time = "2024-08-08T23:44:31.545Z" }, + { url = "https://files.pythonhosted.org/packages/c6/9d/9b9fb6c60b8a0e04031ba85414915e19ecea484ebb625402d968ea45b8d5/orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1", size = 137326, upload-time = "2024-08-08T23:41:30.505Z" }, + { url = "https://files.pythonhosted.org/packages/15/05/121af8a87513c56745d01ad7cf215c30d08356da9ad882ebe2ba890824cd/orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149", size = 251331, upload-time = "2024-08-09T00:18:14.967Z" }, + { url = "https://files.pythonhosted.org/packages/73/7f/8d6ccd64a6f8bdbfe6c9be7c58aeb8094aa52a01fbbb2cda42ff7e312bd7/orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe", size = 142012, upload-time = "2024-08-09T03:05:39.838Z" }, + { url = "https://files.pythonhosted.org/packages/04/65/f2a03fd1d4f0308f01d372e004c049f7eb9bc5676763a15f20f383fa9c01/orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c", size = 169920, upload-time = "2024-08-09T00:18:17.058Z" }, + { url = "https://files.pythonhosted.org/packages/e2/1c/3ef8d83d7c6a619ad3d69a4d5318591b4ce5862e6eda7c26bbe8208652ca/orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad", size = 167916, upload-time = "2024-08-09T00:18:18.992Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/820a640e5a7dfbe525e789c70871ebb82aff73b0c7bf80082653f86b9431/orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2", size = 143089, upload-time = "2024-08-08T23:41:48.588Z" }, + { url = "https://files.pythonhosted.org/packages/1a/72/a424db9116c7cad2950a8f9e4aeb655a7b57de988eb015acd0fcd1b4609b/orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024", size = 137081, upload-time = "2024-08-08T23:40:44.472Z" }, +] + +[[package]] +name = "orjson" +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -942,6 +2853,123 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] +[[package]] +name = "pillow" +version = "10.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload-time = "2024-07-01T09:46:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload-time = "2024-07-01T09:46:19.169Z" }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload-time = "2024-07-01T09:46:21.883Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload-time = "2024-07-01T09:46:24.321Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload-time = "2024-07-01T09:46:26.825Z" }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload-time = "2024-07-01T09:46:29.355Z" }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload-time = "2024-07-01T09:46:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload-time = "2024-07-01T09:46:33.73Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload-time = "2024-07-01T09:46:36.587Z" }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload-time = "2024-07-01T09:46:38.777Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload-time = "2024-07-01T09:46:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685, upload-time = "2024-07-01T09:46:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883, upload-time = "2024-07-01T09:46:47.331Z" }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837, upload-time = "2024-07-01T09:46:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562, upload-time = "2024-07-01T09:46:51.811Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761, upload-time = "2024-07-01T09:46:53.961Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767, upload-time = "2024-07-01T09:46:56.664Z" }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989, upload-time = "2024-07-01T09:46:58.977Z" }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255, upload-time = "2024-07-01T09:47:01.189Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603, upload-time = "2024-07-01T09:47:03.918Z" }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972, upload-time = "2024-07-01T09:47:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375, upload-time = "2024-07-01T09:47:09.065Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "pip" +version = "26.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/91/47e7d486260f618783899587af63ccf7980fb60245c3e63dd4571c6b57ad/pip-26.1.2.tar.gz", hash = "sha256:f49cd134c61cf2fd75e0ce2676db03e4054504a5a4986d00f8299ae632dc4605", size = 1840799, upload-time = "2026-05-31T17:33:58.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/95/6b5cb3461ea5673ba0995989746db58eb18b91b54dbf331e72f569540946/pip-26.1.2-py3-none-any.whl", hash = "sha256:382ff9f685ee3bc25864f820aa50505825f10f5458ffff07e30a6d96e5715cab", size = 1813144, upload-time = "2026-05-31T17:33:56.772Z" }, +] + [[package]] name = "platformdirs" version = "4.10.0" @@ -968,7 +2996,8 @@ dependencies = [ { name = "cfgv" }, { name = "identify" }, { name = "nodeenv" }, - { name = "pyyaml" }, + { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "virtualenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } @@ -1070,6 +3099,122 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "psutil-home-assistant" +version = "0.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "psutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/4f/32a51f53d645044740d0513a6a029d782b35bdc51a55ea171ce85034f5b7/psutil-home-assistant-0.0.1.tar.gz", hash = "sha256:ebe4f3a98d76d93a3140da2823e9ef59ca50a59761fdc453b30b4407c4c1bdb8", size = 6045, upload-time = "2022-08-25T14:28:39.926Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/48/8a0acb683d1fee78b966b15e78143b673154abb921061515254fb573aacd/psutil_home_assistant-0.0.1-py3-none-any.whl", hash = "sha256:35a782e93e23db845fc4a57b05df9c52c2d5c24f5b233bd63b01bae4efae3c41", size = 6300, upload-time = "2022-08-25T14:28:38.083Z" }, +] + +[[package]] +name = "pycares" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/a0/9c823651872e6a0face3f0311de2a40c8bbcb9c8dcb15680bd019ac56ac7/pycares-5.0.1.tar.gz", hash = "sha256:5a3c249c830432631439815f9a818463416f2a8cbdb1e988e78757de9ae75081", size = 652222, upload-time = "2026-01-01T12:37:00.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/67/e84ba11d3fec3bf1322c3b302c4df13c85e0a1bc48f16d65cd0f59ad9853/pycares-5.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ee551be4f3f3ac814ac8547586c464c9035e914f5122a534d25de147fa745e1", size = 136241, upload-time = "2026-01-01T12:35:25.439Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ae/50fbb3b4e52b9f1d16a36ffabd051ef8b2106b3f0a0d1c1113904d187a9d/pycares-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:252d4e5a52a68f825eaa90e16b595f9baee22c760f51e286ab612c6829b96de3", size = 131069, upload-time = "2026-01-01T12:35:26.293Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ea/f431599f1ac42149ea4768e516db7cdae3a503a6646319ae63ab66da1486/pycares-5.0.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c1aa549b8c2f2e224215c793d660270778dcba9abc3b85abbc7c41eabe4f1e5", size = 221120, upload-time = "2026-01-01T12:35:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4f/0a7a6c8b3a64ee5149e935c167cd8ba5d1fdd766ec03e273dbc7502f7bea/pycares-5.0.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:db7c9c9f16e8311998667a7488e817f8cbeedec2447bac827c71804663f1437e", size = 252228, upload-time = "2026-01-01T12:35:28.443Z" }, + { url = "https://files.pythonhosted.org/packages/49/3d/7f9fd20e97ee30c4b959f87ab26e47ddcef666e5e7717e45f2245fe9d70a/pycares-5.0.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9c4c8bb69bab863f677fa166653bb872bfa5d5a742f1f30bebc2d53b6e71db", size = 239473, upload-time = "2026-01-01T12:35:29.794Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d0/c67967a10abd89529cb9aded9d73f43e5de00cf21243638ef529f6757262/pycares-5.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09ef90da8da3026fcba4ed223bd71e8057608d5b3fec4f5990b52ae1e8c855cc", size = 223831, upload-time = "2026-01-01T12:35:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/4f/9a/94aacaf22a20b7d342c8f18bf006be57967beef6319adc668d4d86b627be/pycares-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ce193ebd54f4c74538b751ebb0923a9208c234ff180589d4d3cec134c001840e", size = 223963, upload-time = "2026-01-01T12:35:31.691Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e1/3666aab6fc5e7d0c669b981fe0407e6a4b67e4e6a37ac429d440274663d5/pycares-5.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:36b9ff18ef231277f99a846feade50b417187a96f742689a9d08b9594e386de4", size = 251813, upload-time = "2026-01-01T12:35:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/ddab5fbc16ad0084a827167ae8628f54c7a55ce6b743585e6f47a5dd527e/pycares-5.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5e40ea4a0ef0c01a02ef7f7390a58c62d237d5ad48d36bc3245e9c2ac181cc22", size = 238181, upload-time = "2026-01-01T12:35:34.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/27/05467933e0e5c4e712302a2d7499797bc3029bf4d0d8ffbfe737254482b7/pycares-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f323b0ddfd2c7896af6fba4f8851d34d3d13387566aa573d93330fb01cb1038", size = 223552, upload-time = "2026-01-01T12:35:35.076Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e2/14f3837e943d46ee12441fe6aaa418fdb2f698d42e179f368eaa9829744b/pycares-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bdc6bcafb72a97b3cdd529fc87210e59e67feb647a7e138110656023599b84da", size = 117478, upload-time = "2026-01-01T12:35:36.133Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c3/3284061f18188d5085338e1f1fd4f03d9c135657acf16f8020b9dd3be5fc/pycares-5.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:f8ef4c70c1edaf022875a8f9ff6c0c064f82831225acc91aa1b4f4d389e2e03a", size = 108889, upload-time = "2026-01-01T12:35:37.135Z" }, + { url = "https://files.pythonhosted.org/packages/92/0a/6bd9bdc2d0ee23ff3aabab7747212e2c5323a081b9b745624d62df88f7e9/pycares-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d1b2c6b152c65f14d0e12d741fabb78a487f0f0d22773eede8d8cfc97af612b", size = 136242, upload-time = "2026-01-01T12:35:38.372Z" }, + { url = "https://files.pythonhosted.org/packages/18/2a/2e9f888fc076cfe7a3493a3c4113e787cc4b4533f531dfb562ac9b04898f/pycares-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8c8ffcc9a48cfc296fe1aefc07d2c8e29a7f97e4bb366ce17effea6a38825f70", size = 131070, upload-time = "2026-01-01T12:35:39.262Z" }, + { url = "https://files.pythonhosted.org/packages/ec/5b/83b5aaf7b6ed102f63cd768a747b6cb5d4624f2eaecd84868d103b9dbf39/pycares-5.0.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8efc38c2703e3530b823a4165a7b28d7ce0fdcf41960fb7a4ca834a0f8cfe79", size = 221137, upload-time = "2026-01-01T12:35:40.155Z" }, + { url = "https://files.pythonhosted.org/packages/33/d3/d77ab0b33fb805d02896c385176c462e3386d94457a5e508245c39f41829/pycares-5.0.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e380bf6eff42c260f829a0a14547e13375e949053a966c23ca204a13647ef265", size = 252252, upload-time = "2026-01-01T12:35:41.287Z" }, + { url = "https://files.pythonhosted.org/packages/14/32/8afbc798bce26dfcc5bc1f6bf1560d31cdd0af837ff52cbede657bf9262e/pycares-5.0.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:35dd5858ee1246bd092a212b5e85a8ef70853f7cfaf16b99569bf4af3ae4695d", size = 239447, upload-time = "2026-01-01T12:35:42.614Z" }, + { url = "https://files.pythonhosted.org/packages/61/1b/a056393fda383b2eda5dab20bd0dd034fd631bf5ae754aabb20da815bdfe/pycares-5.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c257c6e7bf310cdb5823aa9d9a28f1e370fed8c653a968d38a954a8f8e0375ce", size = 223822, upload-time = "2026-01-01T12:35:43.594Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c7/9817f0fb954ab9926f88403f2b91a3e4984a277e2b7a4563e0118e4e1ffa/pycares-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07711acb0ef75758f081fb7436acaccc91e8afd5ae34fd35d4edc44297e81f27", size = 223986, upload-time = "2026-01-01T12:35:44.893Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a9/c0ea15c871c77e8c20bcaab18f56ae83988ea4c302155d106cc6a1bd83a9/pycares-5.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:30e5db1ae85cffb031dd8bc1b37903cd74c6d37eb737643bbca3ff2cd4bc6ae2", size = 251838, upload-time = "2026-01-01T12:35:46.271Z" }, + { url = "https://files.pythonhosted.org/packages/be/a4/fe4068abfadf3e06cc22333e87e4730de3c170075572041d5545926062a3/pycares-5.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:efbe7f89425a14edbc94787042309be77cb3674415eb6079b356e1f9552ba747", size = 238238, upload-time = "2026-01-01T12:35:47.196Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/4f140518768d974af4221cfd574a30d99d40b3d5c54c479da2c1553be59e/pycares-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5de9e7ce52d638d78723c24704eb032e60b96fbb6fe90c6b3110882987251377", size = 223574, upload-time = "2026-01-01T12:35:48.191Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0a/6e4afa4a2baffd1eba6c18a90cda17681d4838d3cab5a485e471386e04dc/pycares-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e99af0a1ce015ab6cc6bd85ce158d95ed89fb3b654515f1d0989d1afcf11026", size = 117472, upload-time = "2026-01-01T12:35:50.674Z" }, + { url = "https://files.pythonhosted.org/packages/57/d0/a99f97e9aa8c8404fc899540cf30be63cda0df5150e3c0837423917c7e4c/pycares-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:2a511c9f3b11b7ce9f159c956ea1b8f2de7f419d7ca9fa24528d582cb015dbf9", size = 108889, upload-time = "2026-01-01T12:35:51.902Z" }, + { url = "https://files.pythonhosted.org/packages/38/b2/4af99ff17acb81377c971831520540d1859bf401dc85712eb4abc2e6751f/pycares-5.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e330e3561be259ad7a1b7b0ce282c872938625f76587fae7ac8d6bc5af1d0c3d", size = 136635, upload-time = "2026-01-01T12:35:53.365Z" }, + { url = "https://files.pythonhosted.org/packages/42/da/e2e1683811c427492ee0e86e8fae8d55eb5cca032220438599991fdad866/pycares-5.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82bd37fec2a3fa62add30d4a3854720f7b051386e2f18e6e8f4ee94b89b5a7b0", size = 131093, upload-time = "2026-01-01T12:35:54.28Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2a/9cf2120cafc19e5c589d5252a9ddd3108cc87e9db09938d16317807de03b/pycares-5.0.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:258c38aaa82ad1d565b4591cdb93d2c191be8e0a2c70926999c8e0b717a01f2a", size = 221096, upload-time = "2026-01-01T12:35:57.096Z" }, + { url = "https://files.pythonhosted.org/packages/2c/cc/c5fbf6377e2d6b1f1618f147ad898e5d8ae1585fc726d6301f07aeda6cac/pycares-5.0.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ccc1b2df8a09ca20eefbe20b9f7a484d376525c0fb173cfadd692320013c6bc5", size = 252330, upload-time = "2026-01-01T12:35:58.182Z" }, + { url = "https://files.pythonhosted.org/packages/3b/df/17a7c518c45bb994f76d9064d2519674e2a3950f895abbe6af123ead04ac/pycares-5.0.1-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c4dfc80cc8b43dc79e02a15486c58eead5cae0a40906d6be64e2522285b5b39", size = 239799, upload-time = "2026-01-01T12:36:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6c/d79c94809742b56b9180a9a9ec2937607db0b8eb34b8ca75d86d3114d6dd/pycares-5.0.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f498a6606247bfe896c2a4d837db711eb7b0ba23e409e16e4b23def4bada4b9d", size = 223501, upload-time = "2026-01-01T12:36:02.695Z" }, + { url = "https://files.pythonhosted.org/packages/69/08/83084b67cbce08f44fd803b88816fc80d2fe2fb3d483d5432925df44371b/pycares-5.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a7d197835cdb4b202a3b12562b32799e27bb132262d4aa1ac3ee9d440e8ec22c", size = 223708, upload-time = "2026-01-01T12:36:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/63a6e9ef356c5149b8ec72a694e02207fd8ae643895aeb78a9f0c07f1502/pycares-5.0.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f78ab823732b050d658eb735d553726663c9bccdeeee0653247533a23eb2e255", size = 251816, upload-time = "2026-01-01T12:36:05.618Z" }, + { url = "https://files.pythonhosted.org/packages/43/1c/1c85c6355cf7bc3ae86a1024d60f9cabdc12af63306a5f59370ac8718a41/pycares-5.0.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f444ab7f318e9b2c209b45496fb07bff5e7ada606e15d5253a162964aa078527", size = 238259, upload-time = "2026-01-01T12:36:07.609Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7f/bd5ff5a460e50433f993560e4e5d229559a8bf271dbdf6be832faf1973b5/pycares-5.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9de80997de7538619b7dd28ec4371e5172e3f9480e4fc648726d3d5ba661ca05", size = 223732, upload-time = "2026-01-01T12:36:09.893Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/e77738366e00dc0918bbeb0c8fc63579e5d9cec748a2b838e207e548b5d9/pycares-5.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:206ce9f3cb9d51f5065c81b23c22996230fbc2cf58ae22834c623631b2b473aa", size = 120847, upload-time = "2026-01-01T12:36:11.494Z" }, + { url = "https://files.pythonhosted.org/packages/81/17/758e9af7ee8589ac6deddf7ea56d75b982f155bc2052ef61c45d5f371389/pycares-5.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:45fb3b07231120e8cb5b75be7f15f16115003e9251991dc37a3e5c63733d63b5", size = 112595, upload-time = "2026-01-01T12:36:12.973Z" }, + { url = "https://files.pythonhosted.org/packages/56/12/4f1d418fed957fc96089c69d9ec82314b3b91c48c7f9463385842acad9c4/pycares-5.0.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:602f3eac4b880a2527d21f52b2319cb10fde9225d103d338c4d0b2b07f136849", size = 137061, upload-time = "2026-01-01T12:36:15.027Z" }, + { url = "https://files.pythonhosted.org/packages/29/8c/559cea98a8a5d0f38b50b4b812a07fdbcdb1a961bed9e2e9d5d343e53c6f/pycares-5.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1c3736deef003f0c57bc4e7f94d54270d0824350a8f5ceaba3a20b2ce8fb427", size = 131551, upload-time = "2026-01-01T12:36:16.74Z" }, + { url = "https://files.pythonhosted.org/packages/34/cd/aee5d8070888d7be509d4f32a348e2821309ec67980498e5a974cd9e4990/pycares-5.0.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e63328df86d37150ce697fb5d9313d1d468dd4dddee1d09342cb2ed241ce6ad9", size = 230409, upload-time = "2026-01-01T12:36:18.909Z" }, + { url = "https://files.pythonhosted.org/packages/5e/94/15d5cf7d8e7af4b4ce3e19ea117dfe565c08d60d82f043ad23843703a135/pycares-5.0.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57f6fd696213329d9a69b9664a68b1ff2a71ccbdc1fc928a42c9a92858c1ec5d", size = 261297, upload-time = "2026-01-01T12:36:20.771Z" }, + { url = "https://files.pythonhosted.org/packages/af/46/24f6ddc7a37ec6eaa1c38f617f39624211d8e7cdca49b644bfc5f467f275/pycares-5.0.1-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d0878edabfbecb48a29e8769284003d8dbc05936122fe361849cd5fa52722e0", size = 248071, upload-time = "2026-01-01T12:36:22.925Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/7eb7fe44f0db55b9083725ab7a084874c2dc02806d9613e07e719838c2ab/pycares-5.0.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50e21f27a91be122e066ddd78c2d0d2769e547561481d8342a9d652a345b89f7", size = 232073, upload-time = "2026-01-01T12:36:25.773Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cd/993b17e0c049a56b5af4df3fd053acc57b37e17e0dcd709b2d337c22d57d/pycares-5.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:97ceda969f5a5d5c6b15558b658c29e4301b3a2c4615523797b5f9d4ac74772e", size = 232815, upload-time = "2026-01-01T12:36:27.798Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ff/170177bcc5dff31e735f209f5de63362f513ac18846c83d50e4e68f57866/pycares-5.0.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4d1713e602ab09882c3e65499b2cc763bff0371117327cad704cf524268c2604", size = 261111, upload-time = "2026-01-01T12:36:29.94Z" }, + { url = "https://files.pythonhosted.org/packages/4d/4a/4c6497b8ca9279b4038ee8c7e2c49504008d594d06a044e00678b30c10fe/pycares-5.0.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:954a379055d6c66b2e878b52235b382168d1a3230793ff44454019394aecac5e", size = 246311, upload-time = "2026-01-01T12:36:31.352Z" }, + { url = "https://files.pythonhosted.org/packages/06/19/1603f51f0d73bf34017a9e6967540c2bc138f9541aa7cc1ef38990b3ce9d/pycares-5.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:145d8a20f7fd1d58a2e49b7ef4309ec9bdcab479ac65c2e49480e20d3f890c23", size = 232027, upload-time = "2026-01-01T12:36:34.374Z" }, + { url = "https://files.pythonhosted.org/packages/7a/de/c000a682757b84688722ac232a24a86b6f195f1f4732432ecf35d0a768a5/pycares-5.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ebc9daba03c7ff3f62616c84c6cb37517445d15df00e1754852d6006039eb4a4", size = 121267, upload-time = "2026-01-01T12:36:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c4/8bfffecd08b9b198113fcff5f0ab84bbe696f07dec46dd1ccae0e7b28c23/pycares-5.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:e0a86eff6bf9e91d5dd8876b1b82ee45704f46b1104c24291d3dea2c1fc8ebcb", size = 113043, upload-time = "2026-01-01T12:36:37.895Z" }, +] + +[[package]] +name = "pycognito" +version = "2024.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boto3" }, + { name = "envs" }, + { name = "pyjwt", version = "2.9.0", source = { registry = "https://pypi.org/simple" }, extra = ["crypto"], marker = "python_full_version < '3.14.2'" }, + { name = "pyjwt", version = "2.12.1", source = { registry = "https://pypi.org/simple" }, extra = ["crypto"], marker = "python_full_version >= '3.14.2'" }, + { name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "requests", version = "2.34.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/67/3975cf257fcc04903686ef87d39be386d894a0d8182f43d37e9cbfc9609f/pycognito-2024.5.1.tar.gz", hash = "sha256:e211c66698c2c3dc8680e95107c2b4a922f504c3f7c179c27b8ee1ab0fc23ae4", size = 31182, upload-time = "2024-05-16T10:02:28.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/7a/f38dd351f47596b22ddbde1b8906e7f43d14be391dcdbd0c2daba886f26c/pycognito-2024.5.1-py3-none-any.whl", hash = "sha256:c821895dc62b7aea410fdccae4f96d8be7cab374182339f50a03de0fcb93f9ea", size = 26607, upload-time = "2024-05-16T10:02:27.3Z" }, +] + [[package]] name = "pycparser" version = "3.0" @@ -1088,6 +3233,182 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pyjwt" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825, upload-time = "2024-08-01T15:01:08.445Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344, upload-time = "2024-08-01T15:01:06.481Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] + +[[package]] +name = "pynacl" +version = "1.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "python_full_version >= '3.14.2' and platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", size = 390064, upload-time = "2026-01-01T17:31:57.264Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", size = 809370, upload-time = "2026-01-01T17:31:59.198Z" }, + { url = "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", size = 1408304, upload-time = "2026-01-01T17:32:01.162Z" }, + { url = "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574", size = 844871, upload-time = "2026-01-01T17:32:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", size = 1446356, upload-time = "2026-01-01T17:32:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", size = 826814, upload-time = "2026-01-01T17:32:06.078Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", size = 1411742, upload-time = "2026-01-01T17:32:07.651Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", size = 801714, upload-time = "2026-01-01T17:32:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", size = 1372257, upload-time = "2026-01-01T17:32:11.026Z" }, + { url = "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", size = 231319, upload-time = "2026-01-01T17:32:12.46Z" }, + { url = "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", size = 244044, upload-time = "2026-01-01T17:32:13.781Z" }, + { url = "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", size = 188740, upload-time = "2026-01-01T17:32:15.083Z" }, + { url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" }, + { url = "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", size = 835085, upload-time = "2026-01-01T17:32:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", size = 1437614, upload-time = "2026-01-01T17:32:23.766Z" }, + { url = "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", size = 818251, upload-time = "2026-01-01T17:32:25.69Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", size = 1402859, upload-time = "2026-01-01T17:32:27.215Z" }, + { url = "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", size = 791926, upload-time = "2026-01-01T17:32:29.314Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", size = 1363101, upload-time = "2026-01-01T17:32:31.263Z" }, + { url = "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", size = 227421, upload-time = "2026-01-01T17:32:33.076Z" }, + { url = "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", size = 239754, upload-time = "2026-01-01T17:32:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", size = 184801, upload-time = "2026-01-01T17:32:36.309Z" }, +] + +[[package]] +name = "pyobjc-core" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/e8/a6cc12669211e7c9b29e8f26bf2159e67c7a73555dc229018abf46d8167a/pyobjc_core-12.2.tar.gz", hash = "sha256:51d7de4cfa32f508c6a7aac31f131b12d5e196a8dcf588e6e8d7e6337224f66d", size = 1062064, upload-time = "2026-05-30T12:29:55.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/be/4771f4fd786f0e1a2bd6d8931a72a5f3929b7bb1b28a1fe6ca8a08371c55/pyobjc_core-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7677ed758a367bbbb5589d6f5276fb360a45c89168276c26162f61840b0fa03d", size = 6421145, upload-time = "2026-05-30T10:17:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ed/6e62d038992bc7ef9091d95ec97c3c221686fe52a993a6501e961c757613/pyobjc_core-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9287c7c46d6ae8676b4c6c0389a8f4b5381f42ae53a47151900c08b157e5a992", size = 6428611, upload-time = "2026-05-30T10:21:33.83Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0b/d492110202f4d1050a5e590620ebd1e730cf89f9880a26cf18205e0f5800/pyobjc_core-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:515ecf2afe168301feb66a7230d700584ce2e4b8a0ac178e19450b8898384139", size = 6677992, upload-time = "2026-05-30T10:44:13.039Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/ecfbd0c80e7688ed6f3db23414758443c69c3a9d318f2036e26530ede955/pyobjc_core-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a51352e478785cd7fce1604b9902125a286139caea0759cb340e59d75b594992", size = 6421372, upload-time = "2026-05-30T10:47:27.907Z" }, + { url = "https://files.pythonhosted.org/packages/2b/89/ecd5cb62573fba9a95f8bdb838a9860a360907104a0724af6611d3b20512/pyobjc_core-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:3137b2d14f9f2154fb5b1c092c38d15e164f68ab190c18335d76e4e7e1583f79", size = 6676789, upload-time = "2026-05-30T10:59:33.811Z" }, + { url = "https://files.pythonhosted.org/packages/74/24/5091d156b19df0f657127f42b08eada11c9b9cc5df49fedb91bb354d9821/pyobjc_core-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:1e4216f2ec962dc13cf7f31b9bc3a7190337a0f401e7dc9de6b2d8c08b9dbb7a", size = 6476112, upload-time = "2026-05-30T11:21:46.914Z" }, + { url = "https://files.pythonhosted.org/packages/77/e2/ee91ea8a0ad28e759f351ed8654027c34fc62ad5e207672522025a6a3fc2/pyobjc_core-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:ac952bac8057dd0b97ee7b311c39f97cad7430b7cfbd67ca0a30135a7d17d2ab", size = 6718365, upload-time = "2026-05-30T11:51:47.35Z" }, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/cc/927169225e72bab9c9b44285656768fb75052a0bc85fdbca62740e1ca43c/pyobjc_framework_cocoa-12.2.tar.gz", hash = "sha256:20b392e2b7241caad0538dfde12143343e5dfe48f72e7df660a7548e635903dc", size = 3125555, upload-time = "2026-05-30T12:35:09.273Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/66/5a91f2eddfced4f26bc2df2bcebb7f5f10c5bf5666aff6fa00ded845af07/pyobjc_framework_cocoa-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:06cb92d97d1af9d1f459ae6cf1d1a7b824c12d3aff1b709885966acd6b7208c2", size = 388093, upload-time = "2026-05-30T11:58:14.921Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3b/1af2be2bf5204bbcfc94de215d5f87d35348c9982d9b05f54ceefbc53b8f/pyobjc_framework_cocoa-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f0bbe0abedfb24b11ff6c71e26cdefb0df001c6482f95591fad40c2688c16498", size = 388154, upload-time = "2026-05-30T11:58:38.547Z" }, + { url = "https://files.pythonhosted.org/packages/41/cb/c0435d64f1199210af36141b90aea2ae3344719f7313d4160b8b0dd527db/pyobjc_framework_cocoa-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46b6681e2b21b099ed095339c140f2c8137d6ac5658653166ee90722f9e3c621", size = 392245, upload-time = "2026-05-30T11:59:02.436Z" }, + { url = "https://files.pythonhosted.org/packages/56/4b/df8e359e5e422e8f1430bde038aa64364e8c1d4542d7f6fcc4f8a97ec0b7/pyobjc_framework_cocoa-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:aecfd44908fa12a9291fb6ca2458ebbc611102de6784f2202a35fd5ed9f56c60", size = 388334, upload-time = "2026-05-30T11:59:25.861Z" }, + { url = "https://files.pythonhosted.org/packages/43/a2/68c0702cc9d6dbc7077edbd13ccc9aa30ac589d514f51ad6f5c3840e3bf1/pyobjc_framework_cocoa-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ef679740f541c52118149b558b757d1f11d9dcf30c2a23344b13a6af6a99a1ab", size = 392376, upload-time = "2026-05-30T11:59:50.031Z" }, + { url = "https://files.pythonhosted.org/packages/83/c3/e170672302e75cc1aa833546fb0d5a3bd4a126ede4124566d5a2e4a50cd6/pyobjc_framework_cocoa-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:290e544a8c2d0786a34a359d825eaad44ebaaa3b30cdd765b2755422ca39a0d2", size = 388566, upload-time = "2026-05-30T12:00:13.606Z" }, + { url = "https://files.pythonhosted.org/packages/f7/6b/78e98e4de11646e56cf98066f9f84b43c86adc8b273a660d85a6ceba7a31/pyobjc_framework_cocoa-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:5a0bed77b0b56074cc2b4564aae3e6e9d5da5fdf93252f50b6dbced0f7fece3a", size = 392674, upload-time = "2026-05-30T12:00:37.572Z" }, +] + +[[package]] +name = "pyobjc-framework-corebluetooth" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/df/bee7ba216f9fb513710aac1701b78c97b087b37fca8ec1806f8572e0bbb3/pyobjc_framework_corebluetooth-12.2.tar.gz", hash = "sha256:8b4e5ca99953c360c391a695b0782a5328fcecafd56fdf790ad709e932feb306", size = 37552, upload-time = "2026-05-30T12:35:33.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/9a/99e61547e61b0e38cb13c67931f9ca9bbd1dc72c1ff78322c7c3af805e8b/pyobjc_framework_corebluetooth-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31da3686ea55e9f7f8fc321fe2fcdf23827acd24c91ae1798e47e1f3a8355ab7", size = 13191, upload-time = "2026-05-30T12:02:01.811Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/04f0ea541e6df3b7a328fe63ed1f508898d84910be66ae0f4d08103ad5a1/pyobjc_framework_corebluetooth-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b43a21eb9388453c867ce82bf5d475cfce4fe2ceaea974474071f1cfd178f40f", size = 13205, upload-time = "2026-05-30T12:02:03.831Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b7/502b1e5ff1feede7fbed1f28e1fd14604cf564e4a4f5d4f33c93a4706739/pyobjc_framework_corebluetooth-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:162623f74c85041bb8a2c9a8862cee6674531d603ad7a8d65a3efc81619f84c5", size = 13389, upload-time = "2026-05-30T12:02:05.647Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ef/a75bd6c17e012f7d73a8a2d94e324528a82dc06451b6019d910b1a993864/pyobjc_framework_corebluetooth-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8a3b35a1253de626d5efe705dda36a4f1fb8753e48aafdc4dbf99dc9c9526d86", size = 13196, upload-time = "2026-05-30T12:02:07.462Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d7/11a8a90463e6a3928274e65bd17acaa3f0bfbd9c19a68b93c61cbc4fa379/pyobjc_framework_corebluetooth-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:bd0b9e7bc2fffcfa4843d7f103e1e927ce37817a70932d080378e1e29a073381", size = 13387, upload-time = "2026-05-30T12:02:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/10/d0/d5cc4ba6dbe21f6f4c90e8f0087254f1f742134580394b763025416ab7d2/pyobjc_framework_corebluetooth-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:a2dd4bd8fde13b97a3339a42d6703c9c9b148663e2e23a184ffe2877c8e51470", size = 13192, upload-time = "2026-05-30T12:02:11.41Z" }, + { url = "https://files.pythonhosted.org/packages/25/a0/964b18affc8f6b582ed891f3b59413452fd95acce4adbf070a389b5197e4/pyobjc_framework_corebluetooth-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:42db087cd3a328abc620c76c7f708d7c2b451f3dbc0779983812a0c84320fe60", size = 13394, upload-time = "2026-05-30T12:02:13.443Z" }, +] + +[[package]] +name = "pyobjc-framework-libdispatch" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/fe/e23be301e46c30450955cdb096f16f6a86e7609787a4b8225ec24d6fdc9d/pyobjc_framework_libdispatch-12.2.tar.gz", hash = "sha256:4a41879ef7716b73d70f2e40ff39353d686cbc59d48c93217ed362d2b2baf1ba", size = 40345, upload-time = "2026-05-30T12:39:09.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/9e/94b587d8c5b243cb6026489752093a45d75fff08c85ed5b8b5548ad3596a/pyobjc_framework_libdispatch-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9fb0028b400661b6b2279e2b611d396a4217bc36aa9dadd3ae21419551f5e091", size = 15634, upload-time = "2026-05-30T12:12:01.08Z" }, + { url = "https://files.pythonhosted.org/packages/83/ac/f5e01c960287e3484f71a737e7ffd75ca1c192200de70fa342a22181895f/pyobjc_framework_libdispatch-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:265fe4edfd60de111c870932ab8397f845c314332a935aa7a27100fd8562cebc", size = 15651, upload-time = "2026-05-30T12:12:03.156Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d9/8bcb4e234aee09396d06515ed80fb1f79cee3aeb67a311aa8e81a085584b/pyobjc_framework_libdispatch-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:117ed20ca54e334f657e60b9b9a3de3741d91875fd50ee51ac6a09224912cc68", size = 15918, upload-time = "2026-05-30T12:12:05.257Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0e/caee3281628ba4913206167062274334a62ba5dedb8dada5fd884a53584f/pyobjc_framework_libdispatch-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fef3158dd4a068a9db8108a5cbf7da385788136e1d8af2e6fb7f82215016ecb2", size = 15679, upload-time = "2026-05-30T12:12:07.217Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e0/ecf5fb9f11d12271818c5244c57d36ee654abfa2d15799bd594d4f58d931/pyobjc_framework_libdispatch-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:02dc66f1c11e25eceecf3c1b2b397f1727659dcac3e0c7b97784d5af3cfab491", size = 15960, upload-time = "2026-05-30T12:12:09.199Z" }, + { url = "https://files.pythonhosted.org/packages/b0/74/c1d1d1fc45434dcbab16887273d368d9ad89e65939babcffd502ed308ef9/pyobjc_framework_libdispatch-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:a81d9329fdf5094c9bc09b916e1d2fb86ab27138161d3e7ddb76ec2e780a1aba", size = 15699, upload-time = "2026-05-30T12:12:11.103Z" }, + { url = "https://files.pythonhosted.org/packages/ce/46/6ba13e10cbac853af00ee37444f37826a0688e32d1d22ce2fde4e2add9d3/pyobjc_framework_libdispatch-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:8daaf8844b5b328cf8adb6e8c085bf2aad22237956f7c5dc6fbb7a46c5015af0", size = 15990, upload-time = "2026-05-30T12:12:13.077Z" }, +] + +[[package]] +name = "pyopenssl" +version = "24.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/70/ff56a63248562e77c0c8ee4aefc3224258f1856977e0c1472672b62dadb8/pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", size = 184323, upload-time = "2024-07-20T17:26:31.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/dd/e0aa7ebef5168c75b772eda64978c597a9129b46be17779054652a7999e4/pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d", size = 58390, upload-time = "2024-07-20T17:26:29.057Z" }, +] + +[[package]] +name = "pyopenssl" +version = "26.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/51/27a5ad5f939d08f690a326ef9582cda7140555180db71695f6fb747d6a36/pyopenssl-26.2.0.tar.gz", hash = "sha256:8c6fcecd1183a7fc897548dfe388b0cdb7f37e018200d8409cf33959dbe35387", size = 182195, upload-time = "2026-05-04T23:06:09.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" }, +] + [[package]] name = "pyreadline3" version = "3.5.6" @@ -1097,6 +3418,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/5e/35c856e186b74678c24927847ad9895a51f1bc02a0c6126477a6c6040064/pyreadline3-3.5.6-py3-none-any.whl", hash = "sha256:8449b734232e42a5dcd74048e39b60db2839a4c38cf3ae2bf7707d58b5389c0d", size = 85243, upload-time = "2026-05-14T17:55:03.262Z" }, ] +[[package]] +name = "pyrfc3339" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/7f/3c194647ecb80ada6937c38a162ab3edba85a8b6a58fa2919405f4de2509/pyrfc3339-2.1.0.tar.gz", hash = "sha256:c569a9714faf115cdb20b51e830e798c1f4de8dabb07f6ff25d221b5d09d8d7f", size = 12589, upload-time = "2025-08-23T16:40:31.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/90/0200184d2124484f918054751ef997ed6409cb05b7e8dcbf5a22da4c4748/pyrfc3339-2.1.0-py3-none-any.whl", hash = "sha256:560f3f972e339f579513fe1396974352fd575ef27caff160a38b312252fcddf3", size = 6758, upload-time = "2025-08-23T16:40:30.49Z" }, +] + +[[package]] +name = "pyric" +version = "0.1.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/64/a99f27d3b4347486c7bfc0aa516016c46dc4c0f380ffccbd742a61af1eda/PyRIC-0.1.6.3.tar.gz", hash = "sha256:b539b01cafebd2406c00097f94525ea0f8ecd1dd92f7731f43eac0ef16c2ccc9", size = 870401, upload-time = "2016-12-04T07:54:48.374Z" } + [[package]] name = "pytest" version = "8.4.2" @@ -1226,6 +3562,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" }, ] +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + [[package]] name = "pyusb" version = "1.3.1" @@ -1251,10 +3596,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, ] +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, @@ -1302,7 +3682,8 @@ name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "rpds-py" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] @@ -1311,6 +3692,133 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "certifi", marker = "python_full_version < '3.14.2'" }, + { name = "charset-normalizer", marker = "python_full_version < '3.14.2'" }, + { name = "idna", marker = "python_full_version < '3.14.2'" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "certifi", marker = "python_full_version >= '3.14.2'" }, + { name = "charset-normalizer", marker = "python_full_version >= '3.14.2'" }, + { name = "idna", marker = "python_full_version >= '3.14.2'" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + [[package]] name = "rpds-py" version = "0.30.0" @@ -1398,59 +3906,249 @@ version = "0.15.15" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985, upload-time = "2026-05-28T14:16:57.784Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" }, - { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" }, - { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" }, - { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" }, - { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" }, - { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" }, - { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" }, - { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" }, - { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" }, - { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" }, - { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" }, - { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" }, + { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" }, + { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" }, + { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" }, + { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" }, + { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" }, +] + +[[package]] +name = "s3transfer" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/1f/12417f7f493fc45e1f9fd5d4a9b6c125cf8d2cf3f8ddbdfab3e76406e9d6/s3transfer-0.18.0.tar.gz", hash = "sha256:3760b8b7ec1315da54048b2d626276732bee4300d054d492d4e1d43e20d4ecbd", size = 160560, upload-time = "2026-05-28T19:39:09.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/58/a58fc997655386daa2e25784e30c288aa3e3819e401f77029ee4899fb55a/s3transfer-0.18.0-py3-none-any.whl", hash = "sha256:239c13b09e65ad0346e1be7348b8a202dcad44ac7ea7c6eb858fc881dce739b6", size = 88572, upload-time = "2026-05-28T19:39:07.999Z" }, +] + +[[package]] +name = "securetar" +version = "2026.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "pynacl", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/54/c7084733ce23f33c5f863734d6fd0c1789ba9ec771257cc54529c7a999ac/securetar-2026.4.1.tar.gz", hash = "sha256:536108c7cec0bdd6fb51abd8d293deba02930124d76ec5dd4599ae5e0c755638", size = 28289, upload-time = "2026-04-07T17:02:07.231Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/56/4ef84868d1ff2f77ac080929117283ac9d18f7d018ae64fa1e7f957a8d20/securetar-2026.4.1-py3-none-any.whl", hash = "sha256:7cf66c204d678b9d1e2d17814c83048a6468e928ea00edb7e6c1efe72aab0540", size = 19339, upload-time = "2026-04-07T17:02:05.635Z" }, +] + +[[package]] +name = "sentence-stream" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/69/f3d048692aac843f41102507f6257138392ec841c16718f0618d27051caf/sentence_stream-1.3.0.tar.gz", hash = "sha256:b06261d35729de97df9002a1cc708f9a888f662b80d5d6d008ee69c51f36041b", size = 10049, upload-time = "2026-01-08T16:25:06.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/b6/48339c109bab6f54ff608800773b9425464c6cbf7fd3f2ba01294d78be3d/sentence_stream-1.3.0-py3-none-any.whl", hash = "sha256:7448d131315b85eefdf238e5edd9caa62899acf609145d5e0e10c09812eb8a1d", size = 8707, upload-time = "2026-01-08T16:25:05.918Z" }, +] + +[[package]] +name = "serialx" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/1a/9f0a4782cc08eeb6705960fc465cf3a70298774d63c3f86e22b84ddc9465/serialx-1.8.0.tar.gz", hash = "sha256:957843ec6d1fe91454e182884c29b614fb6d93209668f44197bcbb12d373129b", size = 570883, upload-time = "2026-05-13T06:43:19.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/b9/a949cf2a51e038dc55e903931a5e2177cfdbcc38a4fef0614bdd1292f94c/serialx-1.8.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:426a6586b81d1d8c80074b79852fcbc512e45dee2e677e44034cf977f6cfe7d0", size = 283010, upload-time = "2026-05-13T06:43:11.474Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d5/3fb88d6ddccf8685dd16f5bb4548857d0081a90724422950915c09069361/serialx-1.8.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b10770a949ac79583974853eaca887763b8ed1b03c249ce03401cd2c4664f084", size = 280557, upload-time = "2026-05-13T06:43:12.886Z" }, + { url = "https://files.pythonhosted.org/packages/3f/9b/6f13c8be34bc4f46189ebe572585d7e3d8481f7edd035a88bd20a838633e/serialx-1.8.0-cp310-abi3-win32.whl", hash = "sha256:c58b2dd3ed90e4a543d870afd60f0fe257888d2336b32fca7d3b32a55d4749f4", size = 182041, upload-time = "2026-05-13T06:43:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/48/e2/b5ff96e4f56a7d59dc2286f87a3e9c1ef619d4db05a10cfceb8ca38a2145/serialx-1.8.0-cp310-abi3-win_amd64.whl", hash = "sha256:174127ed4307cc81c6dfaa9f6875f3f6075156348412aa1d31e4ee34fd64caf1", size = 188050, upload-time = "2026-05-13T06:43:15.643Z" }, + { url = "https://files.pythonhosted.org/packages/de/4a/65e4de353a30fa6b5078b4bee4fb722653203e72777107ac175572d48f2f/serialx-1.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:43419e09356c2da10fce7892f5930ce989785f621d3229364c9736771ddd80dd", size = 184355, upload-time = "2026-05-13T06:43:17.143Z" }, + { url = "https://files.pythonhosted.org/packages/58/79/5292b2fd3c6e1c5bca04d04c646cc560a0d2a98590180361da1c4643fc40/serialx-1.8.0-py3-none-any.whl", hash = "sha256:650f12a33d7ddf4068c6a234c9ae1b015d92ab65b714b08def90274f555c35ab", size = 64556, upload-time = "2026-05-13T06:43:18.321Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "snitun" +version = "0.39.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "async-timeout", marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/ff/2b7499dbfea2fa748620f8181aebdff26e24b6f78026516760e73d11a319/snitun-0.39.1.tar.gz", hash = "sha256:fadbe447eea786291d5c52e67eae0658f53a1f68c4b97425e17a9579df503d7e", size = 33073, upload-time = "2024-05-13T07:21:26.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/c3/1fd863e859a91d9087613195012d96e03c09063718b53deac610b4fd86b4/snitun-0.39.1-py3-none-any.whl", hash = "sha256:6ff55f6ba21d463877f9872de7d632fc18e400c8b42f8115c42d17e51075f674", size = 39089, upload-time = "2024-05-13T07:21:24.598Z" }, +] + +[[package]] +name = "snitun" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/e2/b5bbf04971d1c3e07a3e16a706ea3c1a4b711c6d8c9566e8012772d3351a/snitun-0.45.1.tar.gz", hash = "sha256:d76d48cf4190ea59e8f63892da9c18499bfc6ca796220a463c6f3b32099d661c", size = 43335, upload-time = "2025-09-25T05:24:07.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/1b/83ff83003994bc8b56483c75a710de588896c167c7c42d66d059a2eb48dc/snitun-0.45.1-py3-none-any.whl", hash = "sha256:c1fa4536320ec3126926ade775c429e20664db1bc61d8fec0e181dc393d36ab4", size = 51236, upload-time = "2025-09-25T05:24:06.412Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.31" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" }, + { name = "typing-extensions", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/7d/e3312ae374fe7a4af7e1494735125a714a0907317c829ab8d8a31d89ded4/SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484", size = 9524110, upload-time = "2024-06-18T21:38:37.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/26/1a44b4c9286bccdc2ac344da6487227595512aa60d45c6617b1b6cd859b6/SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42", size = 2083132, upload-time = "2024-06-18T22:07:44.845Z" }, + { url = "https://files.pythonhosted.org/packages/27/97/21efc51a670e2772156890e241120aac7dae6c92d1bf56e811fb3bec3373/SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c", size = 2073619, upload-time = "2024-06-18T22:07:54.357Z" }, + { url = "https://files.pythonhosted.org/packages/ef/87/778b09abc441b6db4014efb9ef87be0c6959cbd46ee3280faad297f30c12/SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449", size = 3222863, upload-time = "2024-06-18T23:44:28.479Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/7794f3055d50e96cee04c19c7faeedacb323c7762f334660ba03bed95485/SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e", size = 3233968, upload-time = "2024-06-18T22:18:54.371Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ed/aa6ad258a9478874c4b921e8e78d04d237667fc48550a94df79e827ae91f/SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67", size = 3170897, upload-time = "2024-06-18T23:44:36.786Z" }, + { url = "https://files.pythonhosted.org/packages/7f/da/d8f8515caf3ef357ad02bb4fd38474fb72c21ee788e2995d9395bcf5185e/SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e", size = 3197274, upload-time = "2024-06-18T22:19:01.43Z" }, + { url = "https://files.pythonhosted.org/packages/99/b3/02a2aeb619f5e366b29d9f5eeeaadbe2bd383eab22ce10a00ced870c63f0/SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96", size = 2053430, upload-time = "2024-06-18T22:02:25.431Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ff/5746886a796799d45285573c8a6564b4b84c730142ab974d7a3f7bacee6c/SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a", size = 2079376, upload-time = "2024-06-18T22:02:32.276Z" }, + { url = "https://files.pythonhosted.org/packages/f3/89/ff21b6c7ccdb254fba5444d15afe193d9a71f4fa054b4823d4384d10718e/SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911", size = 1874629, upload-time = "2024-06-18T22:19:04.764Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.49" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "greenlet", marker = "(python_full_version >= '3.14.2' and platform_machine == 'AMD64') or (python_full_version >= '3.14.2' and platform_machine == 'WIN32') or (python_full_version >= '3.14.2' and platform_machine == 'aarch64') or (python_full_version >= '3.14.2' and platform_machine == 'amd64') or (python_full_version >= '3.14.2' and platform_machine == 'ppc64le') or (python_full_version >= '3.14.2' and platform_machine == 'win32') or (python_full_version >= '3.14.2' and platform_machine == 'x86_64')" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" }, + { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" }, + { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" }, + { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" }, + { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" }, + { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" }, + { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" }, + { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" }, + { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" }, + { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/bf28f618c0a9597d14e0b9ee7d1e0622faff738d44fe986ee287cdf1b8d0/sqlalchemy-2.0.49-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e", size = 2156356, upload-time = "2026-04-03T16:53:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a7/5f476227576cb8644650eff68cc35fa837d3802b997465c96b8340ced1e2/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a", size = 3276486, upload-time = "2026-04-03T17:07:46.9Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/efc7c0bf3a1c5eef81d397f6fddac855becdbb11cb38ff957888603014a7/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066", size = 3281479, upload-time = "2026-04-03T17:12:32.226Z" }, + { url = "https://files.pythonhosted.org/packages/91/68/bb406fa4257099c67bd75f3f2261b129c63204b9155de0d450b37f004698/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187", size = 3226269, upload-time = "2026-04-03T17:07:48.678Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/acb56c00cca9f251f437cb49e718e14f7687505749ea9255d7bd8158a6df/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401", size = 3248260, upload-time = "2026-04-03T17:12:34.381Z" }, + { url = "https://files.pythonhosted.org/packages/56/19/6a20ea25606d1efd7bd1862149bb2a22d1451c3f851d23d887969201633f/sqlalchemy-2.0.49-cp314-cp314-win32.whl", hash = "sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5", size = 2118463, upload-time = "2026-04-03T17:05:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4f/8297e4ed88e80baa1f5aa3c484a0ee29ef3c69c7582f206c916973b75057/sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl", hash = "sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", size = 2144204, upload-time = "2026-04-03T17:05:48.694Z" }, + { url = "https://files.pythonhosted.org/packages/1f/33/95e7216df810c706e0cd3655a778604bbd319ed4f43333127d465a46862d/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977", size = 3565474, upload-time = "2026-04-03T16:58:35.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a4/ed7b18d8ccf7f954a83af6bb73866f5bc6f5636f44c7731fbb741f72cc4f/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01", size = 3530567, upload-time = "2026-04-03T17:06:04.587Z" }, + { url = "https://files.pythonhosted.org/packages/73/a3/20faa869c7e21a827c4a2a42b41353a54b0f9f5e96df5087629c306df71e/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61", size = 3474282, upload-time = "2026-04-03T16:58:37.131Z" }, + { url = "https://files.pythonhosted.org/packages/b7/50/276b9a007aa0764304ad467eceb70b04822dc32092492ee5f322d559a4dc/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a", size = 3480406, upload-time = "2026-04-03T17:06:07.176Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c3/c80fcdb41905a2df650c2a3e0337198b6848876e63d66fe9188ef9003d24/sqlalchemy-2.0.49-cp314-cp314t-win32.whl", hash = "sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158", size = 2149151, upload-time = "2026-04-03T17:02:07.281Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/9f1a62feab6ed368aff068524ff414f26a6daebc7361861035ae00b05530/sqlalchemy-2.0.49-cp314-cp314t-win_amd64.whl", hash = "sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7", size = 2184178, upload-time = "2026-04-03T17:02:08.623Z" }, + { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" }, ] [[package]] -name = "serialx" -version = "1.8.0" +name = "standard-aifc" +version = "3.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "typing-extensions" }, + { name = "audioop-lts", marker = "python_full_version >= '3.14.2'" }, + { name = "standard-chunk", marker = "python_full_version >= '3.14.2'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/1a/9f0a4782cc08eeb6705960fc465cf3a70298774d63c3f86e22b84ddc9465/serialx-1.8.0.tar.gz", hash = "sha256:957843ec6d1fe91454e182884c29b614fb6d93209668f44197bcbb12d373129b", size = 570883, upload-time = "2026-05-13T06:43:19.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/b9/a949cf2a51e038dc55e903931a5e2177cfdbcc38a4fef0614bdd1292f94c/serialx-1.8.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:426a6586b81d1d8c80074b79852fcbc512e45dee2e677e44034cf977f6cfe7d0", size = 283010, upload-time = "2026-05-13T06:43:11.474Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d5/3fb88d6ddccf8685dd16f5bb4548857d0081a90724422950915c09069361/serialx-1.8.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b10770a949ac79583974853eaca887763b8ed1b03c249ce03401cd2c4664f084", size = 280557, upload-time = "2026-05-13T06:43:12.886Z" }, - { url = "https://files.pythonhosted.org/packages/3f/9b/6f13c8be34bc4f46189ebe572585d7e3d8481f7edd035a88bd20a838633e/serialx-1.8.0-cp310-abi3-win32.whl", hash = "sha256:c58b2dd3ed90e4a543d870afd60f0fe257888d2336b32fca7d3b32a55d4749f4", size = 182041, upload-time = "2026-05-13T06:43:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/48/e2/b5ff96e4f56a7d59dc2286f87a3e9c1ef619d4db05a10cfceb8ca38a2145/serialx-1.8.0-cp310-abi3-win_amd64.whl", hash = "sha256:174127ed4307cc81c6dfaa9f6875f3f6075156348412aa1d31e4ee34fd64caf1", size = 188050, upload-time = "2026-05-13T06:43:15.643Z" }, - { url = "https://files.pythonhosted.org/packages/de/4a/65e4de353a30fa6b5078b4bee4fb722653203e72777107ac175572d48f2f/serialx-1.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:43419e09356c2da10fce7892f5930ce989785f621d3229364c9736771ddd80dd", size = 184355, upload-time = "2026-05-13T06:43:17.143Z" }, - { url = "https://files.pythonhosted.org/packages/58/79/5292b2fd3c6e1c5bca04d04c646cc560a0d2a98590180361da1c4643fc40/serialx-1.8.0-py3-none-any.whl", hash = "sha256:650f12a33d7ddf4068c6a234c9ae1b015d92ab65b714b08def90274f555c35ab", size = 64556, upload-time = "2026-05-13T06:43:18.321Z" }, + { url = "https://files.pythonhosted.org/packages/c3/52/5fbb203394cc852334d1575cc020f6bcec768d2265355984dfd361968f36/standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66", size = 10492, upload-time = "2024-10-30T16:01:07.071Z" }, ] [[package]] -name = "setuptools" -version = "82.0.1" +name = "standard-chunk" +version = "3.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/06/ce1bb165c1f111c7d23a1ad17204d67224baa69725bb6857a264db61beaf/standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654", size = 4672, upload-time = "2024-10-30T16:18:28.326Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, + { url = "https://files.pythonhosted.org/packages/7a/90/a5c1084d87767d787a6caba615aa50dc587229646308d9420c960cb5e4c0/standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c", size = 4944, upload-time = "2024-10-30T16:18:26.694Z" }, ] [[package]] -name = "six" -version = "1.17.0" +name = "standard-telnetlib" +version = "3.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/06/7bf7c0ec16574aeb1f6602d6a7bdb020084362fb4a9b177c5465b0aae0b6/standard_telnetlib-3.13.0.tar.gz", hash = "sha256:243333696bf1659a558eb999c23add82c41ffc2f2d04a56fae13b61b536fb173", size = 12636, upload-time = "2024-10-30T16:01:42.257Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/33/85/a1808451ac0b36c61dffe8aea21e45c64ba7da28f6cb0d269171298c6281/standard_telnetlib-3.13.0-py3-none-any.whl", hash = "sha256:b268060a3220c80c7887f2ad9df91cd81e865f0c5052332b81d80ffda8677691", size = 9995, upload-time = "2024-10-30T16:01:29.289Z" }, ] [[package]] @@ -1480,6 +4178,137 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "uart-devices" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/08/a8fd6b3dd2cb92344fb4239d4e81ee121767430d7ce71f3f41282f7334e0/uart_devices-0.1.1.tar.gz", hash = "sha256:3a52c4ae0f5f7400ebe1ae5f6e2a2d40cc0b7f18a50e895236535c4e53c6ed34", size = 5167, upload-time = "2025-02-22T16:47:05.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/64/edf33c2d7fba7d6bf057c9dc4235bfc699517ea4c996240a1a9c2bf51c29/uart_devices-0.1.1-py3-none-any.whl", hash = "sha256:55bc8cce66465e90b298f0910e5c496bc7be021341c5455954cf61c6253dc123", size = 4827, upload-time = "2025-02-22T16:47:04.286Z" }, +] + +[[package]] +name = "ulid-transform" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/64/5e/d48740b5aa3f1135c3c76f4bffaa7bddb209807d6ac11ad79666d451641b/ulid_transform-1.0.2.tar.gz", hash = "sha256:9b710f6adb93a7620910bce385c7e977a234ab321443ec3bc1e48ae931f1e5d4", size = 15743, upload-time = "2024-08-24T23:23:22.514Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/61/63d342c76937a4422ec0c581bf976717e243ae0f84d53592a00a78d18aaa/ulid_transform-1.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:eb88626b68fa34883722ade34df0fd3b51f55ab6730e8bda6532a087568bbb54", size = 41179, upload-time = "2024-08-24T23:28:52.585Z" }, + { url = "https://files.pythonhosted.org/packages/74/2e/b5e0afc1f9a19eefb293c55d8c6444c9132b8c026006e2b3f1e3848ce2a9/ulid_transform-1.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b39b0188c788dac1338e3e217fc83728189f1e4a91ff75afd5152ddb7a41fe9", size = 164907, upload-time = "2024-08-24T23:28:53.845Z" }, + { url = "https://files.pythonhosted.org/packages/b7/08/17391871f8585a0d3321a96f57ce27225d1c15d8497881935f180e668ab7/ulid_transform-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc58deae5c2a3868824f3ce8e103845a600b6420fab88c4b6f1cab8a45c657f4", size = 172564, upload-time = "2024-08-24T23:28:55.301Z" }, + { url = "https://files.pythonhosted.org/packages/05/5e/2e326e349ce3da7667817e12de333a5aaa18d904d8ee75122bb17dbdf61c/ulid_transform-1.0.2-cp312-cp312-manylinux_2_36_x86_64.whl", hash = "sha256:55812ff17d265a1318c16b2af3349bdd893f0c5ca4352ea429d82ea8a7ab36ac", size = 170072, upload-time = "2024-08-24T23:23:20.946Z" }, + { url = "https://files.pythonhosted.org/packages/1e/36/736752356192f19d0b65fc1da68b616ac185a6ee960b8c9f69df4d5d8622/ulid_transform-1.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a95449d9343ee6cfae49ace10b7ed094acbfcb19c4d56a5ba06874507e1e550f", size = 1243697, upload-time = "2024-08-24T23:28:56.403Z" }, + { url = "https://files.pythonhosted.org/packages/80/78/768b512c3870d512692ec108b430debdcdaa5fa1cbb5d9411253f37747e8/ulid_transform-1.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e8b7ac6d9273bac1c1a242bbc6c3d9c3018bff8e00501293a8cf7493bff7190", size = 1155626, upload-time = "2024-08-24T23:28:57.898Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/ca24bafa285d37fef4edda165c9893cc23977ba7309cf1cbbf9347dd5de7/ulid_transform-1.0.2-cp312-cp312-win32.whl", hash = "sha256:72f719c10b3e9135a09c8d38182e4afacb2c8908ea566257c037b3a4a62f9b91", size = 38715, upload-time = "2024-08-24T23:28:59.036Z" }, + { url = "https://files.pythonhosted.org/packages/02/be/dcb545a55247636bbbf642d0a61c3db3106aab9fccd59f799630e3203478/ulid_transform-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:943aaa6c6888f93e4718bcd1cc852db5ee0b2694a1107dcf411bfa2b5e3eb3bd", size = 41041, upload-time = "2024-08-24T23:29:00.645Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/3dba6de60c854b9baa56aaff114f1a8f34327ea6bdceb7a76647dbf4b408/ulid_transform-1.0.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:76563b84d0852a2861cdd22fb082ae1f7a88f038c078be4ad716610eeea421a6", size = 40524, upload-time = "2024-08-24T23:29:01.873Z" }, + { url = "https://files.pythonhosted.org/packages/e8/48/323ed2ae0c80a875793115ae97fab331b9785b96d56064096011f31758e3/ulid_transform-1.0.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96c3c2423972cae3f26ef564c79507da49558553fb13e605dcf9395fab2924e4", size = 158855, upload-time = "2024-08-24T23:29:02.938Z" }, + { url = "https://files.pythonhosted.org/packages/be/cc/5b06e4bce8b0b6b1d4334ad83e6224684eac1744fcff0e9e4d215165af33/ulid_transform-1.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da3887088b3a3bc4a98bcd65274a0324c50dfd6d56305434ddcc25024a1ed98b", size = 166633, upload-time = "2024-08-24T23:29:04.435Z" }, + { url = "https://files.pythonhosted.org/packages/56/a0/78b2f37777ef89c335a86f6803dd68d60fd0f700aa2c30990a4cb064aa4e/ulid_transform-1.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bd972e8c1695ad3e24b5d9eaa5ad352ff9237f58186d182f4ef48a1f7d352b1d", size = 1237875, upload-time = "2024-08-24T23:29:05.876Z" }, + { url = "https://files.pythonhosted.org/packages/31/07/398dd416d45b0f7d497486c67d080bc113d1b0aefdeab85d16a12d2f852f/ulid_transform-1.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71c71a436f5e2c0a1580ad5269b1e7ad8193c1cbb69463342261e46f0f722f4d", size = 1150251, upload-time = "2024-08-24T23:29:07.016Z" }, + { url = "https://files.pythonhosted.org/packages/e0/92/47c5c4f09da84e6f73bf4b984a757a53086a8216b0c0ee4ccdb5a3f3c0e9/ulid_transform-1.0.2-cp313-cp313-win32.whl", hash = "sha256:2231ca1d83f5964a7cdf353f9d7cbc16a2e51eb8c9d5a9c743fe3aa0d17c6b3e", size = 38270, upload-time = "2024-08-24T23:29:08.192Z" }, + { url = "https://files.pythonhosted.org/packages/58/88/bdb72f143c76aabe2f5169a441f8d28973ed4e6fb598c4b3acdd77b379a0/ulid_transform-1.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:0d5bc5f3392b78ba9225dbb919b05fed7d62cff77f8674cc1389c01d3ae9a947", size = 40309, upload-time = "2024-08-24T23:29:09.395Z" }, +] + +[[package]] +name = "ulid-transform" +version = "2.2.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/69/80e64aff3bbfd719ef3519774cf489cd972aac89cf8f85cd52009757be71/ulid_transform-2.2.9.tar.gz", hash = "sha256:6a8b573de0158ab4bd0424f26ed4d1cef42f15afaa718c924a8427fe24a3838f", size = 17886, upload-time = "2026-05-25T05:39:04.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/1c/575519cd2a4dcd58741669d233375ba103a2d4787eb37a35a910962d6557/ulid_transform-2.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d8ad10720a3616e33f0758e6f276fdb48e53500eca3e310823458331b941e304", size = 25205, upload-time = "2026-05-25T05:45:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/94e5bcf4ec078fafe7b7f85b839f73162d87edb3bbc1f31846c656a02792/ulid_transform-2.2.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1898d70422647bdfea5f0c400748399b5ddefcd20f0fec030b2bae37ce0fd48c", size = 27458, upload-time = "2026-05-25T05:45:26.694Z" }, + { url = "https://files.pythonhosted.org/packages/60/7a/a5f737dc9fe0af2207a8829d0d6ce4e2b782a17eeceb4f3287d467e2b1ca/ulid_transform-2.2.9-cp312-cp312-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5a5096a219156413e64783a7ed95bea41aa1312147e35dae2de2303fd05699a5", size = 27810, upload-time = "2026-05-25T05:45:28.046Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/908e33fc18e474f23a1ba2a7364b48cab6f6aa038aebbe66d5e16f9d456a/ulid_transform-2.2.9-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5203cd64a82293bf39b5f91eefd4b25f5b2b7be72fac81085ab9b78b5aa1bcd5", size = 26402, upload-time = "2026-05-25T05:45:29.461Z" }, + { url = "https://files.pythonhosted.org/packages/5a/3f/6fbf0f44de4330cbd879d9dc93675a343e6fe8c8b0ccef24b39c6fdfaa5a/ulid_transform-2.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ff04ab5278d07d3a2354979a7b41c9d6062aef3cbf227a8e3dae05065831f9f", size = 1007527, upload-time = "2026-05-25T05:45:30.997Z" }, + { url = "https://files.pythonhosted.org/packages/91/83/ff0d5e154315ae9fbfe59b3c6f15886b42f8fd02ca5f08dce28f7da45a78/ulid_transform-2.2.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7233049801567679eb03061ca22f774440021efbde361153e3cee7a700b6e373", size = 877350, upload-time = "2026-05-25T05:45:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f2/533e4dbc8dab5643b0d8f56393271b2ba4c7e0f8f659f1a1ef23344f21b6/ulid_transform-2.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c4ba1481445317b9646459f4e824b2593eb401013fc1e726b19451a6557240fd", size = 1058958, upload-time = "2026-05-25T05:45:33.953Z" }, + { url = "https://files.pythonhosted.org/packages/53/68/59b62bb09afab8f4de5965f59cb27570e7699c029edd7854b0b6f11030fb/ulid_transform-2.2.9-cp312-cp312-win32.whl", hash = "sha256:d0afa91c91be1aef67821969cb0b4281523c11ea7a1d70b29f23e4856d84db76", size = 25370, upload-time = "2026-05-25T05:45:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/7d/6a/4415a1b3676d90c7130e839ac540b79eb8e658b43ae78d72dc66c2bde9f0/ulid_transform-2.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:ea5fc3f68cb8c200b3b076f9289d226682caa862a1d777b15ad5e87fc4fd94d7", size = 25458, upload-time = "2026-05-25T05:45:36.896Z" }, + { url = "https://files.pythonhosted.org/packages/0d/53/b8cc9aee8f9a58779fb40a33ecf250c4c6088496946e6206df1ab143957c/ulid_transform-2.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:354cf2bf18eee23092bbda870ad12361964f5f4eb0fdab3e77521f4a05940c6d", size = 25202, upload-time = "2026-05-25T05:45:38.039Z" }, + { url = "https://files.pythonhosted.org/packages/74/c7/c675d5525269e9b24f11bc51b170f7f6fdd6187fe27f338fa2616b89e91c/ulid_transform-2.2.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46c4cd1a46912fda1c6b885cfb315b798c4ca43d56763a8989f1ea878b76cecc", size = 27464, upload-time = "2026-05-25T05:45:39.168Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/629cea7f0e1238410d33eb2ad1d633d328cb20b74af2b76adb622b54574d/ulid_transform-2.2.9-cp313-cp313-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6a618bce08233ef693d1fcbc7169943a52a7cc729a71e75f6e6bc260e4e8702f", size = 27812, upload-time = "2026-05-25T05:45:40.483Z" }, + { url = "https://files.pythonhosted.org/packages/02/25/3eb0267d181dcb54eb36572d8c725b53ca8677eebc380d4e565b26535179/ulid_transform-2.2.9-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:423785ae163602c441e9d1eeeb237005d704cbfb593d596052c5ad2f901301b2", size = 26407, upload-time = "2026-05-25T05:45:41.516Z" }, + { url = "https://files.pythonhosted.org/packages/31/e8/037f9a8f500e73118f32f8d60c9bf7f677ce5d28d1d2e1870539bf29344b/ulid_transform-2.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f5d490330924dd9d9267d22025077c1d47c8f28383bd315675164a7a918692cd", size = 1007530, upload-time = "2026-05-25T05:45:42.906Z" }, + { url = "https://files.pythonhosted.org/packages/81/0f/5bab249a5d3244ce574289a70e380dcec71cf80931aaf1e7fa4e6be60c57/ulid_transform-2.2.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3227a50aa76f018966a43b5182627466baf19cd6d0649d235a7029ec8277c1bd", size = 877353, upload-time = "2026-05-25T05:45:44.363Z" }, + { url = "https://files.pythonhosted.org/packages/4b/41/81b05efffa5bdaa348711ba05228873a07098cb2b3be3d35ed92072ebca2/ulid_transform-2.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:873e619db2bbda1a87eecf932e85e6016c73c185f7f08e1335f6a37bc30c8a98", size = 1058952, upload-time = "2026-05-25T05:45:46.01Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b6/84db4fc991f1604ba45546d31bc4fa04e88cef1e40e6028eaa3b7fe1420b/ulid_transform-2.2.9-cp313-cp313-win32.whl", hash = "sha256:f1e5825a28231ea7e5a7a1cd2eddc9beb4a5b8471c82c78e4e4947e60cf7aed6", size = 25369, upload-time = "2026-05-25T05:45:47.57Z" }, + { url = "https://files.pythonhosted.org/packages/15/e1/575d0d28f0c93db62c4ba610f74f435ee1452bfd80058c792d6d69d26c7a/ulid_transform-2.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:88ac570180c147a42a99429fc089245bc948063e165d12c8645eb24b041fea89", size = 25458, upload-time = "2026-05-25T05:45:48.961Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2e/6351361aefb2bfe44036519bb60ef9689511c7351c42e2edb7f86e28cb0a/ulid_transform-2.2.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b64afbdb9143210e7f9f240a91fa8fd54a559c6627e8f4cdb9f40771f8b8a498", size = 25212, upload-time = "2026-05-25T05:45:49.977Z" }, + { url = "https://files.pythonhosted.org/packages/7e/2c/db41412391950083c387c8185641db32e6f67862b52391c992cdd1824b5c/ulid_transform-2.2.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fcbef4c1c537da14fa098529167f64f7d351e7aebeeeafd6779ed2ffe10f4649", size = 27462, upload-time = "2026-05-25T05:45:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/71/aa/8a2b3b3b6fd638f88117e6852f5b1aaaf5f73de25e2d7a8bb483a9adaa58/ulid_transform-2.2.9-cp314-cp314-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:73fef8bdfae646ec4c54f5cc96dc27510fdb55c1aaed3c20d6661e8b51d01bd9", size = 27809, upload-time = "2026-05-25T05:45:52.782Z" }, + { url = "https://files.pythonhosted.org/packages/78/f5/520fc50bfd97a616e5c367cd820eee65ffc3537acc68fdacb09f31fef149/ulid_transform-2.2.9-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6db97794ce8638ee2bd11ace5ddc9e23e450e971b264556c2c42ab43fc3d580", size = 26406, upload-time = "2026-05-25T05:45:54.1Z" }, + { url = "https://files.pythonhosted.org/packages/9a/33/27c3f9ed60426b514b1501c8acd47ab05a305fbb1e294dce6a13c52a88be/ulid_transform-2.2.9-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:f62d23bf7ed08e086e27dc5f5c89f9aec4d79316174529070073505bc367d8fa", size = 16808, upload-time = "2026-05-25T05:39:02.653Z" }, + { url = "https://files.pythonhosted.org/packages/5f/43/b246e2c3e4936789f9797c5e61ebde1d1c6fecbc9a1d6cdf6c427518006e/ulid_transform-2.2.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ae8473948da794f27ecf800511ff7e9bc340e1e5d07f28f1f640a860240d1d39", size = 1007530, upload-time = "2026-05-25T05:45:55.2Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/c1ab2d3901899962afcbddce47dc796f7eaa6694f96c3f4977ab43883712/ulid_transform-2.2.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a978e55081259c95a14bd7d48db6540645ca228c2d07da6c176c291cd955e85b", size = 877349, upload-time = "2026-05-25T05:45:57.625Z" }, + { url = "https://files.pythonhosted.org/packages/af/b1/2bd4509651123caa37b447e0d4e5bb321067fd79a1bcaefa2dfc7fe41206/ulid_transform-2.2.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:364651ca7bf17e56b7570855d185e10ae461d5bfe15943936ee6f79fde71bb02", size = 1058955, upload-time = "2026-05-25T05:45:59.075Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d6/e48135a3992562630043f57a85ba36109787b91d27da842ae8e7136fd24f/ulid_transform-2.2.9-cp314-cp314-win32.whl", hash = "sha256:6daaaadfe2d7e465f0850c05dfcd5a785426571361433e84fc3f5a8ed78321ae", size = 25741, upload-time = "2026-05-25T05:46:00.438Z" }, + { url = "https://files.pythonhosted.org/packages/a3/8e/ff96df79000ed277a9fc73e8825f27c81fba2446acb7fe33f53a1489ba70/ulid_transform-2.2.9-cp314-cp314-win_amd64.whl", hash = "sha256:f586c25a2457b88511e1acceae9aeb90f002aee06a4d37e68280ba0ff6af4b9d", size = 25837, upload-time = "2026-05-25T05:46:01.955Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c6/59879019153dbbd34eff5f3c633f329b5d75519c1a95f3271948ea51715f/ulid_transform-2.2.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a873a51c8ec7f3d51c6d3d6ecdf3720d29637d5619e7c7df16dbd451f19ceb82", size = 25198, upload-time = "2026-05-25T05:46:03.022Z" }, + { url = "https://files.pythonhosted.org/packages/51/c6/293ba411f0e32e17086baed7c955e4287e368275f31f59ef7a005d401eac/ulid_transform-2.2.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65711af19211c89c115954aab2965abad5287596d24f5754fcd142e80dd7656c", size = 27474, upload-time = "2026-05-25T05:46:04.058Z" }, + { url = "https://files.pythonhosted.org/packages/57/17/2eabe1ba6e1ed341d372c996380a812a5278217582349fc609d61fb57342/ulid_transform-2.2.9-cp314-cp314t-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9456b9bd8a572448cbddd9f3c5266d7354c68a3c0dc25209108ea904846fac3", size = 27826, upload-time = "2026-05-25T05:46:05.166Z" }, + { url = "https://files.pythonhosted.org/packages/65/36/1f3f2e6c1243cafd75d9c2736b6b73e5530d96b49f955493e8917b2fe716/ulid_transform-2.2.9-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12639c5dda80d6d90437e9427dd87c6bb71bb291dcfe8d6063703394c2e15e3a", size = 26405, upload-time = "2026-05-25T05:46:06.268Z" }, + { url = "https://files.pythonhosted.org/packages/00/6b/200c79dc081b4c9673923dd9d9bc9dc0e47ccdc90eeae8e85acbb1914751/ulid_transform-2.2.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8cf746c642569dfe3efd94fe45672954f6257ff910f993bca80ab6dcb5a34f92", size = 1007528, upload-time = "2026-05-25T05:46:07.417Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/228a44957e37a3c7541989d47c481c9f51ae045ad07fdacc0deb49d4e120/ulid_transform-2.2.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:5c19d10c7ad9a24c456aba5cbd74aeeca84a4b33cd35077b4bec61d2cf92e30c", size = 877361, upload-time = "2026-05-25T05:46:09.145Z" }, + { url = "https://files.pythonhosted.org/packages/a9/7e/4beaa80ac820b3243e376f79fde4a8bca2b477d8b6529ff5afcfd83f5f74/ulid_transform-2.2.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:35084b9ade29b462472485966789e5edc66dcbc376ffaf89759e7ada0c7d1f6c", size = 1058912, upload-time = "2026-05-25T05:46:10.736Z" }, + { url = "https://files.pythonhosted.org/packages/39/f8/ab3af64aa6ced7c65b09aa4b1c33d2cd4bf48646c9957a17659cea69e1c5/ulid_transform-2.2.9-cp314-cp314t-win32.whl", hash = "sha256:ccf6b173ae1e90a589935fd99176dc9d71d461c9f413b1a754d542ba2d399860", size = 25725, upload-time = "2026-05-25T05:46:12.117Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c8/cbb9374e8a0e26d18e94ffb3e1f861beab5312b3e0be4fbad1bf60cdb47c/ulid_transform-2.2.9-cp314-cp314t-win_amd64.whl", hash = "sha256:fed352965a8682a156000d29e0efff8b85e7c2b8cb74f2e953a1e6d0157648b9", size = 25827, upload-time = "2026-05-25T05:46:13.164Z" }, +] + +[[package]] +name = "urllib3" +version = "1.26.20" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380, upload-time = "2024-08-29T15:43:11.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225, upload-time = "2024-08-29T15:43:08.921Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "usb-devices" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/93/76be49b956574f75c9b907193732ff2b6b8f7075692adde93a7f151b30e5/usb_devices-0.5.1.tar.gz", hash = "sha256:8ef45c401490391a3651b3ba748eb213a654ac165be10a0786ab963668c49cc3", size = 5365, upload-time = "2026-05-27T18:01:56.115Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/12/f8dd0bd25e9e2ab747b47ae4efed08de75f595f6136a71bafaf3db0c47ed/usb_devices-0.5.1-py3-none-any.whl", hash = "sha256:e77e7972b5ac6a883b3778b0273d9582c5b8ae847ac78fba3d080dd4a4cc69ad", size = 5423, upload-time = "2026-05-27T18:01:55.012Z" }, +] + [[package]] name = "uv" version = "0.11.16" @@ -1523,122 +4352,470 @@ wheels = [ [[package]] name = "voluptuous" -version = "0.16.0" +version = "0.15.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/af/a54ce0fb6f1d867e0b9f0efe5f082a691f51ccf705188fca67a3ecefd7f4/voluptuous-0.15.2.tar.gz", hash = "sha256:6ffcab32c4d3230b4d2af3a577c87e1908a714a11f6f95570456b1849b0279aa", size = 51651, upload-time = "2024-07-02T19:10:00.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/a8/8f9cc6749331186e6a513bfe3745454f81d25f6e34c6024f88f80c71ed28/voluptuous-0.15.2-py3-none-any.whl", hash = "sha256:016348bc7788a9af9520b1764ebd4de0df41fe2138ebe9e06fa036bf86a65566", size = 31349, upload-time = "2024-07-02T19:09:58.125Z" }, +] + +[[package]] +name = "voluptuous-openapi" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "voluptuous", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/3a/0dda3268377c890561bdce1c445bd36a9c79fa77be7f456d9db45b664a7f/voluptuous_openapi-0.0.5.tar.gz", hash = "sha256:1619cd298da0024fa01338ac5a9ce3b3b7059205ce3c69230c24803b11308fb0", size = 10621, upload-time = "2024-07-30T06:18:46.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4e/b20c825f6cc78ed857f44afe32b7408c1d1c2c08cab4bcb5bb9b5e8fcb00/voluptuous_openapi-0.0.5-py3-none-any.whl", hash = "sha256:d51509503b3080b54a746ef357534f124ef7ae4f0ccecd3c3f261660b193c19a", size = 8636, upload-time = "2024-07-30T06:18:45.134Z" }, +] + +[[package]] +name = "voluptuous-openapi" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/5f/e77f088f4eff2e0f572f651bb9c2259561ec32d0bff20f59dd107e392ad8/voluptuous_openapi-0.3.0.tar.gz", hash = "sha256:1ecd2386e63b59b791e0f64715e317c7018a2e5598333138bd1f28adfd348d5c", size = 17467, upload-time = "2025-12-31T00:40:24.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/2d/3a8728e638bac8fd5857bb7775e39a9a520ae90b1d6dffd8badfa9852ccc/voluptuous_openapi-0.3.0-py3-none-any.whl", hash = "sha256:db35598e9fc711cbaf7d7732de160de3edf89cc7411b6febe5a132f1f54ea931", size = 10896, upload-time = "2025-12-31T00:40:23.067Z" }, +] + +[[package]] +name = "voluptuous-serialize" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] +dependencies = [ + { name = "voluptuous", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/09/c26b38ab35d9f61e9bf5c3e805215db1316dd73c77569b47ab36a40d19b1/voluptuous-serialize-2.6.0.tar.gz", hash = "sha256:79acdc58239582a393144402d827fa8efd6df0f5350cdc606d9242f6f9bca7c4", size = 7562, upload-time = "2023-02-15T21:09:08.077Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/86/355e1c65934760e2fb037219f1f360562567cf6731d281440c1d57d36856/voluptuous_serialize-2.6.0-py3-none-any.whl", hash = "sha256:85a5c8d4d829cb49186c1b5396a8a517413cc5938e1bb0e374350190cd139616", size = 6819, upload-time = "2023-02-15T21:09:06.512Z" }, +] + +[[package]] +name = "voluptuous-serialize" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/70/03a9b61324e1bb8b16682455b8b953bccd1001a28e43478c86f539e26285/voluptuous_serialize-2.7.0.tar.gz", hash = "sha256:d0da959f2fd93c8f1eb779c5d116231940493b51020c2c1026bab76eb56cd09e", size = 9202, upload-time = "2025-08-17T10:43:04.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/41/d536d9cf39821c35cc13aff403728e60e32b2fd711c240b6b9980af1c03f/voluptuous_serialize-2.7.0-py3-none-any.whl", hash = "sha256:ee3ebecace6136f38d0bf8c20ee97155db2486c6b2d0795563fafd04a519e76f", size = 7850, upload-time = "2025-08-17T10:43:03.498Z" }, +] + +[[package]] +name = "webrtc-models" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/f4/0738e6849858deae22218be3bbb8207ba83a96e9d0ec7e8e8cd67b30e5ca/voluptuous-0.16.0.tar.gz", hash = "sha256:006535e22fed944aec17bef6e8725472476194743c87bd233e912eb463f8ff05", size = 54238, upload-time = "2025-12-18T23:18:46.08Z" } +dependencies = [ + { name = "mashumaro", marker = "python_full_version >= '3.14.2'" }, + { name = "orjson", version = "3.11.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/e8/050ffe3b71ff44d3885eee2bed763ca937e2a30bc950d866f22ba657776b/webrtc_models-0.3.0.tar.gz", hash = "sha256:559c743e5cc3bcc8133be1b6fb5e8492a9ddb17151129c21cbb2e3f2a1166526", size = 9411, upload-time = "2024-11-18T17:43:45.682Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/e7/62f29980c9e8d75af93b642a0c37aa8e201fd5268ba3a7179c172549bac3/webrtc_models-0.3.0-py3-none-any.whl", hash = "sha256:8fddded3ffd7ca837de878033501927580799a2c1b7829f7ae8a0f43b49004ea", size = 7476, upload-time = "2024-11-18T17:43:44.165Z" }, +] + +[[package]] +name = "winrt-runtime" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/dd/acdd527c1d890c8f852cc2af644aa6c160974e66631289420aa871b05e65/winrt_runtime-3.2.1.tar.gz", hash = "sha256:c8dca19e12b234ae6c3dadf1a4d0761b51e708457492c13beb666556958801ea", size = 21721, upload-time = "2025-06-06T14:40:27.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/54/3dd06f2341fab6abb06588a16b30e0b213b0125be7b79dafc3bdba3b334a/winrt_runtime-3.2.1-cp312-cp312-win32.whl", hash = "sha256:762b3d972a2f7037f7db3acbaf379dd6d8f6cda505f71f66c6b425d1a1eae2f1", size = 210090, upload-time = "2025-06-06T06:44:08.151Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a1/1d7248d5c62ccbea5f3e0da64ca4529ce99c639c3be2485b6ed709f5c740/winrt_runtime-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:06510db215d4f0dc45c00fbb1251c6544e91742a0ad928011db33b30677e1576", size = 241391, upload-time = "2025-06-06T06:44:09.442Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ae/6a205d8dafc79f7c242be7f940b1e0c1971fd64ab3079bda4b514aa3d714/winrt_runtime-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:14562c29a087ccad38e379e585fef333e5c94166c807bdde67b508a6261aa195", size = 415242, upload-time = "2025-06-06T06:44:10.407Z" }, + { url = "https://files.pythonhosted.org/packages/79/d4/1a555d8bdcb8b920f8e896232c82901cc0cda6d3e4f92842199ae7dff70a/winrt_runtime-3.2.1-cp313-cp313-win32.whl", hash = "sha256:44e2733bc709b76c554aee6c7fe079443b8306b2e661e82eecfebe8b9d71e4d1", size = 210022, upload-time = "2025-06-06T06:44:11.767Z" }, + { url = "https://files.pythonhosted.org/packages/aa/24/2b6e536ca7745d788dfd17a2ec376fa03a8c7116dc638bb39b035635484f/winrt_runtime-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c1fdcaeedeb2920dc3b9039db64089a6093cad2be56a3e64acc938849245a6d", size = 241349, upload-time = "2025-06-06T06:44:12.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/7f/6d72973279e2929b2a71ed94198ad4a5d63ee2936e91a11860bf7b431410/winrt_runtime-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:28f3dab083412625ff4d2b46e81246932e6bebddf67bea7f05e01712f54e6159", size = 415126, upload-time = "2025-06-06T06:44:13.702Z" }, + { url = "https://files.pythonhosted.org/packages/c8/87/88bd98419a9da77a68e030593fee41702925a7ad8a8aec366945258cbb31/winrt_runtime-3.2.1-cp314-cp314-win32.whl", hash = "sha256:9b6298375468ac2f6815d0c008a059fc16508c8f587e824c7936ed9216480dad", size = 210257, upload-time = "2025-09-20T07:06:41.054Z" }, + { url = "https://files.pythonhosted.org/packages/87/85/e5c2a10d287edd9d3ee8dc24bf7d7f335636b92bf47119768b7dd2fd1669/winrt_runtime-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:e36e587ab5fd681ee472cd9a5995743f75107a1a84d749c64f7e490bc86bc814", size = 241873, upload-time = "2025-09-20T07:06:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/52/2a/eb9e78397132175f70dd51dfa4f93e489c17d6b313ae9dce60369b8d84a7/winrt_runtime-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:35d6241a2ebd5598e4788e69768b8890ee1eee401a819865767a1fbdd3e9a650", size = 416222, upload-time = "2025-09-20T07:06:43.376Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/a0/1c8a0c469abba7112265c6cb52f0090d08a67c103639aee71fc690e614b8/winrt_windows_devices_bluetooth-3.2.1.tar.gz", hash = "sha256:db496d2d92742006d5a052468fc355bf7bb49e795341d695c374746113d74505", size = 23732, upload-time = "2025-06-06T14:41:20.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/ff/c4a3de909a875b46fad5e9f4fd412bba48571405bfa802b878954abf128c/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win32.whl", hash = "sha256:18c833ec49e7076127463679e85efc59f61785ade0dc185c852586b21be1f31c", size = 105752, upload-time = "2025-06-06T07:00:10.684Z" }, + { url = "https://files.pythonhosted.org/packages/e7/78/bfee1f0c8d188c561c5b946ab21f6a0037e60dea110e80b1d6a1d529639f/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:9b6702c462b216c91e32388023a74d0f87210cef6fd5d93b7191e9427ce2faca", size = 113356, upload-time = "2025-06-06T07:00:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1b/d9da9c29d36cabadef4e19c3e9ba6d2692f6a28224c81fcff757132ea0da/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:419fd1078c7749119f6b4bbf6be4e586e03a0ed544c03b83178f1d85f1b3d148", size = 104724, upload-time = "2025-06-06T07:00:12.406Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/797516c5c0f8d7f5b680862e0ed7c1087c58aec0bcf57a417fa90f7eb983/winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win32.whl", hash = "sha256:12b0a16fb36ce0b42243ca81f22a6b53fbb344ed7ea07a6eeec294604f0505e4", size = 105757, upload-time = "2025-06-06T07:00:13.269Z" }, + { url = "https://files.pythonhosted.org/packages/05/6d/f60588846a065e69a2ec5e67c5f85eb45cb7edef2ee8974cd52fa8504de6/winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6703dfbe444ee22426738830fb305c96a728ea9ccce905acfdf811d81045fdb3", size = 113363, upload-time = "2025-06-06T07:00:14.135Z" }, + { url = "https://files.pythonhosted.org/packages/2c/13/2d3c4762018b26a9f66879676ea15d7551cdbf339c8e8e0c56ea05ea31ef/winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2cf8a0bfc9103e32dc7237af15f84be06c791f37711984abdca761f6318bbdb2", size = 104722, upload-time = "2025-06-06T07:00:14.999Z" }, + { url = "https://files.pythonhosted.org/packages/b7/95/91cfdf941a1ba791708ab3477fc4e46793c8fe9117fc3e0a8c5ac5d7a09c/winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win32.whl", hash = "sha256:de36ded53ca3ba12fc6dd4deb14b779acc391447726543815df4800348aad63a", size = 109015, upload-time = "2025-09-20T07:09:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/7460655628d0f340a93524f5236bb9f8514eb0e1d334b38cba8a89f6c1a6/winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3295d932cc93259d5ccb23a41e3a3af4c78ce5d6a6223b2b7638985f604fa34c", size = 115931, upload-time = "2025-09-20T07:09:51.922Z" }, + { url = "https://files.pythonhosted.org/packages/de/70/e1248dea2ab881eb76b61ff1ad6cb9c07ac005faf99349e4af0b29bc3f1b/winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1f61c178766a1bbce0669f44790c6161ff4669404c477b4aedaa576348f9e102", size = 109561, upload-time = "2025-09-20T07:09:52.733Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth-advertisement" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/fc/7ffe66ca4109b9e994b27c00f3d2d506e6e549e268791f755287ad9106d8/winrt_windows_devices_bluetooth_advertisement-3.2.1.tar.gz", hash = "sha256:0223852a7b7fa5c8dea3c6a93473bd783df4439b1ed938d9871f947933e574cc", size = 16906, upload-time = "2025-06-06T14:41:21.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/15/ad05c28e049208c97011728e2debdb45439175f75efe357b6faa4c9ba099/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win32.whl", hash = "sha256:901933cc40de5eb7e5f4188897c899dd0b0f577cb2c13eab1a63c7dfe89b08c4", size = 90033, upload-time = "2025-06-06T07:00:23.421Z" }, + { url = "https://files.pythonhosted.org/packages/26/48/074779081841f6eba4987930c4e7adcec38a5985b7dffd9fecc41f39a89c/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e6c66e7d4f4ca86d2c801d30efd2b9673247b59a2b4c365d9e11650303d68d89", size = 95824, upload-time = "2025-06-06T07:00:24.238Z" }, + { url = "https://files.pythonhosted.org/packages/aa/25/e01966033a02b2d0718710bb47ef4f6b9b5a619ca2c857e06eb5c8e3ed13/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:447d19defd8982d39944642eb7ebe89e4e20259ec9734116cf88879fb2c514ff", size = 89311, upload-time = "2025-06-06T07:00:25.029Z" }, + { url = "https://files.pythonhosted.org/packages/34/01/8fc8e57605ea08dd0723c035ed0c2d0435dace2bc80a66d33aecfea49a56/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4122348ea525a914e85615647a0b54ae8b2f42f92cdbf89c5a12eea53ef6ed90", size = 90037, upload-time = "2025-06-06T07:00:25.818Z" }, + { url = "https://files.pythonhosted.org/packages/86/83/503cf815d84c5ba8c8bc61480f32e55579ebf76630163405f7df39aa297b/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:b66410c04b8dae634a7e4b615c3b7f8adda9c7d4d6902bcad5b253da1a684943", size = 95822, upload-time = "2025-06-06T07:00:26.666Z" }, + { url = "https://files.pythonhosted.org/packages/32/13/052be8b6642e6f509b30c194312b37bfee8b6b60ac3bd5ca2968c3ea5b80/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:07af19b1d252ddb9dd3eb2965118bc2b7cabff4dda6e499341b765e5038ca61d", size = 89326, upload-time = "2025-06-06T07:00:27.477Z" }, + { url = "https://files.pythonhosted.org/packages/27/3d/421d04a20037370baf13de929bc1dc5438b306a76fe17275ec5d893aae6c/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win32.whl", hash = "sha256:2985565c265b3f9eab625361b0e40e88c94b03d89f5171f36146f2e88b3ee214", size = 92264, upload-time = "2025-09-20T07:09:53.563Z" }, + { url = "https://files.pythonhosted.org/packages/07/c7/43601ab82fe42bcff430b8466d84d92b31be06cc45c7fd64e9aac40f7851/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d102f3fac64fde32332e370969dfbc6f37b405d8cc055d9da30d14d07449a3c2", size = 97517, upload-time = "2025-09-20T07:09:54.411Z" }, + { url = "https://files.pythonhosted.org/packages/91/17/e3303f6a25a2d98e424b06580fc85bbfd068f383424c67fa47cb1b357a46/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:ffeb5e946cd42c32c6999a62e240d6730c653cdfb7b49c7839afba375e20a62a", size = 94122, upload-time = "2025-09-20T07:09:55.187Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth-genericattributeprofile" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/21/aeeddc0eccdfbd25e543360b5cc093233e2eab3cdfb53ad3cabae1b5d04d/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1.tar.gz", hash = "sha256:cdf6ddc375e9150d040aca67f5a17c41ceaf13a63f3668f96608bc1d045dde71", size = 38896, upload-time = "2025-06-06T14:41:22.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/a1/75ac783a5faee9b455fef2f53b7fef97b21ed60d52401b44c690202141e4/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win32.whl", hash = "sha256:ef894d21e0a805f3e114940254636a8045335fa9de766c7022af5d127dfad557", size = 183326, upload-time = "2025-06-06T07:00:52.662Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d9/a9dcc15322d2f5c7dfd491bd7ab121e36437caf78ebfa92bc0dd0546e2ca/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:db05de95cd1b24a51abb69cb936a8b17e9214e015757d0b37e3a5e207ddceb3d", size = 187810, upload-time = "2025-06-06T07:00:53.594Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fc/47d00af076f558267097af3050910beda6bf8a21ceaa5830bbd26fcaf85e/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d4e131cf3d15fc5ad81c1bcde3509ac171298217381abed6bdf687f29871984", size = 184516, upload-time = "2025-06-06T07:00:55.24Z" }, + { url = "https://files.pythonhosted.org/packages/ec/93/30b45ce473d1a604908221a1fa035fe8d5e4bb9008e820ae671a21dab94c/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win32.whl", hash = "sha256:b1879c8dcf46bd2110b9ad4b0b185f4e2a5f95170d014539203a5fee2b2115f0", size = 183342, upload-time = "2025-06-06T07:00:56.16Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3b/eb9d99b82a36002d7885206d00ea34f4a23db69c16c94816434ded728fa3/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d8d89f01e9b6931fb48217847caac3227a0aeb38a5b7782af71c2e7b262ec30", size = 187844, upload-time = "2025-06-06T07:00:57.134Z" }, + { url = "https://files.pythonhosted.org/packages/84/9b/ebbbe9be9a3e640dcfc5f166eb48f2f9d8ce42553f83aa9f4c5dcd9eb5f5/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:4e71207bb89798016b1795bb15daf78afe45529f2939b3b9e78894cfe650b383", size = 184540, upload-time = "2025-06-06T07:00:58.081Z" }, + { url = "https://files.pythonhosted.org/packages/b7/32/cb447ca7730a1e05730272309b074da6a04af29a8c0f5121014db8a2fc02/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win32.whl", hash = "sha256:d5f83739ca370f0baf52b0400aebd6240ab80150081fbfba60fd6e7b2e7b4c5f", size = 185249, upload-time = "2025-09-20T07:09:58.639Z" }, + { url = "https://files.pythonhosted.org/packages/bb/fa/f465d5d44dda166bf7ec64b7a950f57eca61f165bfe18345e9a5ea542def/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:13786a5853a933de140d456cd818696e1121c7c296ae7b7af262fc5d2cffb851", size = 193739, upload-time = "2025-09-20T07:09:59.893Z" }, + { url = "https://files.pythonhosted.org/packages/78/08/51c53ac3c704cd92da5ed7e7b9b57159052f6e46744e4f7e447ed708aa22/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:5140682da2860f6a55eb6faf9e980724dc457c2e4b4b35a10e1cebd8fc97d892", size = 194836, upload-time = "2025-09-20T07:10:00.87Z" }, +] + +[[package]] +name = "winrt-windows-devices-enumeration" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/dd/75835bfbd063dffa152109727dedbd80f6e92ea284855f7855d48cdf31c9/winrt_windows_devices_enumeration-3.2.1.tar.gz", hash = "sha256:df316899e39bfc0ffc1f3cb0f5ee54d04e1d167fbbcc1484d2d5121449a935cf", size = 23538, upload-time = "2025-06-06T14:41:26.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/3e/81642208ecd6c6c936f35a39a433c54e3f68e09d316546b8f953581ae334/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win32.whl", hash = "sha256:1db22b0292b93b0688d11ad932ad1f3629d4f471310281a2fbfe187530c2c1f3", size = 130249, upload-time = "2025-06-06T07:02:02.237Z" }, + { url = "https://files.pythonhosted.org/packages/00/f4/a9ede5f3f0d86abfc7590726cf711133d97419b49ced372fca532e4f0696/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a73bc88d7f510af454f2b392985501c96f39b89fd987140708ccaec1588ceebc", size = 141512, upload-time = "2025-06-06T07:02:03.424Z" }, + { url = "https://files.pythonhosted.org/packages/31/ef/4fad07c03124bdc3acd64f80f3bd3cc4417ea641e07bb16a9503afd3e554/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:2853d687803f0dd76ae1afe3648abc0453e09dff0e7eddbb84b792eddb0473ca", size = 135383, upload-time = "2025-06-06T07:02:04.312Z" }, + { url = "https://files.pythonhosted.org/packages/ff/7d/ebd712ab8ccd599c593796fbcd606abe22b5a8e20db134aa87987d67ac0e/winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win32.whl", hash = "sha256:14a71cdcc84f624c209cbb846ed6bd9767a9a9437b2bf26b48ac9a91599da6e9", size = 130276, upload-time = "2025-06-06T07:02:05.178Z" }, + { url = "https://files.pythonhosted.org/packages/70/de/f30daaaa0e6f4edb6bd7ddb3e058bd453c9ad90c032a4545c4d4639338aa/winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6ca40d334734829e178ad46375275c4f7b5d6d2d4fc2e8879690452cbfb36015", size = 141536, upload-time = "2025-06-06T07:02:06.067Z" }, + { url = "https://files.pythonhosted.org/packages/75/4b/9a6aafdc74a085c550641a325be463bf4b811f6f605766c9cd4f4b5c19d2/winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2d14d187f43e4409c7814b7d1693c03a270e77489b710d92fcbbaeca5de260d4", size = 135362, upload-time = "2025-06-06T07:02:06.997Z" }, + { url = "https://files.pythonhosted.org/packages/41/31/5785cd1ec54dc0f0e6f3e6a466d07a62b8014a6e2b782e80444ef87e83ab/winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win32.whl", hash = "sha256:e087364273ed7c717cd0191fed4be9def6fdf229fe9b536a4b8d0228f7814106", size = 134252, upload-time = "2025-09-20T07:10:12.935Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f6/68d91068048410f49794c0b19c45759c63ca559607068cfe5affba2f211b/winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:0da1ddb8285d97a6775c36265d7157acf1bbcb88bcc9a7ce9a4549906c822472", size = 145509, upload-time = "2025-09-20T07:10:13.797Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a4/898951d5bfc474aa9c7d133fe30870f0f2184f4ba3027eafb779d30eb7bc/winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:09bf07e74e897e97a49a9275d0a647819254ddb74142806bbbcf4777ed240a22", size = 141334, upload-time = "2025-09-20T07:10:14.637Z" }, +] + +[[package]] +name = "winrt-windows-devices-radios" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/02/9704ea359ad8b0d6faa1011f98fb477e8fb6eac5201f39d19e73c2407e7b/winrt_windows_devices_radios-3.2.1.tar.gz", hash = "sha256:4dc9b9d1501846049eb79428d64ec698d6476c27a357999b78a8331072e18a0b", size = 5908, upload-time = "2025-06-06T14:41:44.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/e0/4731a3c412318b2c5e74a8803a32e2fb9afc2c98368c6b61a422eb359e7e/winrt_windows_devices_radios-3.2.1-cp312-cp312-win32.whl", hash = "sha256:c3e683ce682338a5a5ed465f735e223ba7a22f16d0bbea2d070962bc7657edbb", size = 38606, upload-time = "2025-06-06T07:08:01.477Z" }, + { url = "https://files.pythonhosted.org/packages/37/8e/91464854dfc9e0be9ce8dcbe2bd6a67c19b68ab91584fc5de0f4f13e78f8/winrt_windows_devices_radios-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a116e552a3f38607b9be558fb2e7de9b4450d1f9080069944d74d80cdda1873e", size = 40172, upload-time = "2025-06-06T07:08:02.214Z" }, + { url = "https://files.pythonhosted.org/packages/c3/0d/1bd62f606b6c4dfa936fccc4712be5506a40fc5d1b7177c3d3cbcaf30972/winrt_windows_devices_radios-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4c28822f9251c9d547324f596b5c2581f050254ded05e5b786c650a3502744c1", size = 36989, upload-time = "2025-06-06T07:08:03.295Z" }, + { url = "https://files.pythonhosted.org/packages/d1/94/c22a14fd424632f3f3c0b25672218db9e8f4ae9e1355e0b148f2fe6015b5/winrt_windows_devices_radios-3.2.1-cp313-cp313-win32.whl", hash = "sha256:ae4a0065927fcd2d10215223f8a46be6fb89bad71cb4edd25dae3d01c137b3a8", size = 38613, upload-time = "2025-06-06T07:08:04.077Z" }, + { url = "https://files.pythonhosted.org/packages/39/c1/24cec0cc228642554b48d436a7617d7162fb952919c55fc26e2d99c310bd/winrt_windows_devices_radios-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:bf1a975f46a2aa271ffea1340be0c7e64985050d07433e701343dddc22a72290", size = 40180, upload-time = "2025-06-06T07:08:04.849Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/776453af26e78c0d0c0e1bfa89f86fd81322872f31a3e5dafb344dd47bf2/winrt_windows_devices_radios-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:10b298ed154c5824cea2de174afce1694ed2aabfb58826de814074027ffef96f", size = 36989, upload-time = "2025-06-06T07:08:05.576Z" }, + { url = "https://files.pythonhosted.org/packages/76/79/4627afae6b389ddd1e5f1d691663c6b14d6c8f98959082aed1217cc57ef9/winrt_windows_devices_radios-3.2.1-cp314-cp314-win32.whl", hash = "sha256:21452e1cae50e44cd1d5e78159e1b9986ac3389b66458ad89caa196ce5eca2d6", size = 39521, upload-time = "2025-09-20T07:11:17.992Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7c/c6aea91908ee7279ed51d12157bc8aeecb8850af2441073c3c91b261ad31/winrt_windows_devices_radios-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:6a8413e586fe597c6849607885cca7e0549da33ae5699165d11f7911534c6eaf", size = 41121, upload-time = "2025-09-20T07:11:18.747Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/652f14e3c501452ad8e0723518d9bbd729219b47f4a4dbe2966c2f82dca8/winrt_windows_devices_radios-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:39129fd9d09103adb003575f59881c1a5a70a43310547850150b46c6f4020312", size = 38114, upload-time = "2025-09-20T07:11:19.599Z" }, +] + +[[package]] +name = "winrt-windows-foundation" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/55/098ce7ea0679efcc1298b269c48768f010b6c68f90c588f654ec874c8a74/winrt_windows_foundation-3.2.1.tar.gz", hash = "sha256:ad2f1fcaa6c34672df45527d7c533731fdf65b67c4638c2b4aca949f6eec0656", size = 30485, upload-time = "2025-06-06T14:41:53.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/f8/495e304ddedd5ff2f196efbde906265cb75ade4d79e2937837f72ef654a0/winrt_windows_foundation-3.2.1-cp312-cp312-win32.whl", hash = "sha256:867642ccf629611733db482c4288e17b7919f743a5873450efb6d69ae09fdc2b", size = 112169, upload-time = "2025-06-06T07:11:01.438Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5e/b5059e4ece095351c496c9499783130c302d25e353c18031d5231b1b3b3c/winrt_windows_foundation-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:45550c5b6c2125cde495c409633e6b1ea5aa1677724e3b95eb8140bfccbe30c9", size = 118668, upload-time = "2025-06-06T07:11:02.475Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/acbcb3ef07b1b67e2de4afab9176a5282cfd775afd073efe6828dfc65ace/winrt_windows_foundation-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:94f4661d71cb35ebc52be7af112f2eeabdfa02cb05e0243bf9d6bd2cafaa6f37", size = 109671, upload-time = "2025-06-06T07:11:03.538Z" }, + { url = "https://files.pythonhosted.org/packages/7b/71/5e87131e4aecc8546c76b9e190bfe4e1292d028bda3f9dd03b005d19c76c/winrt_windows_foundation-3.2.1-cp313-cp313-win32.whl", hash = "sha256:3998dc58ed50ecbdbabace1cdef3a12920b725e32a5806d648ad3f4829d5ba46", size = 112184, upload-time = "2025-06-06T07:11:04.459Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7f/8d5108461351d4f6017f550af8874e90c14007f9122fa2eab9f9e0e9b4e1/winrt_windows_foundation-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6e98617c1e46665c7a56ce3f5d28e252798416d1ebfee3201267a644a4e3c479", size = 118672, upload-time = "2025-06-06T07:11:05.55Z" }, + { url = "https://files.pythonhosted.org/packages/44/f5/2edf70922a3d03500dab17121b90d368979bd30016f6dbca0d043f0c71f1/winrt_windows_foundation-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2a8c1204db5c352f6a563130a5a41d25b887aff7897bb677d4ff0b660315aad4", size = 109673, upload-time = "2025-06-06T07:11:06.398Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0a/d77346e39fe0c81f718cde49f83fe77c368c0e14c6418f72dfa1e7ef22d0/winrt_windows_foundation-3.2.1-cp314-cp314-win32.whl", hash = "sha256:35e973ab3c77c2a943e139302256c040e017fd6ff1a75911c102964603bba1da", size = 114590, upload-time = "2025-09-20T07:11:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/a1/56/4d2b545bea0f34f68df6d4d4ca22950ff8a935497811dccdc0ca58737a05/winrt_windows_foundation-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22a7ebcec0d262e60119cff728f32962a02df60471ded8b2735a655eccc0ef5", size = 122148, upload-time = "2025-09-20T07:11:50.826Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ed/b9d3a11cac73444c0a3703200161cd7267dab5ab85fd00e1f965526e74a8/winrt_windows_foundation-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3be7fbae829b98a6a946db4fbaf356b11db1fbcbb5d4f37e7a73ac6b25de8b87", size = 114360, upload-time = "2025-09-20T07:11:51.626Z" }, +] + +[[package]] +name = "winrt-windows-foundation-collections" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/62/d21e3f1eeb8d47077887bbf0c3882c49277a84d8f98f7c12bda64d498a07/winrt_windows_foundation_collections-3.2.1.tar.gz", hash = "sha256:0eff1ad0d8d763ad17e9e7bbd0c26a62b27215016393c05b09b046d6503ae6d5", size = 16043, upload-time = "2025-06-06T14:41:53.983Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/0b/7802349391466d3f7e8f62f588f36a1a0b6560abfcdbdaa426fe21d322b4/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win32.whl", hash = "sha256:15704eef3125788f846f269cf54a3d89656fa09a1dc8428b70871f717d595ad6", size = 60060, upload-time = "2025-06-06T07:11:16.173Z" }, + { url = "https://files.pythonhosted.org/packages/37/94/5b888713e472746635a382e523513ab1b8200af55c5b56bc70e1e4369115/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:550dfb8c82fe74d9e0728a2a16a9175cc9e34ca2b8ef758d69b2a398894b698b", size = 69058, upload-time = "2025-06-06T07:11:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/829273622c9b37c67b97f187b92be318404f7d33db045e31d72b7d50f54c/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:810ad4bd11ab4a74fdbcd3ed33b597ef7c0b03af73fc9d7986c22bcf3bd24f84", size = 58793, upload-time = "2025-06-06T07:11:17.837Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cd/99ef050d80bea2922fa1ded93e5c250732634095d8bd3595dd808083e5ca/winrt_windows_foundation_collections-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4267a711b63476d36d39227883aeb3fb19ac92b88a9fc9973e66fbce1fd4aed9", size = 60063, upload-time = "2025-06-06T07:11:18.65Z" }, + { url = "https://files.pythonhosted.org/packages/94/93/4f75fd6a4c96f1e9bee198c5dc9a9b57e87a9c38117e1b5e423401886353/winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:5e12a6e75036ee90484c33e204b85fb6785fcc9e7c8066ad65097301f48cdd10", size = 69057, upload-time = "2025-06-06T07:11:19.446Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/de47ccc390017ec5575e7e7fd9f659ee3747c52049cdb2969b1b538ce947/winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:34b556255562f1b36d07fba933c2bcd9f0db167fa96727a6cbb4717b152ad7a2", size = 58792, upload-time = "2025-06-06T07:11:20.24Z" }, + { url = "https://files.pythonhosted.org/packages/e1/47/b3301d964422d4611c181348149a7c5956a2a76e6339de451a000d4ae8e7/winrt_windows_foundation_collections-3.2.1-cp314-cp314-win32.whl", hash = "sha256:33188ed2d63e844c8adfbb82d1d3d461d64aaf78d225ce9c5930421b413c45ab", size = 62211, upload-time = "2025-09-20T07:11:52.411Z" }, + { url = "https://files.pythonhosted.org/packages/20/59/5f2c940ff606297129e93ebd6030c813e6a43a786de7fc33ccb268e0b06b/winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d4cfece7e9c0ead2941e55a1da82f20d2b9c8003bb7a8853bb7f999b539f80a4", size = 70399, upload-time = "2025-09-20T07:11:53.254Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2d/2c8eb89062c71d4be73d618457ed68e7e2ba29a660ac26349d44fc121cbf/winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3884146fea13727510458f6a14040b7632d5d90127028b9bfd503c6c655d0c01", size = 61392, upload-time = "2025-09-20T07:11:53.993Z" }, +] + +[[package]] +name = "winrt-windows-storage-streams" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/50/f4488b07281566e3850fcae1021f0285c9653992f60a915e15567047db63/winrt_windows_storage_streams-3.2.1.tar.gz", hash = "sha256:476f522722751eb0b571bc7802d85a82a3cae8b1cce66061e6e758f525e7b80f", size = 34335, upload-time = "2025-06-06T14:43:23.905Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/00/0e0da784245c93cf346150ab67634177bf277f93b7a162bb56c928c39c04/voluptuous-0.16.0-py3-none-any.whl", hash = "sha256:ee342095263e1b5afbd4d418cb5adc92810eebfd07696bb033a261210df33db4", size = 31931, upload-time = "2025-12-18T23:18:44.694Z" }, + { url = "https://files.pythonhosted.org/packages/87/e7/7d3f2a4a442f264e05cab2bdf20ed1b95cb3f753bd1b0f277f2b49fb8335/winrt_windows_storage_streams-3.2.1-cp312-cp312-win32.whl", hash = "sha256:77c1f0e004b84347b5bd705e8f0fc63be8cd29a6093be13f1d0869d0d97b7d78", size = 127787, upload-time = "2025-06-06T14:02:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2f/cc36f475f8af293f40e2c2a5d6c2e75a189c2c2d4d01ecb3551578518c79/winrt_windows_storage_streams-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4508ee135af53e4fc142876abbf4bc7c2a95edfc7d19f52b291a8499cacd6dc", size = 131849, upload-time = "2025-06-06T14:02:03.09Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/896fb734f7456910ec412f3f3adfdc3f0dc3134864a496d5b120592f3bfd/winrt_windows_storage_streams-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:040cb94e6fb26b0d00a00e8b88b06fadf29dfe18cf24ed6cb3e69709c3613307", size = 128144, upload-time = "2025-06-06T14:02:03.946Z" }, + { url = "https://files.pythonhosted.org/packages/d9/d2/24d9f59bdc05e741261d5bec3bcea9a848d57714126a263df840e2b515a8/winrt_windows_storage_streams-3.2.1-cp313-cp313-win32.whl", hash = "sha256:401bb44371720dc43bd1e78662615a2124372e7d5d9d65dfa8f77877bbcb8163", size = 127774, upload-time = "2025-06-06T14:02:04.752Z" }, + { url = "https://files.pythonhosted.org/packages/15/59/601724453b885265c7779d5f8025b043a68447cbc64ceb9149d674d5b724/winrt_windows_storage_streams-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:202c5875606398b8bfaa2a290831458bb55f2196a39c1d4e5fa88a03d65ef915", size = 131827, upload-time = "2025-06-06T14:02:05.601Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c2/a419675a6087c9ea496968c9b7805ef234afa585b7483e2269608a12b044/winrt_windows_storage_streams-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ca3c5ec0aab60895006bf61053a1aca6418bc7f9a27a34791ba3443b789d230d", size = 128180, upload-time = "2025-06-06T14:02:06.759Z" }, + { url = "https://files.pythonhosted.org/packages/55/70/2869ea2112c565caace73c9301afd1d7afcc49bdd37fac058f0178ba95d4/winrt_windows_storage_streams-3.2.1-cp314-cp314-win32.whl", hash = "sha256:5cd0dbad86fcc860366f6515fce97177b7eaa7069da261057be4813819ba37ee", size = 131701, upload-time = "2025-09-20T07:17:16.849Z" }, + { url = "https://files.pythonhosted.org/packages/f4/3d/aae50b1d0e37b5a61055759aedd42c6c99d7c17ab8c3e568ab33c0288938/winrt_windows_storage_streams-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3c5bf41d725369b9986e6d64bad7079372b95c329897d684f955d7028c7f27a0", size = 135566, upload-time = "2025-09-20T07:17:17.69Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c3/6d3ce7a58e6c828e0795c9db8790d0593dd7fdf296e513c999150deb98d4/winrt_windows_storage_streams-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:293e09825559d0929bbe5de01e1e115f7a6283d8996ab55652e5af365f032987", size = 134393, upload-time = "2025-09-20T07:17:18.802Z" }, ] [[package]] name = "yarl" -version = "1.23.0" +version = "1.9.7" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and python_full_version < '3.14.2'", + "python_full_version >= '3.13.2' and python_full_version < '3.14'", + "python_full_version < '3.13.2'", +] dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, + { name = "idna", marker = "python_full_version < '3.14.2'" }, + { name = "multidict", marker = "python_full_version < '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/50/dcf6d0ea0da893b23f73ea5b21fa1f96fd45e9cb4404cc6b665368b4ab19/yarl-1.9.7.tar.gz", hash = "sha256:f28e602edeeec01fc96daf7728e8052bc2e12a672e2a138561a1ebaf30fd9df7", size = 153261, upload-time = "2024-09-01T20:06:03.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/4e/b3d7679b158a981e6fa36c1d4388a7c3f4adb1b5c33ec22708ec550ddf91/yarl-1.9.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d8cf3d0b67996edc11957aece3fbce4c224d0451c7c3d6154ec3a35d0e55f6b", size = 188957, upload-time = "2024-09-01T20:04:02.548Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7b/2fe90636cf0f745210bcb79347369a3882e829e1070ab7d8b3949684d209/yarl-1.9.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a7748cd66fef49c877e59503e0cc76179caf1158d1080228e67e1db14554f08", size = 113292, upload-time = "2024-09-01T20:04:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ee/fae90e40bb4c2af6fd8a1a50d052140101a6634f1d2b32596a6cf53f4244/yarl-1.9.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a6fa3aeca8efabb0fbbb3b15e0956b0cb77f7d9db67c107503c30af07cd9e00", size = 110974, upload-time = "2024-09-01T20:04:06.392Z" }, + { url = "https://files.pythonhosted.org/packages/28/60/3e985358440d6467c2ea81673000aef762c448462ab88e98e6676845f24f/yarl-1.9.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf37dd0008e5ac5c3880198976063c491b6a15b288d150d12833248cf2003acb", size = 504133, upload-time = "2024-09-01T20:04:07.96Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ae/f0730026d7011f5403a8f49fec4e666358db43a0339dc8259b19a7c2e6f1/yarl-1.9.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87aa5308482f248f8c3bd9311cd6c7dfd98ea1a8e57e35fb11e4adcac3066003", size = 521269, upload-time = "2024-09-01T20:04:10.629Z" }, + { url = "https://files.pythonhosted.org/packages/24/5d/1b982866e45906f236cb9a93ec9a07a5b61854b34f0f6fa368056ee9cda3/yarl-1.9.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:867b13c1b361f9ba5d2f84dc5408082f5d744c83f66de45edc2b96793a9c5e48", size = 518617, upload-time = "2024-09-01T20:04:12.76Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ec/eaab7e272ddf1eab39b793e5cd3af304ac28d9342f6a3f2e356276bcc4fe/yarl-1.9.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48ce93947554c2c85fe97fc4866646ec90840bc1162e4db349b37d692a811755", size = 510893, upload-time = "2024-09-01T20:04:14.801Z" }, + { url = "https://files.pythonhosted.org/packages/98/c3/ed093752106c61e3b2a108f798649cb24119484802bb5ca521a36cf559bd/yarl-1.9.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcd3d94b848cba132f39a5b40d80b0847d001a91a6f35a2204505cdd46afe1b2", size = 487621, upload-time = "2024-09-01T20:04:17.327Z" }, + { url = "https://files.pythonhosted.org/packages/71/ff/bce0bda27957d4f8cdb8e56b807f185683e8b6a3717637fb8d1faa39269d/yarl-1.9.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d06d6a8f98dd87646d98f0c468be14b201e47ec6092ad569adf835810ad0dffb", size = 506332, upload-time = "2024-09-01T20:04:19.14Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b5/95702c9719808331d2401e13660af86b323139f0293feb3a44698a194439/yarl-1.9.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:91567ff4fce73d2e7ac67ed5983ad26ba2343bc28cb22e1e1184a9677df98d7c", size = 505515, upload-time = "2024-09-01T20:04:21.194Z" }, + { url = "https://files.pythonhosted.org/packages/17/7d/74a41e5d49329be134602a7e840adf3a499c7562afb982282d079067d5e4/yarl-1.9.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d5594512541e63188fea640b7f066c218d2176203d6e6f82abf702ae3dca3b2", size = 528662, upload-time = "2024-09-01T20:04:23.301Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f9/8a9083b6b73944c0bb5c99cfc0edf3bec14456b621f76b338fc945afc69f/yarl-1.9.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c2743e43183e4afbb07d5605693299b8756baff0b086c25236c761feb0e3c56", size = 539801, upload-time = "2024-09-01T20:04:25.04Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/877076885263c214abbed93462ef2e4e95579c047d188530c849ea207846/yarl-1.9.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:daa69a3a2204355af39f4cfe7f3870d87c53d77a597b5100b97e3faa9460428b", size = 524783, upload-time = "2024-09-01T20:04:26.929Z" }, + { url = "https://files.pythonhosted.org/packages/95/58/e509c4ad1460bce6cf5cd485c5baa5c4c6a9a53999a82f90462f7908ee26/yarl-1.9.7-cp312-cp312-win32.whl", hash = "sha256:36b16884336c15adf79a4bf1d592e0c1ffdb036a760e36a1361565b66785ec6c", size = 98822, upload-time = "2024-09-01T20:04:29.075Z" }, + { url = "https://files.pythonhosted.org/packages/b0/71/c8136c8c240ccf9d38715aaad31fb4f2c2f14e83c6db6b83d389274b0e9e/yarl-1.9.7-cp312-cp312-win_amd64.whl", hash = "sha256:2ead2f87a1174963cc406d18ac93d731fbb190633d3995fa052d10cefae69ed8", size = 108657, upload-time = "2024-09-01T20:04:31.244Z" }, + { url = "https://files.pythonhosted.org/packages/2b/3c/c159233854485307e3355af11099d9c351c8475b10b2b3dc64bb8cdc608b/yarl-1.9.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:808eddabcb6f7b2cdb6929b3e021ac824a2c07dc7bc83f7618e18438b1b65781", size = 185789, upload-time = "2024-09-01T20:04:32.832Z" }, + { url = "https://files.pythonhosted.org/packages/89/f3/24c3b30a9d95827280130ecb6ef33f0ab2bdc690391f19178493cf149196/yarl-1.9.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:395ab0d8ce6d104a988da429bcbfd445e03fb4c911148dfd523f69d13f772e47", size = 111661, upload-time = "2024-09-01T20:04:34.669Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a1/e610bfb3c74efdbeeff19ee370e6a76dd552c66680a9180777828dc2e7fa/yarl-1.9.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:49827dfccbd59c4499605c13805e947349295466e490860a855b7c7e82ec9c75", size = 109560, upload-time = "2024-09-01T20:04:36.165Z" }, + { url = "https://files.pythonhosted.org/packages/d0/aa/dc3657bcf79cd98bdfa03c1b85c88de2a36037171894fa56890146e08615/yarl-1.9.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b8bbdd425d0978311520ea99fb6c0e9e04e64aee84fac05f3157ace9f81b05", size = 485985, upload-time = "2024-09-01T20:04:37.84Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2a/a69ad3ae4facef03df228790e6f4cfd4971cc267ee140fc8f6331c7e6194/yarl-1.9.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71d33fd1c219b5b28ee98cd76da0c9398a4ed4792fd75c94135237db05ba5ca8", size = 501335, upload-time = "2024-09-01T20:04:39.697Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8f/b0a35ecd3f31fdffa704d11bf452a277ce4b29e7b878a94e636349945d87/yarl-1.9.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62440431741d0b7d410e5cbad800885e3289048140a43390ecab4f0b96dde3bb", size = 502295, upload-time = "2024-09-01T20:04:42.239Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/bfc953df3a6ee6c0c9cd9f84d488681e377e49b8ced6d2d5b9289d639c89/yarl-1.9.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db97210433366dfba55590e48285b89ad0146c52bf248dd0da492dd9f0f72cf", size = 493550, upload-time = "2024-09-01T20:04:44.099Z" }, + { url = "https://files.pythonhosted.org/packages/40/5d/5092b93da54659f1e737f95d9a554f79aa68d1fda05e26c9f0e86184d894/yarl-1.9.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:653597b615809f2e5f4dba6cd805608b6fd3597128361a22cc612cf7c7a4d1bf", size = 470835, upload-time = "2024-09-01T20:04:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/66/e6/dedce99c469f8d1c66432006910adf943fdb7d0cfb77f026030e234f234f/yarl-1.9.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df47612129e66f7ce7c9994d4cd4e6852f6e3bf97699375d86991481796eeec8", size = 490437, upload-time = "2024-09-01T20:04:49.069Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/b36cd8d9356734fda4a668ce358f56ecb16c7171b8a658fdfde31476de7c/yarl-1.9.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5e338b6febbae6c9fe86924bac3ea9c1944e33255c249543cd82a4af6df6047b", size = 492765, upload-time = "2024-09-01T20:04:50.795Z" }, + { url = "https://files.pythonhosted.org/packages/aa/9e/6ad4300fc040fc34e323f1254a05886a6441d05bd251a9a4063ed8d35c32/yarl-1.9.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e649d37d04665dddb90994bbf0034331b6c14144cc6f3fbce400dc5f28dc05b7", size = 508901, upload-time = "2024-09-01T20:04:52.523Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/8b27ea0a0faaba43b389c3a170c25a1fc063c34ae41c8660055e47d5dc89/yarl-1.9.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0a1b8fd849567be56342e988e72c9d28bd3c77b9296c38b9b42d2fe4813c9d3f", size = 519676, upload-time = "2024-09-01T20:04:54.434Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e6/1b88c9b952c69b4bfb5d38260de4bf65eab4d0787bfcdc0a4d680e084ccb/yarl-1.9.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9d715b2175dff9a49c6dafdc2ab3f04850ba2f3d4a77f69a5a1786b057a9d45", size = 510005, upload-time = "2024-09-01T20:04:56.275Z" }, + { url = "https://files.pythonhosted.org/packages/72/81/c456d5060bf4c2cb1213cd71e9211e0859ce6fff444bd13c61e2ae681b64/yarl-1.9.7-cp313-cp313-win32.whl", hash = "sha256:bc9233638b07c2e4a3a14bef70f53983389bffa9e8cb90a2da3f67ac9c5e1842", size = 483263, upload-time = "2024-09-01T20:04:58.024Z" }, + { url = "https://files.pythonhosted.org/packages/5d/08/fe455390603d0377140c1ef02287dd32d3d4a0a6d596aa4a1fc881ac68d2/yarl-1.9.7-cp313-cp313-win_amd64.whl", hash = "sha256:62e110772330d7116f91e79cd83fef92545cb2f36414c95881477aa01971f75f", size = 491236, upload-time = "2024-09-01T20:04:59.848Z" }, + { url = "https://files.pythonhosted.org/packages/48/04/8cc40203453e4bce05cd3e9a5bea930ac0086aa4848a9c41aa1da13ae1a0/yarl-1.9.7-py3-none-any.whl", hash = "sha256:49935cc51d272264358962d050d726c3e5603a616f53e52ea88e9df1728aa2ee", size = 35402, upload-time = "2024-09-01T20:06:02.126Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.14.2' and python_full_version < '3.15'", +] +dependencies = [ + { name = "idna", marker = "python_full_version >= '3.14.2'" }, + { name = "multidict", marker = "python_full_version >= '3.14.2'" }, + { name = "propcache", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] + +[[package]] +name = "zeroconf" +version = "0.149.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ifaddr", marker = "python_full_version >= '3.14.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/34/c981e760690f7b7dc91532d4d4ad21e3922887aaa425a0e7bff8067152da/zeroconf-0.149.16.tar.gz", hash = "sha256:5e6b5a3b153c2cc2a8d9e6f6f189ec5638f7d9c86fc3e88a6c53eb6863761a5e", size = 196586, upload-time = "2026-05-21T14:04:17.781Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/3c/a34998e51b9923ce7446c06016df1dca4dce3fe9074d7673678dfb939fb7/zeroconf-0.149.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e1d403ca6145268664af9d87a7522f9ad7e5e388482db0d4b055553616564087", size = 1676206, upload-time = "2026-05-21T14:31:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/6b/24/07eaf020c2f206dfd35378db721e81e9f48792d8f6297663763ca9739157/zeroconf-0.149.16-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50c9ec1fb46e336ca7b098d44cae5fc21baf9d44cd758b776c06b453b2d3d73e", size = 2076988, upload-time = "2026-05-21T14:32:00.114Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/3ea6bbd4151e2afc9249fa6c345c68f5fffafcb608a0147b3a4e49df75d3/zeroconf-0.149.16-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95b8681ab0a3209d04debc96818998bcc86642be32a17b17f6ecda5d7ff560f9", size = 1879896, upload-time = "2026-05-21T14:32:02.172Z" }, + { url = "https://files.pythonhosted.org/packages/ad/21/9dd0d3a9587155b7efd10d5f5484df836c2ec2b54ae719ef1c972a9a90a9/zeroconf-0.149.16-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:65b312685ef3e12284f907c07217df3c6873438313ce0e6cf53e01a182aaf0c4", size = 2175116, upload-time = "2026-05-21T14:32:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/63/1c/452e418d4fadb10118ea381056099714eec5cec42d6fbee7f28eeebb41d2/zeroconf-0.149.16-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ca881fbf6edd21651b247710cdaa7b72593982bb65433bcf80c509df9550d12", size = 2110395, upload-time = "2026-05-21T14:32:06.524Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0f/1883b7bed80fd33627c802593b49377e2aa0a458c430ebc3710cfce062ac/zeroconf-0.149.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ea9acdb58407bb73c11cad6d4965bdc01bec3c2284727c4a482d444f943092c", size = 2101220, upload-time = "2026-05-21T14:32:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d6/460c6a4ebbd350935073fb7501b38d143791e8478d737934cae296fffc4a/zeroconf-0.149.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cf3e109ec0181bab93ac954b17f2921eebac28a97c81ebf3559aeaad7a3b7a67", size = 1969601, upload-time = "2026-05-21T14:32:10.435Z" }, + { url = "https://files.pythonhosted.org/packages/4e/7e/f4bdad2ba7623e131907db61ce436bdb3bbef7e08917448170b87aa11446/zeroconf-0.149.16-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4fd08c930004ffaad2dc192762f9a2cd7f1db34b5fb1a36f38993dde2c4d55d9", size = 2114226, upload-time = "2026-05-21T14:32:12.331Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fd/7b5c0244c8db96e79c355ebb48865663ada68de096d02d09e743861b1662/zeroconf-0.149.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a46c8fc8a4133574484b24b6bef3ae46e7e0958167d2c44ee84cc2edff2b4c9f", size = 2196367, upload-time = "2026-05-21T14:32:14.314Z" }, + { url = "https://files.pythonhosted.org/packages/3b/42/ba94573fa62df4e623d7e7a0ca68e2aab644c729e83c64117da39ef7f9a3/zeroconf-0.149.16-cp312-cp312-win32.whl", hash = "sha256:48e0847568b35d3ccce0eaf0546313d3f35541f794f244097e6c8e80e75ec78c", size = 1272101, upload-time = "2026-05-21T14:32:16.168Z" }, + { url = "https://files.pythonhosted.org/packages/03/bb/f829895ed725d58e5891632e7b2c507b4f88508d2d301b57452ce57190ab/zeroconf-0.149.16-cp312-cp312-win_amd64.whl", hash = "sha256:813c9f0223c97d67970b4013cedeea072a28c0809962d58750d00d502066d2d0", size = 1508797, upload-time = "2026-05-21T14:32:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/ebaa279ecdb453c7b09d4ee20fef74d8684e2d9b76ea5b6fca7bb39e0d73/zeroconf-0.149.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:575a548bdc820223e2360fdcf56fb1eea4d8345f286c441c3ef1cd8f68d853f1", size = 1662398, upload-time = "2026-05-21T14:32:19.878Z" }, + { url = "https://files.pythonhosted.org/packages/17/db/18b63b074b966f6374d03d8cd62a5276ab8796021dc798bf79c4723a04ab/zeroconf-0.149.16-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58efbb338f1ac44b62cfe92999e4476c2051288729de2f6ba8939c977acdd0c5", size = 2070137, upload-time = "2026-05-21T14:32:21.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/55/3f0803f6c6c006761b236117e440e00815c366568ffead49a99a83b10295/zeroconf-0.149.16-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a14610cc2eb90e1530e042183c9ea6981260cdcb97c4ecc7b11ca3749a433664", size = 1875696, upload-time = "2026-05-21T14:32:23.686Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9e/8a7eef9c9c4af9f5a90e95703db1adbfc932453d01679f719f84323bcf0a/zeroconf-0.149.16-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6cb2873ae74265e28756f83454d1fde53eaaed9bc380bb43ccb2ac42b4b5a5a", size = 2169690, upload-time = "2026-05-21T14:32:25.558Z" }, + { url = "https://files.pythonhosted.org/packages/6d/dc/0bd920ece38ca90053d330dee74a1f01089cb0eb7bc7632cf7e87f412311/zeroconf-0.149.16-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:74fedb7c4a073cdc410770e54c27e8c87072c5a4e68eb8d680c0111c84364c0d", size = 2106805, upload-time = "2026-05-21T14:32:27.821Z" }, + { url = "https://files.pythonhosted.org/packages/b5/14/26ce98ef1420aa46a5b02ef24349cf41218313726481949d67807213e218/zeroconf-0.149.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b76b7228db26895090ff0e156ef8e193039e971c443d3c14fa8589f08bae2c6e", size = 2100414, upload-time = "2026-05-21T14:32:29.67Z" }, + { url = "https://files.pythonhosted.org/packages/1e/aa/3a2a09a4f2699ddb7506ef0b802d41b6858d09224c42a9b672ea9db8a086/zeroconf-0.149.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cd4b56d4951d62a6e7629286877c123703ac27b2a35e7744a63cc860ee3aaf39", size = 1963115, upload-time = "2026-05-21T14:32:31.448Z" }, + { url = "https://files.pythonhosted.org/packages/24/d9/74f5e6f4530dfb615f3ecc1d23c4ac72b6c86b1bdd4a6fe9dab15466f609/zeroconf-0.149.16-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7c1003a992ef65a10a29194630a58c2bfd4a87b9117a161c2ad9a1773d8191e1", size = 2117608, upload-time = "2026-05-21T14:32:33.41Z" }, + { url = "https://files.pythonhosted.org/packages/15/69/cb4ed1b97ab3a4f4d8a328439c2050211f6826cefec2d0bd6e0bcd59b4bd/zeroconf-0.149.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:63d48d9ed65f2bf05d237fc42773dab74d8ddf6d241c88e65297769c5ba87931", size = 2193839, upload-time = "2026-05-21T14:32:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/be/d9/064323d0b9ba42e93c9afd9db3aa6d14c7ddbfe07dc242d897c5f9abf7b5/zeroconf-0.149.16-cp313-cp313-win32.whl", hash = "sha256:9dbff444474354460f19b592b1f57b72688f23455d8f208f616ca49e8ecbc4ef", size = 1269059, upload-time = "2026-05-21T14:32:37.584Z" }, + { url = "https://files.pythonhosted.org/packages/11/77/c15f97a2e3e373bd6e1431238028700f5537667e502d7fa0b218619ac6ad/zeroconf-0.149.16-cp313-cp313-win_amd64.whl", hash = "sha256:8f669e6aed52feaf73316a6205eff09696bfddce3e4f39fbc2c6e28500366dda", size = 1505791, upload-time = "2026-05-21T14:32:39.903Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d8/6524389c593f6963224cc0604bdb0424f97a7baa0417690120a652bfbba5/zeroconf-0.149.16-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:379ee7aadefffa2be227a501025356ef9685397a38b471df42032c30eff9e54d", size = 1678965, upload-time = "2026-05-21T14:32:41.797Z" }, + { url = "https://files.pythonhosted.org/packages/09/17/a7c58cb389ee7f344a30014ba16c2badbf85eb4cc0ae5c89bedd74070561/zeroconf-0.149.16-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb7fb399b475ec1d18cfef70ffce89bd168a37f3f5db9387ce37d7a180baa678", size = 2095711, upload-time = "2026-05-21T14:32:43.718Z" }, + { url = "https://files.pythonhosted.org/packages/92/3a/de0fff96e52cc04441f6194aa3db2b8e4e886b93b5c5ece370db5e921f14/zeroconf-0.149.16-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:57029dca8e6c9be83bd94130d41f6e1c2ce8b3f4678480b27bf24aa2053ade24", size = 1852716, upload-time = "2026-05-21T14:32:45.675Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/d067e60f39ceee21e3c12b1ed7181743001b46c331b3657717d7b0f8eab2/zeroconf-0.149.16-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fa4576c6d9b029ea425c2fffa586dd5d25295ec78d28b219f35e9a2d6786cb7f", size = 2182010, upload-time = "2026-05-21T14:32:47.845Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1e/6e7d8b87d59b6cb81127377aa8a8008c3dabe3f12d7fc0b709ef29bfcc86/zeroconf-0.149.16-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d58bd30d137402f101b1b7e5c7ab508632e101007fea38f3b3907e983cd32789", size = 2117823, upload-time = "2026-05-21T14:32:49.768Z" }, + { url = "https://files.pythonhosted.org/packages/22/53/d5ca316708c3fa1c81423b88aa778f9d19e8bd48ca8dc3d035e53f687844/zeroconf-0.149.16-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:1559b089d58e47a057a68cfe36e9910acc01d500b2d51ae410a38dbda5d66230", size = 2178304, upload-time = "2026-05-21T14:04:14.129Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8d/1d139ddf2c9e8484c14a5cf5dd08033513a894e061843fe64ffee9c30f64/zeroconf-0.149.16-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ddf6db171e2a44dda0cb68956c50e165a6f106b0f4ec480575ff2ef91134dfef", size = 2126907, upload-time = "2026-05-21T14:32:52.089Z" }, + { url = "https://files.pythonhosted.org/packages/39/a6/e85aae306295b7bdd66e53f01fe62c18da6fd9a93cb89033950c52b536b9/zeroconf-0.149.16-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5d8308cad249e52fc7466c2a9315438d42e9a142c5c33f622b60601d9f0e7ef7", size = 1934204, upload-time = "2026-05-21T14:32:54.064Z" }, + { url = "https://files.pythonhosted.org/packages/07/f1/26b033222d669d7e96a3a1b2cdb252905686288d9071ea96716b8899d548/zeroconf-0.149.16-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:56a14eb52d65c683a180f91d007c87ff7922a5f6e839dd1fb74d81675d783111", size = 2127389, upload-time = "2026-05-21T14:32:56.182Z" }, + { url = "https://files.pythonhosted.org/packages/70/92/b57d3569ad362f57ab9dd1f5a48c22c2a81fe0dedf5e31c92b7853b109d4/zeroconf-0.149.16-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f817bf1b0319885882a208511249e5e2fe5ceb02fe3a9060ce295f064d13c3bd", size = 2204573, upload-time = "2026-05-21T14:32:58.303Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7c/35d5ac05eaed8de2f39af47ea9501244a98ffe2a70b1ddfac04d06a1b070/zeroconf-0.149.16-cp314-cp314-win32.whl", hash = "sha256:aad4b1941354bf7d0b4005b22495e6fc99218e37c9ebf0f556da9cf1ac94060f", size = 1301124, upload-time = "2026-05-21T14:33:00.147Z" }, + { url = "https://files.pythonhosted.org/packages/09/64/ee0c34a953c474a2c512252cd2bfd5932882afbc9519adceb3102d1c4261/zeroconf-0.149.16-cp314-cp314-win_amd64.whl", hash = "sha256:f0814028202457aac116f4315c8eff407b77aa0b441b0f27a60ac83699a04641", size = 1544411, upload-time = "2026-05-21T14:33:02.193Z" }, + { url = "https://files.pythonhosted.org/packages/a5/4b/e8e2208c874b381264b21bac29aef50212c5e357988ff009200c7ed32074/zeroconf-0.149.16-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:35301c63f8dd0328e1e78fc8444cdc39bd472e6cda2570dd185b78126f576c41", size = 3354872, upload-time = "2026-05-21T14:33:04.485Z" }, + { url = "https://files.pythonhosted.org/packages/7a/73/b6b017f67d745c37c1ebc4cde3c51ff10edcd88adf7a6924c21105b175bd/zeroconf-0.149.16-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2bdd9052521a7795d6386021b1346a8f87ec66da6a917d904a327e947a6c87ce", size = 4010851, upload-time = "2026-05-21T14:33:06.692Z" }, + { url = "https://files.pythonhosted.org/packages/69/5b/9e5e15e5b0428a25b768348cdec870d533b0a54fe6f0e773630329fbd159/zeroconf-0.149.16-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e77d9235a33a79ebe57fa711fe5a1f9a225e64b2964ab329d320d22cdcf1486", size = 3556413, upload-time = "2026-05-21T14:33:09.547Z" }, + { url = "https://files.pythonhosted.org/packages/ef/24/8d8a89e2775a86e4bd1501ff91db689320bcfc7b93f725d8530f42211cfa/zeroconf-0.149.16-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:79cb12a3a189cf20a5903af3a76fc0a12d32b44a1e75391c0c4875d82d959843", size = 4166790, upload-time = "2026-05-21T14:33:12.107Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f3/7c8f1d999d37d8c6f186cad6b5eacfc270fadb3412bf6dcd354e1696e4fc/zeroconf-0.149.16-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:752942a1a803cc4aa1dc83227d0ce408912b8c5f7b22db0d9ee9b6541cb1f32b", size = 4039796, upload-time = "2026-05-21T14:33:14.288Z" }, + { url = "https://files.pythonhosted.org/packages/78/0c/f0d563de36cb4c6b7c68cbe588a0ffe129a12f1b59d880b6216dde967f05/zeroconf-0.149.16-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:37b93d889804f029d782059f77d22073b7fcff055a3e48f414164e2a79dd758b", size = 4075206, upload-time = "2026-05-21T14:33:16.623Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/df2cb77eefde839cd095f8262a41044be851130dce5ed84125896dd5e9b4/zeroconf-0.149.16-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:9aa9ba48ceec11f5b768a81335409672eff294d7ac9c6ac5dcbcc1b0d3538a49", size = 3707566, upload-time = "2026-05-21T14:33:18.79Z" }, + { url = "https://files.pythonhosted.org/packages/34/59/d76eabba71dca1cb17ad806ed79ad9c96f5150304cf4cfc532a9db30edcf/zeroconf-0.149.16-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2f5a409d2df93e02137d4cd86c6bcab5636ee4b80bc40963eb1ece32a631c848", size = 4065727, upload-time = "2026-05-21T14:33:21.374Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9f/a830a10b69844cc3f3e5bb82a1622b9757e627c5d68236f58ebbfe6c16b1/zeroconf-0.149.16-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25ace9939767ed7bda7da6d08be50ba3ad90a9055ddbc28288d5c8d080a7c7c3", size = 4215403, upload-time = "2026-05-21T14:33:23.571Z" }, + { url = "https://files.pythonhosted.org/packages/0e/74/0f52fd22323e7142dc9a72b5f46ba6e8170835a5d7fc15325ab98f574569/zeroconf-0.149.16-cp314-cp314t-win32.whl", hash = "sha256:f8445c04cdb544a3aa58e5e30cb54dfd4c1cb82ab6389e7155872fa1c7ba7c0f", size = 2595104, upload-time = "2026-05-21T14:33:25.958Z" }, + { url = "https://files.pythonhosted.org/packages/8e/fd/f44626de06585216091baef6da5c56ea834f36de4ec754e2a2a79b659987/zeroconf-0.149.16-cp314-cp314t-win_amd64.whl", hash = "sha256:fc77736d1b8c22c1ad2c13d48aab0374a75f8aa1a21bae075478eb1c54a2c1bf", size = 3103203, upload-time = "2026-05-21T14:33:28.109Z" }, ] [[package]] name = "zha" source = { editable = "." } dependencies = [ + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "bellows" }, + { name = "frozendict" }, { name = "zha-quirks" }, { name = "zigpy" }, { name = "zigpy-deconz" }, @@ -1650,6 +4827,8 @@ dependencies = [ [package.optional-dependencies] testing = [ { name = "coloredlogs" }, + { name = "homeassistant", version = "2024.9.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "homeassistant", version = "2026.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "pytest" }, { name = "python-slugify" }, ] @@ -1696,12 +4875,15 @@ testing = [ [package.metadata] requires-dist = [ + { name = "attrs" }, { name = "bellows", specifier = "==0.49.2" }, { name = "coloredlogs", marker = "extra == 'testing'" }, + { name = "frozendict" }, + { name = "homeassistant", marker = "extra == 'testing'" }, { name = "pytest", marker = "extra == 'testing'" }, { name = "python-slugify", marker = "extra == 'testing'" }, - { name = "zha-quirks", specifier = "==1.2.0" }, - { name = "zigpy", specifier = "==1.5.1" }, + { name = "zha-quirks", specifier = ">=1.2.0" }, + { name = "zigpy", editable = "../zigpy" }, { name = "zigpy-deconz", specifier = "==0.25.5" }, { name = "zigpy-xbee", specifier = "==0.21.1" }, { name = "zigpy-zigate", specifier = "==0.14.0" }, @@ -1763,23 +4945,73 @@ wheels = [ [[package]] name = "zigpy" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } +source = { editable = "../zigpy" } dependencies = [ - { name = "aiohttp" }, + { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "aiosqlite" }, + { name = "attrs", version = "23.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "crccheck" }, + { name = "cryptography", version = "43.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "frozendict" }, + { name = "jsonschema" }, + { name = "serialx" }, + { name = "typing-extensions" }, + { name = "voluptuous" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp" }, + { name = "aiosqlite", specifier = ">=0.20.0,<0.22.0" }, { name = "attrs" }, { name = "crccheck" }, { name = "cryptography" }, { name = "frozendict" }, { name = "jsonschema" }, - { name = "serialx" }, + { name = "serialx", specifier = ">=1.4.0" }, { name = "typing-extensions" }, { name = "voluptuous" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/b8/384fdea540dd5ad78b4f56e2c74667828156723a741b3dc1ca1e931f8fd8/zigpy-1.5.1.tar.gz", hash = "sha256:c0c7b342be963e174b06dd0df325c36e6fe096881030347313de3c51cfa69c5a", size = 351830, upload-time = "2026-05-27T03:10:06.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/1e/df306ef9b5c2ccb674206471fbe0037d213bf5d0c4b7d2ebefeec0620f8d/zigpy-1.5.1-py3-none-any.whl", hash = "sha256:51c7c5ea824d769b2d4409a42e2896bb6919126115fd1287f40463bcf4ab0d3a", size = 261541, upload-time = "2026-05-27T03:10:05.106Z" }, + +[package.metadata.requires-dev] +ci = [ + { name = "aioresponses" }, + { name = "asynctest" }, + { name = "codespell", specifier = "==2.4.1" }, + { name = "coverage", extras = ["toml"] }, + { name = "coveralls" }, + { name = "filelock" }, + { name = "freezegun" }, + { name = "mypy", specifier = "==1.19.1" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-asyncio", specifier = ">=1.3.0" }, + { name = "pytest-cov" }, + { name = "pytest-github-actions-annotate-failures" }, + { name = "pytest-timeout" }, + { name = "pytest-xdist" }, + { name = "ruff", specifier = "==0.14.14" }, + { name = "tomli" }, +] +testing = [ + { name = "aioresponses" }, + { name = "asynctest" }, + { name = "codespell", specifier = "==2.4.1" }, + { name = "coverage", extras = ["toml"] }, + { name = "coveralls" }, + { name = "filelock" }, + { name = "freezegun" }, + { name = "mypy", specifier = "==1.19.1" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-asyncio", specifier = ">=1.3.0" }, + { name = "pytest-cov" }, + { name = "pytest-timeout" }, + { name = "ruff", specifier = "==0.14.14" }, + { name = "tomli" }, ] [[package]] diff --git a/zha/application/discovery.py b/zha/application/discovery.py index 7fb2a47eb..d7cd366a2 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -9,16 +9,6 @@ import logging from typing import TYPE_CHECKING, Any -from zigpy.quirks.v2 import ( - BinarySensorMetadata, - CustomDeviceV2, - NumberMetadata, - SwitchMetadata, - WriteAttributeButtonMetadata, - ZCLCommandButtonMetadata, - ZCLEnumMetadata, - ZCLSensorMetadata, -) from zigpy.zcl import Cluster, ClusterType, ReportingConfig from zha.application import Platform, const as zha_const @@ -48,6 +38,15 @@ update, virtual, ) +from zha.quirks.metadata import ( + BinarySensorMetadata, + NumberMetadata, + SwitchMetadata, + WriteAttributeButtonMetadata, + ZCLCommandButtonMetadata, + ZCLEnumMetadata, + ZCLSensorMetadata, +) from zha.zigbee.group import Group if TYPE_CHECKING: @@ -169,25 +168,8 @@ def discover_group_entities(group: Group) -> Iterator[GroupEntity]: def discover_quirks_v2_entities(device: Device) -> Iterator[PlatformEntity]: - """Discover entities for a ZHA device exposed by quirks v2.""" - _LOGGER.debug( - "Attempting to discover quirks v2 entities for device: %s-%s", - str(device.ieee), - device.name, - ) - - if not isinstance(device.device, CustomDeviceV2): - _LOGGER.debug( - "Device: %s-%s is not a quirks v2 device - skipping " - "discover_quirks_v2_entities", - str(device.ieee), - device.name, - ) - return - - zigpy_device: CustomDeviceV2 = device.device - - if not zigpy_device.exposes_metadata: + """Discover entities for a ZHA device exposed by quirks v2 metadata.""" + if device.quirk_metadata is None or not device.quirk_metadata.entity_metadata: _LOGGER.debug( "Device: %s-%s does not expose any quirks v2 entities", str(device.ieee), @@ -195,20 +177,19 @@ def discover_quirks_v2_entities(device: Device) -> Iterator[PlatformEntity]: ) return - for ( - cluster_details, - entity_metadata_list, - ) in zigpy_device.exposes_metadata.items(): - endpoint_id, cluster_id, cluster_type = cluster_details + for entity_metadata in device.quirk_metadata.entity_metadata: + endpoint_id = entity_metadata.endpoint_id + cluster_id = entity_metadata.cluster_id + cluster_type = entity_metadata.cluster_type if endpoint_id not in device.endpoints: _LOGGER.warning( "Device: %s-%s does not have an endpoint with id: %s - unable to " - "create entity with cluster details: %s", + "create entity with metadata: %s", str(device.ieee), device.name, endpoint_id, - cluster_details, + entity_metadata, ) continue @@ -222,88 +203,82 @@ def discover_quirks_v2_entities(device: Device) -> Iterator[PlatformEntity]: if cluster is None: _LOGGER.warning( "Device: %s-%s does not have a cluster with id: %s - " - "unable to create entity with cluster details: %s", + "unable to create entity with metadata: %s", str(device.ieee), device.name, cluster_id, - cluster_details, + entity_metadata, ) continue - for entity_metadata in entity_metadata_list: - platform = Platform(entity_metadata.entity_platform.value) - metadata_type = type(entity_metadata) - entity_class = QUIRKS_ENTITY_META_TO_ENTITY_CLASS.get( - (platform, metadata_type) - ) - - if entity_class is None: - _LOGGER.warning( - "Device: %s-%s has an entity with details: %s that does not" - " have an entity class mapping - unable to create entity", - str(device.ieee), - device.name, - { - zha_const.CLUSTER_DETAILS: cluster_details, - zha_const.ENTITY_METADATA: entity_metadata, - }, - ) - continue + platform = Platform(entity_metadata.entity_platform.value) + metadata_type = type(entity_metadata) + entity_class = QUIRKS_ENTITY_META_TO_ENTITY_CLASS.get((platform, metadata_type)) - entity = entity_class( - endpoint=endpoint, - device=device, - cluster=cluster, - entity_metadata=entity_metadata, + if entity_class is None: + _LOGGER.warning( + "Device: %s-%s has an entity with details: %s that does not" + " have an entity class mapping - unable to create entity", + str(device.ieee), + device.name, + {zha_const.ENTITY_METADATA: entity_metadata}, ) + continue - # Translate quirks v2 reporting/attribute-init metadata into a - # per-instance cluster config that the cluster_config aggregator - # picks up alongside the entity's normal (class-level) declarations. - if attr_name := getattr(entity_metadata, "attribute_name", None): - rep_conf = getattr(entity_metadata, "reporting_config", None) - if rep_conf is not None: - attr_config = AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=rep_conf.min_interval, - max_interval=rep_conf.max_interval, - reportable_change=rep_conf.reportable_change, - ), - ) - bind = True - else: - attr_config = AttrConfig( - read_on_startup=( - not entity_metadata.attribute_initialized_from_cache - ), - ) - bind = False - - # Keep attr_name as a string here - quirks v2 entities can reference - # attribute names that aren't part of the cluster's attribute schema - # (e.g. manufacturer-specific extensions); aggregation/configure handle - # both name and ZCLAttributeDef. - config = { - cluster.cluster_id: ClusterConfig( - bind=bind, - attributes={attr_name: attr_config}, - ), - } + entity = entity_class( + endpoint=endpoint, + device=device, + cluster=cluster, + entity_metadata=entity_metadata, + ) - if cluster_type is ClusterType.Server: - entity._server_cluster_config = config - else: - entity._client_cluster_config = config + # Translate quirks v2 reporting/attribute-init metadata into a + # per-instance cluster config that the cluster_config aggregator + # picks up alongside the entity's normal (class-level) declarations. + if attr_name := getattr(entity_metadata, "attribute_name", None): + rep_conf = getattr(entity_metadata, "reporting_config", None) + if rep_conf is not None: + attr_config = AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=rep_conf.min_interval, + max_interval=rep_conf.max_interval, + reportable_change=rep_conf.reportable_change, + ), + ) + bind = True + else: + attr_config = AttrConfig( + read_on_startup=( + not entity_metadata.attribute_initialized_from_cache + ), + ) + bind = False + + # Keep attr_name as a string here - quirks v2 entities can reference + # attribute names that aren't part of the cluster's attribute schema + # (e.g. manufacturer-specific extensions); aggregation/configure handle + # both name and ZCLAttributeDef. + config = { + cluster.cluster_id: ClusterConfig( + bind=bind, + attributes={attr_name: attr_config}, + ), + } + + if cluster_type is ClusterType.Server: + entity._server_cluster_config = config + else: + entity._client_cluster_config = config - yield entity + yield entity - _LOGGER.debug( - "'%s' platform -> '%s' using cluster 0x%04x", - platform, - entity_class.__name__, - cluster.cluster_id, - ) + _LOGGER.debug( + "'%s' platform -> '%s' using cluster 0x%04x", + platform, + entity_class.__name__, + cluster.cluster_id, + ) def _is_renamed_cluster(cluster: Cluster) -> bool: diff --git a/zha/application/gateway.py b/zha/application/gateway.py index c23a7fb73..672d0b786 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -9,6 +9,7 @@ from datetime import timedelta from enum import Enum import logging +from pathlib import Path import time from typing import Any, Final, Self, TypeVar, cast @@ -26,7 +27,6 @@ import zigpy.device import zigpy.endpoint import zigpy.group -from zigpy.quirks.v2 import UNBUILT_QUIRK_BUILDERS from zigpy.state import State import zigpy.types as t from zigpy.types.named import EUI64 @@ -55,13 +55,9 @@ gather_with_limited_concurrency, ) from zha.event import EventBase -from zha.zigbee.device import ( - Device, - DeviceInfo, - DeviceStatus, - ExtendedDeviceInfo, - resolve_device, -) +from zha.quirks import DEVICE_REGISTRY, resolve_device +from zha.quirks.v2 import UNBUILT_QUIRK_BUILDERS +from zha.zigbee.device import Device, DeviceInfo, DeviceStatus, ExtendedDeviceInfo from zha.zigbee.group import Group, GroupInfo, GroupMemberReference BLOCK_LOG_TIMEOUT: Final[int] = 60 @@ -241,10 +237,11 @@ async def async_from_config(cls, config: ZHAData) -> Self: UNBUILT_QUIRK_BUILDERS.clear() - await instance.async_add_executor_job( - setup_quirks, - instance.config.config.quirks_configuration.custom_quirks_path, - ) + custom_quirks_path = config.config.quirks_configuration.custom_quirks_path + if custom_quirks_path is not None: + DEVICE_REGISTRY.purge_custom_quirks(Path(custom_quirks_path)) + + await instance.async_add_executor_job(setup_quirks, custom_quirks_path) return instance diff --git a/zha/application/platforms/__init__.py b/zha/application/platforms/__init__.py index 2a04ac1c6..5bbfb2c9e 100644 --- a/zha/application/platforms/__init__.py +++ b/zha/application/platforms/__init__.py @@ -17,7 +17,6 @@ from zigpy.profiles.zha import PROFILE_ID as ZHA_PROFILE_ID from zigpy.profiles.zll import PROFILE_ID as ZLL_PROFILE_ID -from zigpy.quirks.v2 import EntityMetadata from zigpy.types import ClusterId from zigpy.types.named import EUI64 import zigpy.zcl @@ -32,6 +31,7 @@ from zha.mixins import LogMixin if TYPE_CHECKING: + from zha.quirks.metadata import EntityMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint from zha.zigbee.group import Group diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index 663c7daf5..79b5df03f 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -11,7 +11,6 @@ from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT from zigpy.profiles import zha, zll -from zigpy.quirks.v2 import BinarySensorMetadata from zigpy.zcl import ( AttributeReadEvent, AttributeReportedEvent, @@ -49,6 +48,7 @@ from zha.application.platforms.helpers import validate_device_class if TYPE_CHECKING: + from zha.quirks.metadata import BinarySensorMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint diff --git a/zha/application/platforms/button/__init__.py b/zha/application/platforms/button/__init__.py index 12d32a3fd..b5df2cc2d 100644 --- a/zha/application/platforms/button/__init__.py +++ b/zha/application/platforms/button/__init__.py @@ -8,7 +8,6 @@ import logging from typing import TYPE_CHECKING, Any -from zigpy.quirks.v2 import WriteAttributeButtonMetadata, ZCLCommandButtonMetadata from zigpy.zcl.clusters.general import Identify from zha.application import Platform @@ -28,6 +27,10 @@ ) if TYPE_CHECKING: + from zha.quirks.metadata import ( + WriteAttributeButtonMetadata, + ZCLCommandButtonMetadata, + ) from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 0f437e077..cdd1271f0 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -9,7 +9,6 @@ from typing import TYPE_CHECKING, Any from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT -from zigpy.quirks.v2 import NumberMetadata from zigpy.zcl import ( AttributeReadEvent, AttributeReportedEvent, @@ -47,6 +46,8 @@ from zha.units import UnitOfMass, UnitOfTemperature, UnitOfTime if TYPE_CHECKING: + from zha.quirks.metadata import NumberMetadata + pass _LOGGER = logging.getLogger(__name__) diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index 5b747391d..b8195c548 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -20,7 +20,6 @@ from zhaquirks.xiaomi.aqara.magnet_ac01 import OppleCluster as MagnetAC01OppleCluster from zhaquirks.xiaomi.aqara.switch_acn047 import OppleCluster as T2RelayOppleCluster from zigpy import types -from zigpy.quirks.v2 import ZCLEnumMetadata from zigpy.zcl import ( AttributeReadEvent, AttributeReportedEvent, @@ -60,6 +59,7 @@ from zha.application.platforms.siren import AdvancedSiren if TYPE_CHECKING: + from zha.quirks.metadata import ZCLEnumMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index b6f415184..206a7b220 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -17,7 +17,6 @@ from zhaquirks.danfoss import thermostat as danfoss_thermostat from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT, SE_POLL_SUMMATION from zigpy import types -from zigpy.quirks.v2 import ZCLEnumMetadata, ZCLSensorMetadata from zigpy.state import Counter, State from zigpy.zcl import ( AttributeReadEvent, @@ -132,6 +131,7 @@ ) if TYPE_CHECKING: + from zha.quirks.metadata import ZCLEnumMetadata, ZCLSensorMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index e3d1899e8..19bcade18 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -11,7 +11,6 @@ from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT, TUYA_PLUG_ONOFF from zigpy import types as t from zigpy.profiles import zha, zll -from zigpy.quirks.v2 import EntityMetadata, SwitchMetadata import zigpy.zcl from zigpy.zcl import ( AttributeReadEvent, @@ -52,6 +51,7 @@ from zha.zigbee.group import Group if TYPE_CHECKING: + from zha.quirks.metadata import EntityMetadata, SwitchMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 203650c31..534001cbc 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -21,12 +21,10 @@ import zigpy.exceptions from zigpy.profiles import PROFILES import zigpy.quirks -from zigpy.quirks.v2 import DeviceAlertMetadata, QuirksV2RegistryEntry from zigpy.types import uint1_t, uint8_t, uint16_t from zigpy.types.named import EUI64, NWK, ExtendedPanId from zigpy.typing import UNDEFINED, UndefinedType import zigpy.zcl -from zigpy.zcl import ClusterType from zigpy.zcl.clusters import Cluster from zigpy.zcl.clusters.general import Basic, Groups, Identify, Ota from zigpy.zcl.foundation import ( @@ -90,6 +88,8 @@ from zha.event import EventBase from zha.exceptions import ZHAException from zha.mixins import LogMixin +from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, DeviceMatch, ZigpyOp +from zha.quirks.metadata import DeviceAlertMetadata, QuirkDefinition from zha.zigbee.cluster_config import ( aggregate_cluster_configs, configure_cluster_configs, @@ -192,79 +192,6 @@ def get_device_automation_triggers( } -def _read_current_firmware_version( - zigpy_device: zigpy.device.Device, -) -> int | None: - """Read `current_file_version` from the device's OTA cluster, or None.""" - try: - ota = zigpy_device.find_cluster( - cluster_id=Ota.cluster_id, cluster_type=ClusterType.Client - ) - except ValueError: - return None - return ota.get(Ota.AttributeDefs.current_file_version.id) - - -@dataclass(frozen=True) -class DeviceMatch: - """Fingerprint criteria for matching a `Device` subclass to a zigpy device.""" - - manufacturers: frozenset[str] | None = None - models: frozenset[str] | None = None - firmware_versions: tuple[int | None, int | None] = (None, None) - firmware_version_allow_missing: bool = True - - -DEVICE_QUIRKS: list[type[Device]] = [] - - -def register_device(cls: type[Device]) -> type[Device]: - """Register a `Device` subclass for fingerprint-based dispatch.""" - DEVICE_QUIRKS.append(cls) - return cls - - -@dataclass(frozen=True) -class Replace: - """Replace a cluster on a device's endpoint with a CustomCluster subclass.""" - - endpoint_id: int - cluster_id: int - cluster_type: ClusterType - replacement: type[zigpy.quirks.CustomCluster] - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - endpoint = device.endpoints[self.endpoint_id] - if self.cluster_type is ClusterType.Server: - endpoint.in_clusters.pop(self.cluster_id, None) - endpoint.add_input_cluster( - self.cluster_id, self.replacement(endpoint, is_server=True) - ) - else: - endpoint.out_clusters.pop(self.cluster_id, None) - endpoint.add_output_cluster( - self.cluster_id, self.replacement(endpoint, is_server=False) - ) - - -def resolve_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: - """Zigpy device resolver.""" - for cls in DEVICE_QUIRKS: - if cls.matches(zigpy_device): - _LOGGER.warning( - "v3 resolver matched %s for %s/%s, applying %d ops", - cls.__name__, - zigpy_device.manufacturer, - zigpy_device.model, - len(cls._operations), - ) - for op in cls._operations: - op.apply(zigpy_device) - return zigpy_device - return zigpy.quirks.get_device(zigpy_device) - - @dataclass(frozen=True, kw_only=True) class ClusterBinding: """Describes a cluster binding.""" @@ -443,10 +370,19 @@ class Device(LogMixin, EventBase): unique_id: str - # The base `Device` is the universal fallback (matches anything) and is never - # iterated through `DEVICE_QUIRKS`. - _device_match: DeviceMatch = DeviceMatch() - _operations: tuple[Replace, ...] = () + # Quirk matching criteria and zigpy-level modifications. Set by subclasses + # registered with `zha.quirks.register_device`; the base class matches + # nothing. `_zigpy_device_class` optionally replaces the zigpy device + # object with an instance of the given class, which must accept + # `(application, ieee, nwk, replaces)` like `zigpy.quirks.v2.CustomDeviceV2`. + _device_match: DeviceMatch | None = None + _zigpy_device_class: type[zigpy.device.Device] | None = None + _zigpy_ops: tuple[ZigpyOp, ...] = () + + # ZHA-level quirk metadata (entities, triggers, alerts, naming), set by + # `zha.quirks.v2.QuirkBuilder` compiled quirks. Hand-written quirks express + # the same things by overriding `Device` directly. + _quirk_definition: QuirkDefinition | None = None # Cached properties that depend on the zigpy device and must be invalidated # when the underlying device is swapped (e.g. after a re-interview). @@ -497,30 +433,25 @@ def __init__( self._init_from_zigpy_device(zigpy_device) @classmethod - def matches(cls, zigpy_device: zigpy.device.Device) -> bool: - """Return True if this Device subclass should wrap `zigpy_device`.""" - m = cls._device_match - if ( - m.manufacturers is not None - and zigpy_device.manufacturer not in m.manufacturers - ): - return False - if m.models is not None and zigpy_device.model not in m.models: - return False - - min_fw, max_fw = m.firmware_versions - if min_fw is not None or max_fw is not None: - current = _read_current_firmware_version(zigpy_device) - if current is None: - if not m.firmware_version_allow_missing: - return False - else: - if min_fw is not None and current < min_fw: - return False - if max_fw is not None and current >= max_fw: - return False - - return True + def apply_to_zigpy_device( + cls, zigpy_device: zigpy.device.Device + ) -> zigpy.device.Device: + """Apply this quirk's modifications to a freshly-constructed zigpy device. + + The default implementation wraps the device with `_zigpy_device_class` + (when set) and then applies `_zigpy_ops` in order. Override this for + arbitrary surgery, including returning a replacement device object. + """ + if cls._zigpy_device_class is not None: + zigpy_device = cls._zigpy_device_class( + zigpy_device.application, + zigpy_device.ieee, + zigpy_device.nwk, + zigpy_device, + ) + for op in cls._zigpy_ops: + op.apply(zigpy_device) + return zigpy_device def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: """(Re-)initialize device state from a zigpy device. @@ -546,13 +477,16 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: with contextlib.suppress(AttributeError): delattr(self, attr) - self.quirk_applied: bool = isinstance( + self.quirk_applied: bool = type(self)._device_match is not None or isinstance( self._zigpy_device, zigpy.quirks.BaseCustomDevice ) - self.quirk_class: str = ( - f"{self._zigpy_device.__class__.__module__}." - f"{self._zigpy_device.__class__.__name__}" - ) + if type(self)._device_match is not None: + self.quirk_class: str = f"{type(self).__module__}.{type(self).__name__}" + else: + self.quirk_class = ( + f"{self._zigpy_device.__class__.__module__}." + f"{self._zigpy_device.__class__.__name__}" + ) # add v1 quirk exposed features (legacy quirk id) qid: set[str] | str = getattr(self._zigpy_device, ATTR_QUIRK_ID, set()) @@ -614,9 +548,9 @@ def ieee(self) -> EUI64: return self._zigpy_device.ieee @property - def quirk_metadata(self) -> QuirksV2RegistryEntry | None: - """Return the quirk metadata for this device.""" - return getattr(self._zigpy_device, "quirk_metadata", None) + def quirk_metadata(self) -> QuirkDefinition | None: + """Return the ZHA-level quirk metadata for this device.""" + return self._quirk_definition @cached_property def manufacturer(self) -> str: @@ -761,6 +695,8 @@ def is_groupable(self) -> bool: @cached_property def skip_configuration(self) -> bool: """Return true if the device should not issue configuration related commands.""" + if self.quirk_metadata is not None and self.quirk_metadata.skip_configuration: + return True return self._zigpy_device.skip_configuration or bool(self.is_active_coordinator) @property @@ -780,7 +716,10 @@ def device_automation_commands(self) -> dict[str, list[tuple[str, str]]]: @cached_property def device_automation_triggers(self) -> dict[tuple[str, str], dict[str, str]]: """Return the device automation triggers for this device.""" - return get_device_automation_triggers(self._zigpy_device) + triggers = get_device_automation_triggers(self._zigpy_device) + if self.quirk_metadata is not None: + triggers.update(self.quirk_metadata.device_automation_triggers) + return triggers @property def available(self): @@ -898,13 +837,13 @@ def new( zigpy_dev: zigpy.device.Device, gateway: Gateway, ) -> Device: - """Create new device, dispatching to a registered subclass when applicable.""" + """Create new device, dispatching to the class matched during resolution.""" if zigpy_dev.ieee == gateway.state.node_info.ieee: return CoordinatorDevice(zigpy_dev, gateway) - for quirk_cls in DEVICE_QUIRKS: - if quirk_cls.matches(zigpy_dev): - return quirk_cls(zigpy_dev, gateway) + quirk_cls = getattr(zigpy_dev, ZHA_DEVICE_CLASS_ATTRIBUTE, None) + if quirk_cls is not None: + return quirk_cls(zigpy_dev, gateway) return cls(zigpy_dev, gateway) From 5f4ea4b42cd41c638a85da5ea1bead755835ea45 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 9 Jun 2026 19:15:23 -0400 Subject: [PATCH 09/41] WIP --- zha/quirks/__init__.py | 470 ++++++++++++++++++++++ zha/quirks/metadata.py | 245 ++++++++++++ zha/quirks/v2.py | 890 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1605 insertions(+) create mode 100644 zha/quirks/__init__.py create mode 100644 zha/quirks/metadata.py create mode 100644 zha/quirks/v2.py diff --git a/zha/quirks/__init__.py b/zha/quirks/__init__.py new file mode 100644 index 000000000..fda359f86 --- /dev/null +++ b/zha/quirks/__init__.py @@ -0,0 +1,470 @@ +"""Quirks support for ZHA. + +This package owns the matching, registration and dispatch of quirked `Device` +subclasses. A quirk is a `zha.zigbee.device.Device` subclass decorated with +`@register_device`: its `_device_match` decides which zigpy devices it wraps, +its `_zigpy_ops` describe the modifications applied to the zigpy device during +resolution, and everything else (entities, triggers, alerts, configuration) is +expressed by overriding the `Device` class itself. + +`resolve_device` is registered with zigpy as the application's device resolver. +It is handed a freshly-constructed zigpy device exactly once (on join and on +database load), applies the matching quirk's zigpy-level modifications, and +falls back to zigpy's legacy v1/v2 quirks registry when no ZHA quirk matches. +""" + +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Callable +from dataclasses import dataclass, field +import inspect +import logging +from pathlib import Path +from typing import TYPE_CHECKING, Any, NamedTuple, Protocol + +import zigpy.device +import zigpy.profiles.zha +import zigpy.quirks +from zigpy.zcl import Cluster, ClusterType +from zigpy.zcl.clusters.general import Ota +from zigpy.zcl.foundation import ZCLAttributeDef +from zigpy.zdo.types import NodeDescriptor + +if TYPE_CHECKING: + from zha.zigbee.device import Device + +_LOGGER = logging.getLogger(__name__) + +FilterType = Callable[[zigpy.device.Device], bool] + +# Attribute stamped onto a zigpy device by `resolve_device` so that +# `Device.new` dispatches to the class that already matched during resolution, +# instead of running a second, potentially divergent, match pass. +ZHA_DEVICE_CLASS_ATTRIBUTE = "zha_device_class" + + +class ModelInfo(NamedTuple): + """A (manufacturer, model) pair to match. `None` is a wildcard.""" + + manufacturer: str | None + model: str | None + + +def _read_current_firmware_version( + zigpy_device: zigpy.device.Device, +) -> int | None: + """Read `current_file_version` from the device's OTA cluster, or None.""" + try: + ota = zigpy_device.find_cluster( + cluster_id=Ota.cluster_id, cluster_type=ClusterType.Client + ) + except ValueError: + return None + return ota.get(Ota.AttributeDefs.current_file_version.id) + + +@dataclass(frozen=True) +class DeviceMatch: + """Criteria matching a `Device` subclass to a zigpy device. + + A device matches when any `applies_to` pair matches, all `filters` pass, + and the firmware version (when filtered on) is within + `[firmware_version_min, firmware_version_max)`. + """ + + applies_to: tuple[ModelInfo, ...] + filters: tuple[FilterType, ...] = () + firmware_version_min: int | None = None + firmware_version_max: int | None = None + firmware_version_allow_missing: bool = True + + def matches(self, zigpy_device: zigpy.device.Device) -> bool: + """Return True if `zigpy_device` satisfies all criteria.""" + if not any( + (manufacturer is None or manufacturer == zigpy_device.manufacturer) + and (model is None or model == zigpy_device.model) + for manufacturer, model in self.applies_to + ): + return False + + if not all(matcher(zigpy_device) for matcher in self.filters): + return False + + if ( + self.firmware_version_min is not None + or self.firmware_version_max is not None + ): + current = _read_current_firmware_version(zigpy_device) + if current is None: + return self.firmware_version_allow_missing + if self.firmware_version_min is not None and ( + current < self.firmware_version_min + ): + return False + if self.firmware_version_max is not None and ( + current >= self.firmware_version_max + ): + return False + + return True + + +class ZigpyOp(Protocol): + """A single modification applied to a zigpy device during resolution.""" + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + + +@dataclass(frozen=True) +class AddCluster: + """Add a cluster to an endpoint. + + `cluster` is either a bare cluster id or a `Cluster` subclass. When + `constant_attributes` is provided (mapping `ZCLAttributeDef` to value), the + values are served by the cluster without contacting the device; this + requires `cluster` to be a `CustomCluster` subclass. + """ + + cluster: int | type[Cluster] + endpoint_id: int = 1 + cluster_type: ClusterType = ClusterType.Server + constant_attributes: dict[ZCLAttributeDef, Any] = field(default_factory=dict) + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + endpoint = device.endpoints[self.endpoint_id] + is_server = self.cluster_type == ClusterType.Server + + if isinstance(self.cluster, int): + cluster = None + cluster_id = self.cluster + else: + cluster = self.cluster(endpoint, is_server=is_server) + cluster_id = cluster.cluster_id + + if is_server: + cluster = endpoint.add_input_cluster(cluster_id, cluster) + else: + cluster = endpoint.add_output_cluster(cluster_id, cluster) + + if self.constant_attributes: + cluster._CONSTANT_ATTRIBUTES = { + attribute.id: value + for attribute, value in self.constant_attributes.items() + } + + +@dataclass(frozen=True) +class RemoveCluster: + """Remove a cluster from an endpoint.""" + + cluster_id: int + endpoint_id: int = 1 + cluster_type: ClusterType = ClusterType.Server + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + endpoint = device.endpoints[self.endpoint_id] + if self.cluster_type == ClusterType.Server: + endpoint.in_clusters.pop(self.cluster_id, None) + else: + endpoint.out_clusters.pop(self.cluster_id, None) + + +@dataclass(frozen=True) +class ReplaceCluster: + """Replace a cluster on an endpoint with a `Cluster` subclass. + + `cluster_id` identifies the cluster to remove and defaults to the + replacement's own cluster id. Cached attribute values of the replaced + cluster (e.g. restored from the database) carry over to the replacement. + """ + + cluster: type[Cluster] + cluster_id: int | None = None + endpoint_id: int = 1 + cluster_type: ClusterType = ClusterType.Server + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + endpoint = device.endpoints[self.endpoint_id] + is_server = self.cluster_type == ClusterType.Server + removed_cluster_id = ( + self.cluster.cluster_id if self.cluster_id is None else self.cluster_id + ) + + if is_server: + old_cluster = endpoint.in_clusters.pop(removed_cluster_id, None) + else: + old_cluster = endpoint.out_clusters.pop(removed_cluster_id, None) + + new_cluster = self.cluster(endpoint, is_server=is_server) + if is_server: + endpoint.add_input_cluster(new_cluster.cluster_id, new_cluster) + else: + endpoint.add_output_cluster(new_cluster.cluster_id, new_cluster) + + if old_cluster is not None: + new_cluster._attr_cache_internal = old_cluster._attr_cache.clone( + new_cluster + ) + + +@dataclass(frozen=True) +class ReplaceClusterOccurrences: + """Replace a cluster with a `Cluster` subclass on every endpoint.""" + + cluster: type[Cluster] + cluster_types: tuple[ClusterType, ...] = (ClusterType.Server, ClusterType.Client) + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + for endpoint in device.non_zdo_endpoints: + if ( + ClusterType.Server in self.cluster_types + and self.cluster.cluster_id in endpoint.in_clusters + ): + ReplaceCluster( + cluster=self.cluster, + endpoint_id=endpoint.endpoint_id, + cluster_type=ClusterType.Server, + ).apply(device) + if ( + ClusterType.Client in self.cluster_types + and self.cluster.cluster_id in endpoint.out_clusters + ): + ReplaceCluster( + cluster=self.cluster, + endpoint_id=endpoint.endpoint_id, + cluster_type=ClusterType.Client, + ).apply(device) + + +@dataclass(frozen=True) +class AddEndpoint: + """Add an endpoint to a device, if it does not already exist.""" + + endpoint_id: int + profile_id: int = zigpy.profiles.zha.PROFILE_ID + device_type: int = 0xFF + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + if self.endpoint_id in device.endpoints: + return + endpoint = device.add_endpoint(self.endpoint_id) + endpoint.profile_id = self.profile_id + endpoint.device_type = self.device_type + + +@dataclass(frozen=True) +class RemoveEndpoint: + """Remove an endpoint from a device.""" + + endpoint_id: int + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + device.endpoints.pop(self.endpoint_id, None) + + +@dataclass(frozen=True) +class ReplaceEndpoint: + """Set the profile and device type of an endpoint, creating it if needed.""" + + endpoint_id: int + profile_id: int = zigpy.profiles.zha.PROFILE_ID + device_type: int = 0xFF + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + if self.endpoint_id in device.endpoints: + endpoint = device.endpoints[self.endpoint_id] + else: + endpoint = device.add_endpoint(self.endpoint_id) + endpoint.profile_id = self.profile_id + endpoint.device_type = self.device_type + + +@dataclass(frozen=True) +class SetNodeDescriptor: + """Replace the node descriptor of a device.""" + + node_descriptor: NodeDescriptor + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + device.node_desc = self.node_descriptor.freeze() + + +@dataclass(frozen=True) +class SetModelInfo: + """Override the manufacturer and/or model of a device.""" + + manufacturer: str | None = None + model: str | None = None + + def apply(self, device: zigpy.device.Device) -> None: + """Apply this operation to the given zigpy device.""" + if self.manufacturer is not None: + device.manufacturer = self.manufacturer + if self.model is not None: + device.model = self.model + + +class DeviceRegistry: + """Registry of quirked `Device` subclasses, keyed by (manufacturer, model).""" + + def __init__(self) -> None: + """Initialize the registry.""" + self._registry: defaultdict[ModelInfo, list[type[Device]]] = defaultdict(list) + + def register(self, cls: type[Device]) -> type[Device]: + """Add a `Device` subclass to the registry.""" + match = cls._device_match + if match is None: + raise ValueError(f"{cls!r} does not define `_device_match`") + if not match.applies_to: + raise ValueError(f"{cls!r} must apply to at least one model") + for manufacturer, model in match.applies_to: + if manufacturer is None and model is None: + raise ValueError( + f"{cls!r} must specify a manufacturer and/or model to match" + ) + # Most recently registered quirks take precedence, so quirks loaded + # from the custom quirks directory override built-in ones. + self._registry[ModelInfo(manufacturer, model)].insert(0, cls) + return cls + + def get(self, zigpy_device: zigpy.device.Device) -> type[Device] | None: + """Return the first registered class matching `zigpy_device`.""" + for key in ( + ModelInfo(zigpy_device.manufacturer, zigpy_device.model), + ModelInfo(zigpy_device.manufacturer, None), + ModelInfo(None, zigpy_device.model), + ): + for cls in self._registry[key]: + if cls._device_match.matches(zigpy_device): + return cls + return None + + def remove(self, cls: type[Device]) -> None: + """Remove a `Device` subclass from the registry.""" + for manufacturer, model in cls._device_match.applies_to: + self._registry[ModelInfo(manufacturer, model)].remove(cls) + + def purge_custom_quirks(self, custom_quirks_root: Path) -> None: + """Remove quirks loaded from the custom quirks directory.""" + for classes in self._registry.values(): + for cls in list(classes): + if ( + cls._quirk_definition is not None + and cls._quirk_definition.quirk_file is not None + ): + quirk_file = Path(cls._quirk_definition.quirk_file) + else: + module = inspect.getmodule(cls) + quirk_file = Path(module.__file__) + + if quirk_file.is_relative_to(custom_quirks_root): + _LOGGER.debug("Removing stale custom quirk: %s", cls) + classes.remove(cls) + + +DEVICE_REGISTRY = DeviceRegistry() + + +def register_device(cls: type[Device]) -> type[Device]: + """Class decorator registering a `Device` subclass as a quirk.""" + return DEVICE_REGISTRY.register(cls) + + +def _snapshot_zigpy_device(zigpy_device: zigpy.device.Device) -> tuple: + """Capture the device state that quirk operations can modify.""" + return ( + zigpy_device.manufacturer, + zigpy_device.model, + zigpy_device.node_desc, + dict(zigpy_device.endpoints), + { + endpoint.endpoint_id: ( + endpoint.profile_id, + endpoint.device_type, + dict(endpoint.in_clusters), + dict(endpoint.out_clusters), + ) + for endpoint in zigpy_device.non_zdo_endpoints + }, + ) + + +def _restore_zigpy_device(zigpy_device: zigpy.device.Device, snapshot: tuple) -> None: + """Restore device state captured by `_snapshot_zigpy_device`.""" + manufacturer, model, node_desc, endpoints, endpoint_state = snapshot + zigpy_device._manufacturer = manufacturer + zigpy_device._model = model + zigpy_device.node_desc = node_desc + zigpy_device.endpoints.clear() + zigpy_device.endpoints.update(endpoints) + for endpoint_id, ( + profile_id, + device_type, + in_clusters, + out_clusters, + ) in endpoint_state.items(): + endpoint = zigpy_device.endpoints[endpoint_id] + endpoint.profile_id = profile_id + endpoint.device_type = device_type + endpoint.in_clusters.clear() + endpoint.in_clusters.update(in_clusters) + endpoint.out_clusters.clear() + endpoint.out_clusters.update(out_clusters) + + +def resolve_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: + """Resolve a freshly-constructed zigpy device into its final object. + + Registered with zigpy as the application's device resolver. The matching + quirk class applies its zigpy-level modifications (and may return a + replacement device object); the class is then stamped onto the zigpy + device so `Device.new` dispatches to it without a second match pass. + + Devices without a matching ZHA quirk go through zigpy's legacy v1/v2 + quirks registry instead. + """ + # Resolution is idempotent: an already-quirked device is returned as-is + # (re-applying in-place operations would clobber cluster state). + if hasattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE): + return zigpy_device + + quirk_cls = DEVICE_REGISTRY.get(zigpy_device) + if quirk_cls is None: + return zigpy.quirks.get_device(zigpy_device) + + _LOGGER.debug( + "Resolved %s/%s (%s) to quirk %s", + zigpy_device.manufacturer, + zigpy_device.model, + zigpy_device.ieee, + quirk_cls.__name__, + ) + + # A quirk that fails to apply leaves the device unquirked, matching the + # zigpy registry's behavior. Operations apply in place, so the prior state + # is restored on failure. + snapshot = _snapshot_zigpy_device(zigpy_device) + try: + resolved = quirk_cls.apply_to_zigpy_device(zigpy_device) + except Exception: # noqa: BLE001 + _restore_zigpy_device(zigpy_device, snapshot) + _LOGGER.exception( + "Failed to apply quirk %s to %r. This is a bug, please report it", + quirk_cls.__name__, + zigpy_device, + ) + return zigpy_device + + setattr(resolved, ZHA_DEVICE_CLASS_ATTRIBUTE, quirk_cls) + return resolved diff --git a/zha/quirks/metadata.py b/zha/quirks/metadata.py new file mode 100644 index 000000000..77178b853 --- /dev/null +++ b/zha/quirks/metadata.py @@ -0,0 +1,245 @@ +"""ZHA quirk metadata model. + +The declarative metadata produced by `zha.quirks.v2.QuirkBuilder`: exposed +entities, default-entity changes, alerts and naming, aggregated into a +`QuirkDefinition` stored on the compiled `Device` subclass. +""" + +from __future__ import annotations + +from collections.abc import Callable +from enum import Enum +from typing import Any + +import attrs +from frozendict import frozendict +from zigpy.zcl import ClusterType + +from zha.application import EntityPlatform, EntityType +from zha.application.platforms.binary_sensor.device_class import BinarySensorDeviceClass +from zha.application.platforms.number.device_class import NumberDeviceClass +from zha.application.platforms.sensor.device_class import ( + SensorDeviceClass, + SensorStateClass, +) + +# pylint: disable=too-many-instance-attributes + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ReportingConfig: + """Reporting config for an entity attribute.""" + + min_interval: int + max_interval: int + reportable_change: int + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class EntityMetadata: + """Metadata for an exposed entity.""" + + entity_platform: EntityPlatform = attrs.field() + entity_type: EntityType = attrs.field() + cluster_id: int = attrs.field() + endpoint_id: int = attrs.field(default=1) + cluster_type: ClusterType = attrs.field(default=ClusterType.Server) + initially_disabled: bool = attrs.field(default=False) + attribute_initialized_from_cache: bool = attrs.field(default=True) + unique_id_suffix: str | None = attrs.field(default=None) + translation_key: str | None = attrs.field(default=None) + translation_placeholders: frozendict[str, str] = attrs.field( + factory=frozendict, converter=frozendict + ) + fallback_name: str = attrs.field(validator=attrs.validators.instance_of(str)) + primary: bool | None = attrs.field(default=None) + + def __attrs_post_init__(self) -> None: + """Validate the entity metadata.""" + has_device_class: bool = getattr(self, "device_class", None) is not None + if self.translation_key is None and not has_device_class: + raise ValueError( + f"EntityMetadata must have a translation_key or device_class: {self}" + ) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ZCLEnumMetadata(EntityMetadata): + """Metadata for exposed ZCL enum based entity.""" + + enum: type[Enum] = attrs.field() + attribute_name: str = attrs.field() + reporting_config: ReportingConfig | None = attrs.field(default=None) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ZCLSensorMetadata(EntityMetadata): + """Metadata for exposed ZCL attribute based sensor entity.""" + + attribute_name: str | None = attrs.field(default=None) + attribute_converter: Callable[[Any], Any] | None = attrs.field(default=None) + reporting_config: ReportingConfig | None = attrs.field(default=None) + divisor: int | None = attrs.field(default=None) + multiplier: int | None = attrs.field(default=None) + suggested_display_precision: int | None = attrs.field(default=None) + unit: str | None = attrs.field(default=None) + device_class: SensorDeviceClass | None = attrs.field(default=None) + state_class: SensorStateClass | None = attrs.field(default=None) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class SwitchMetadata(EntityMetadata): + """Metadata for exposed switch entity.""" + + attribute_name: str = attrs.field() + reporting_config: ReportingConfig | None = attrs.field(default=None) + force_inverted: bool = attrs.field(default=False) + invert_attribute_name: str | None = attrs.field(default=None) + off_value: int = attrs.field(default=0) + on_value: int = attrs.field(default=1) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class NumberMetadata(EntityMetadata): + """Metadata for exposed number entity.""" + + attribute_name: str = attrs.field() + reporting_config: ReportingConfig | None = attrs.field(default=None) + min: float | None = attrs.field(default=None) + max: float | None = attrs.field(default=None) + step: float | None = attrs.field(default=None) + unit: str | None = attrs.field(default=None) + mode: str | None = attrs.field(default=None) + multiplier: float | None = attrs.field(default=None) + device_class: NumberDeviceClass | None = attrs.field(default=None) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class BinarySensorMetadata(EntityMetadata): + """Metadata for exposed binary sensor entity.""" + + attribute_name: str = attrs.field() + attribute_converter: Callable[[Any], Any] | None = attrs.field(default=None) + reporting_config: ReportingConfig | None = attrs.field(default=None) + device_class: BinarySensorDeviceClass | None = attrs.field(default=None) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class WriteAttributeButtonMetadata(EntityMetadata): + """Metadata for exposed button entity that writes an attribute when pressed.""" + + attribute_name: str = attrs.field() + attribute_value: int = attrs.field() + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ZCLCommandButtonMetadata(EntityMetadata): + """Metadata for exposed button entity that executes a ZCL command when pressed.""" + + command_name: str = attrs.field() + args: tuple = attrs.field(default=tuple) + kwargs: frozendict[str, Any] = attrs.field(factory=frozendict, converter=frozendict) + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class FriendlyNameMetadata: + """Metadata to rename a device.""" + + model: str = attrs.field() + manufacturer: str = attrs.field() + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ExposesFeatureMetadata: + """Metadata for an exposed feature to match against in ZHA.""" + + feature: str = attrs.field() + config: frozendict[str, Any] = attrs.field(factory=frozendict, converter=frozendict) + + +class DeviceAlertLevel(Enum): + """Device alert level.""" + + INFO = "info" + WARNING = "warning" + ERROR = "error" + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class DeviceAlertMetadata: + """Metadata for device-specific alerts.""" + + level: DeviceAlertLevel = attrs.field(converter=DeviceAlertLevel) + message: str = attrs.field() + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class PreventDefaultEntityCreationMetadata: + """Metadata to prevent the default creation of an entity.""" + + endpoint_id: int | None = attrs.field() + cluster_id: int | None = attrs.field() + cluster_type: ClusterType | None = attrs.field() + unique_id_suffix: str | None = attrs.field() + function: Callable[[Any], bool] | None = attrs.field() + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class ChangedEntityMetadata: + """Metadata to change entity metadata for matching entities.""" + + endpoint_id: int | None = attrs.field() + cluster_id: int | None = attrs.field() + cluster_type: ClusterType | None = attrs.field() + unique_id_suffix: str | None = attrs.field() + function: Callable[[Any], bool] | None = attrs.field() + # Entity metadata changes + new_primary: bool | None = attrs.field(default=None) + new_unique_id: str | None = attrs.field(default=None) + new_translation_key: str | None = attrs.field(default=None) + new_translation_placeholders: frozendict[str, str] | None = attrs.field( + default=None, converter=lambda d: None if d is None else frozendict(d) + ) + new_device_class: ( + BinarySensorDeviceClass | NumberDeviceClass | SensorDeviceClass | None + ) = attrs.field(default=None) + new_state_class: SensorStateClass | None = attrs.field(default=None) + new_entity_category: EntityType | None = attrs.field(default=None) + new_entity_registry_enabled_default: bool | None = attrs.field(default=None) + new_fallback_name: str | None = attrs.field(default=None) + + +def recursive_freeze(obj: Any) -> Any: + """Recursively convert mutable collections to immutable ones.""" + if isinstance(obj, dict): + return frozendict({k: recursive_freeze(v) for k, v in obj.items()}) + if isinstance(obj, tuple | list | set): + return tuple(recursive_freeze(v) for v in obj) + return obj + + +@attrs.define(frozen=True, kw_only=True, repr=True) +class QuirkDefinition: + """ZHA-level metadata of a quirk. + + Everything a quirk expresses about the ZHA device model: entities, + automation triggers, alerts and naming. Zigbee-level modifications live in + `Device._zigpy_ops` instead. + """ + + quirk_file: str | None = attrs.field(default=None, eq=False) + quirk_file_line: int | None = attrs.field(default=None, eq=False) + friendly_name: FriendlyNameMetadata | None = attrs.field(default=None) + exposes_features: tuple[ExposesFeatureMetadata, ...] = attrs.field(factory=tuple) + device_alerts: tuple[DeviceAlertMetadata, ...] = attrs.field(factory=tuple) + disabled_default_entities: tuple[PreventDefaultEntityCreationMetadata, ...] = ( + attrs.field(factory=tuple) + ) + changed_entity_metadata: tuple[ChangedEntityMetadata, ...] = attrs.field( + factory=tuple + ) + entity_metadata: tuple[EntityMetadata, ...] = attrs.field(factory=tuple) + device_automation_triggers: frozendict[tuple[str, str], frozendict[str, str]] = ( + attrs.field(factory=frozendict, converter=recursive_freeze) + ) + skip_configuration: bool = attrs.field(default=False) diff --git a/zha/quirks/v2.py b/zha/quirks/v2.py new file mode 100644 index 000000000..151d545f4 --- /dev/null +++ b/zha/quirks/v2.py @@ -0,0 +1,890 @@ +"""ZHA-native quirks v2 builder. + +`QuirkBuilder` keeps the declarative quirks v2 authoring API but compiles to +the ZHA device model: `add_to_registry()` synthesizes a `zha.zigbee.device.Device` +subclass whose `_device_match` carries the matching criteria, whose +`_zigpy_ops` carry the Zigbee-level modifications, and whose +`_quirk_definition` carries the ZHA-level metadata (entities, triggers, +alerts, naming). +""" + +from __future__ import annotations + +from collections.abc import Callable +from copy import deepcopy +from enum import Enum +import inspect +import logging +import pathlib +import re +from types import FrameType +from typing import Any, Self, overload + +from frozendict import frozendict +import zigpy.device +import zigpy.profiles.zha +from zigpy.typing import UNDEFINED, UndefinedType +from zigpy.zcl import Cluster, ClusterType +from zigpy.zcl.foundation import ZCLAttributeDef +from zigpy.zdo.types import NodeDescriptor + +from zha.application import ( # noqa: F401 + EntityPlatform, + EntityType, + # `discovery` must be imported before `zha.zigbee.device`: the platform + # modules it loads participate in an import cycle with the device module + # and cannot be loaded with `zha.zigbee.device` partially initialized. + discovery, +) +from zha.application.platforms.binary_sensor.device_class import BinarySensorDeviceClass +from zha.application.platforms.number.device_class import NumberDeviceClass +from zha.application.platforms.sensor.device_class import ( + SensorDeviceClass, + SensorStateClass, +) +from zha.quirks import ( + DEVICE_REGISTRY, + AddCluster, + AddEndpoint, + DeviceMatch, + DeviceRegistry, + FilterType, + ModelInfo, + RemoveCluster, + RemoveEndpoint, + ReplaceCluster, + ReplaceClusterOccurrences, + ReplaceEndpoint, + SetNodeDescriptor, + ZigpyOp, +) +from zha.quirks.metadata import ( + BinarySensorMetadata, + ChangedEntityMetadata, + DeviceAlertLevel, + DeviceAlertMetadata, + EntityMetadata, + ExposesFeatureMetadata, + FriendlyNameMetadata, + NumberMetadata, + PreventDefaultEntityCreationMetadata, + QuirkDefinition, + ReportingConfig, + SwitchMetadata, + WriteAttributeButtonMetadata, + ZCLCommandButtonMetadata, + ZCLEnumMetadata, + ZCLSensorMetadata, +) +from zha.zigbee.device import Device + +_LOGGER = logging.getLogger(__name__) + +UNBUILT_QUIRK_BUILDERS: list[QuirkBuilder] = [] + +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-arguments + + +class QuirkBuilder: + """Builder compiling a declarative quirk into a registered `Device` subclass.""" + + def __init__( + self, + manufacturer: str | None | UndefinedType = UNDEFINED, + model: str | None | UndefinedType = UNDEFINED, + registry: DeviceRegistry | None = None, + ) -> None: + """Initialize the quirk builder.""" + self.registry: DeviceRegistry = ( + registry if registry is not None else DEVICE_REGISTRY + ) + self.manufacturer_model_metadata: list[ModelInfo] = [] + self.friendly_name_metadata: FriendlyNameMetadata | None = None + self.exposes_features: list[ExposesFeatureMetadata] = [] + self.device_alerts: list[DeviceAlertMetadata] = [] + self.disabled_default_entities: list[PreventDefaultEntityCreationMetadata] = [] + self.changed_entity_metadata: list[ChangedEntityMetadata] = [] + self.filters: list[FilterType] = [] + self.firmware_version_min: int | None = None + self.firmware_version_max: int | None = None + self.firmware_version_allow_missing: bool = True + self.custom_device_class: type[Device] | None = None + self.custom_zigpy_device_class: type[zigpy.device.Device] | None = None + self.device_node_descriptor: NodeDescriptor | None = None + self.skip_device_configuration: bool = False + self.removes_endpoint_ops: list[RemoveEndpoint] = [] + self.adds_endpoint_ops: list[AddEndpoint] = [] + self.replaces_endpoint_ops: list[ReplaceEndpoint] = [] + self.removes_ops: list[RemoveCluster] = [] + self.adds_ops: list[AddCluster] = [] + self.replaces_ops: list[ReplaceCluster] = [] + self.replace_occurrences_ops: list[ReplaceClusterOccurrences] = [] + self.entity_metadata: list[EntityMetadata] = [] + self.device_automation_triggers_metadata: dict[ + tuple[str, str], dict[str, str] + ] = {} + + current_frame: FrameType = inspect.currentframe() + caller: FrameType = current_frame.f_back + self.quirk_file = pathlib.Path(caller.f_code.co_filename) + self.quirk_file_line = caller.f_lineno + self.quirk_module: str = caller.f_globals["__name__"] + + if manufacturer is not UNDEFINED or model is not UNDEFINED: + self.applies_to( + manufacturer=manufacturer if manufacturer is not UNDEFINED else None, + model=model if model is not UNDEFINED else None, + ) + + UNBUILT_QUIRK_BUILDERS.append(self) + + def _add_entity_metadata(self, entity_metadata: EntityMetadata) -> Self: + """Register new entity metadata and validate config.""" + if entity_metadata.primary and any( + entity.primary for entity in self.entity_metadata + ): + raise ValueError("Only one primary entity can be defined per device") + + self.entity_metadata.append(entity_metadata) + return self + + @overload + def applies_to(self, manufacturer: str, model: str) -> Self: ... + + @overload + def applies_to(self, manufacturer: str, model: str | None) -> Self: ... + + @overload + def applies_to(self, manufacturer: None, model: str) -> Self: ... + + def applies_to(self, manufacturer: str | None, model: str | None) -> Self: + """Register this quirk for the specified manufacturer and model.""" + if manufacturer is None and model is None: + raise ValueError( + "A manufacturer and/or model must be specified for a v2 quirk." + ) + + self.manufacturer_model_metadata.append(ModelInfo(manufacturer, model)) + return self + + # backward compatibility + also_applies_to = applies_to + + def filter(self, filter_function: FilterType) -> Self: + """Add a filter and returns self. + + The filter function should take a single argument, a zigpy.device.Device + instance, and return a boolean if the condition the filter is testing + passes. + + Ex: def some_filter(device: zigpy.device.Device) -> bool: + """ + self.filters.append(filter_function) + return self + + def firmware_version_filter( + self, + min_version: int | None = None, + max_version: int | None = None, + allow_missing: bool = True, + ) -> Self: + """Add a firmware version filter and returns self. + + The min_version and max_version are integers representing the firmware version, + minimum inclusive but maximum exclusive. If allow_missing is True, the filter + will pass if the device does not have a firmware version. + """ + self.firmware_version_min = min_version + self.firmware_version_max = max_version + self.firmware_version_allow_missing = allow_missing + return self + + def device_class( + self, custom_device_class: type[Device] | type[zigpy.device.Device] + ) -> Self: + """Set the `Device` subclass used as the base of this quirk's class. + + For compatibility with quirks v2, a `zigpy.device.Device` subclass is + also accepted and is treated as `zigpy_device_class` instead. + """ + if issubclass(custom_device_class, Device): + self.custom_device_class = custom_device_class + return self + + assert issubclass(custom_device_class, zigpy.device.Device), ( + f"{custom_device_class} is not a subclass of Device" + ) + return self.zigpy_device_class(custom_device_class) + + def zigpy_device_class( + self, custom_device_class: type[zigpy.device.Device] + ) -> Self: + """Replace the zigpy device object with an instance of the given class. + + The class must accept `(application, ieee, nwk, replaces)`, like + `zigpy.quirks.v2.CustomDeviceV2`, and is used to override low-level + zigpy device behavior. The replacement happens before any cluster or + endpoint operations are applied. + """ + self.custom_zigpy_device_class = custom_device_class + return self + + def node_descriptor(self, node_descriptor: NodeDescriptor) -> Self: + """Set the node descriptor and returns self. + + The node descriptor must be a NodeDescriptor instance and it will be used + to replace the node descriptor of the device when the quirk is applied. + """ + self.device_node_descriptor = node_descriptor.freeze() + return self + + def skip_configuration(self, skip_configuration: bool = True) -> Self: + """Set the skip_configuration and returns self. + + If skip_configuration is True, reporting configuration will not be + applied to any cluster on this device. + """ + self.skip_device_configuration = skip_configuration + return self + + def adds( + self, + cluster: int | type[Cluster], + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + constant_attributes: dict[ZCLAttributeDef, Any] | None = None, + ) -> Self: + """Add an `AddCluster` operation and return self. + + This method allows adding a cluster to a device when the quirk is applied. + + If cluster is an int, it will be used as the cluster_id. If cluster is a + subclass of Cluster or CustomCluster, it will be used to create a new + cluster instance. + + If constant_attributes is provided, it should be a dictionary of ZCLAttributeDef + instances and their values. These attributes will be added to the cluster when + the quirk is applied and the values will be constant. + """ + self.adds_ops.append( + AddCluster( + cluster=cluster, + endpoint_id=endpoint_id, + cluster_type=cluster_type, + constant_attributes=constant_attributes or {}, + ) + ) + return self + + def removes( + self, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + ) -> Self: + """Add a `RemoveCluster` operation and return self. + + This method allows removing a cluster from a device when the quirk is applied. + """ + self.removes_ops.append( + RemoveCluster( + cluster_id=cluster_id, + endpoint_id=endpoint_id, + cluster_type=cluster_type, + ) + ) + return self + + def replaces( + self, + replacement_cluster_class: type[Cluster], + cluster_id: int | None = None, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + ) -> Self: + """Add a `ReplaceCluster` operation and return self. + + This method allows replacing a cluster on a device when the quirk is applied. + + replacement_cluster_class should be a subclass of Cluster or CustomCluster and + will be used to create a new cluster instance to replace the existing cluster. + + If cluster_id is provided, it will be used as the cluster_id for the cluster to + be removed. If cluster_id is not provided, the cluster_id of the replacement + cluster will be used. + """ + self.replaces_ops.append( + ReplaceCluster( + cluster=replacement_cluster_class, + cluster_id=cluster_id, + endpoint_id=endpoint_id, + cluster_type=cluster_type, + ) + ) + return self + + def replace_cluster_occurrences( + self, + replacement_cluster_class: type[Cluster], + replace_server_instances: bool = True, + replace_client_instances: bool = True, + ) -> Self: + """Add a `ReplaceClusterOccurrences` operation and return self. + + This method allows replacing a cluster on a device across all endpoints + for the specified cluster types when the quirk is applied. + """ + types = [] + if replace_server_instances: + types.append(ClusterType.Server) + if replace_client_instances: + types.append(ClusterType.Client) + self.replace_occurrences_ops.append( + ReplaceClusterOccurrences( + cluster=replacement_cluster_class, + cluster_types=tuple(types), + ) + ) + return self + + def adds_endpoint( + self, + endpoint_id: int, + profile_id: int = zigpy.profiles.zha.PROFILE_ID, + device_type: int = 0xFF, + ) -> Self: + """Add an `AddEndpoint` operation and return self.""" + self.adds_endpoint_ops.append( + AddEndpoint( + endpoint_id=endpoint_id, + profile_id=profile_id, + device_type=device_type, + ) + ) + return self + + def removes_endpoint(self, endpoint_id: int) -> Self: + """Add a `RemoveEndpoint` operation and return self.""" + self.removes_endpoint_ops.append(RemoveEndpoint(endpoint_id=endpoint_id)) + return self + + def replaces_endpoint( + self, + endpoint_id: int, + profile_id: int = zigpy.profiles.zha.PROFILE_ID, + device_type: int = 0xFF, + ) -> Self: + """Add a `ReplaceEndpoint` operation and return self.""" + self.replaces_endpoint_ops.append( + ReplaceEndpoint( + endpoint_id=endpoint_id, + profile_id=profile_id, + device_type=device_type, + ) + ) + return self + + def enum( + self, + attribute_name: str, + enum_class: type[Enum], + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + entity_platform: EntityPlatform = EntityPlatform.SELECT, + entity_type: EntityType = EntityType.CONFIG, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + reporting_config: ReportingConfig | None = None, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing ZCLEnumMetadata and return self. + + This method allows exposing an enum based entity in Home Assistant. + """ + self._add_entity_metadata( + ZCLEnumMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=entity_platform, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + reporting_config=reporting_config, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + enum=enum_class, + attribute_name=attribute_name, + primary=primary, + ) + ) + return self + + def sensor( + self, + attribute_name: str, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + divisor: int = 1, + multiplier: int = 1, + suggested_display_precision: int = 1, + entity_type: EntityType = EntityType.STANDARD, + device_class: SensorDeviceClass | None = None, + state_class: SensorStateClass | None = None, + unit: str | None = None, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + attribute_converter: Callable[[Any], Any] | None = None, + reporting_config: ReportingConfig | None = None, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing ZCLSensorMetadata and return self. + + This method allows exposing a sensor entity in Home Assistant. + """ + self._add_entity_metadata( + ZCLSensorMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=EntityPlatform.SENSOR, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + reporting_config=reporting_config, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + attribute_name=attribute_name, + attribute_converter=attribute_converter, + divisor=divisor, + multiplier=multiplier, + suggested_display_precision=suggested_display_precision, + unit=unit, + device_class=device_class, + state_class=state_class, + primary=primary, + ) + ) + return self + + def switch( + self, + attribute_name: str, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + force_inverted: bool = False, + invert_attribute_name: str | None = None, + off_value: int = 0, + on_value: int = 1, + entity_platform: EntityPlatform = EntityPlatform.SWITCH, + entity_type: EntityType = EntityType.CONFIG, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + reporting_config: ReportingConfig | None = None, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing SwitchMetadata and return self. + + This method allows exposing a switch entity in Home Assistant. + """ + self._add_entity_metadata( + SwitchMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=entity_platform, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + reporting_config=reporting_config, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + attribute_name=attribute_name, + force_inverted=force_inverted, + invert_attribute_name=invert_attribute_name, + off_value=off_value, + on_value=on_value, + primary=primary, + ) + ) + return self + + def number( + self, + attribute_name: str, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + min_value: float | None = None, + max_value: float | None = None, + step: float | None = None, + unit: str | None = None, + mode: str | None = None, + multiplier: float | None = None, + entity_type: EntityType = EntityType.CONFIG, + device_class: NumberDeviceClass | None = None, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + reporting_config: ReportingConfig | None = None, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing NumberMetadata and return self. + + This method allows exposing a number entity in Home Assistant. + """ + self._add_entity_metadata( + NumberMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=EntityPlatform.NUMBER, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + reporting_config=reporting_config, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + attribute_name=attribute_name, + min=min_value, + max=max_value, + step=step, + unit=unit, + mode=mode, + multiplier=multiplier, + device_class=device_class, + primary=primary, + ) + ) + return self + + def binary_sensor( + self, + attribute_name: str, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + entity_type: EntityType = EntityType.DIAGNOSTIC, + device_class: BinarySensorDeviceClass | None = None, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + attribute_converter: Callable[[Any], Any] | None = None, + reporting_config: ReportingConfig | None = None, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing BinarySensorMetadata and return self. + + This method allows exposing a binary sensor entity in Home Assistant. + """ + self._add_entity_metadata( + BinarySensorMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=EntityPlatform.BINARY_SENSOR, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + reporting_config=reporting_config, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + attribute_name=attribute_name, + attribute_converter=attribute_converter, + device_class=device_class, + primary=primary, + ) + ) + return self + + def write_attr_button( + self, + attribute_name: str, + attribute_value: int, + cluster_id: int, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + entity_type: EntityType = EntityType.CONFIG, + initially_disabled: bool = False, + attribute_initialized_from_cache: bool = True, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing WriteAttributeButtonMetadata and return self. + + This method allows exposing a button entity in Home Assistant that writes + a value to an attribute when pressed. + """ + self._add_entity_metadata( + WriteAttributeButtonMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=EntityPlatform.BUTTON, + entity_type=entity_type, + initially_disabled=initially_disabled, + attribute_initialized_from_cache=attribute_initialized_from_cache, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + attribute_name=attribute_name, + attribute_value=attribute_value, + primary=primary, + ) + ) + return self + + def command_button( + self, + command_name: str, + cluster_id: int, + command_args: tuple | None = None, + command_kwargs: dict[str, Any] | None = None, + cluster_type: ClusterType = ClusterType.Server, + endpoint_id: int = 1, + entity_type: EntityType = EntityType.CONFIG, + initially_disabled: bool = False, + unique_id_suffix: str | None = None, + translation_key: str | None = None, + fallback_name: str | None = None, + primary: bool | None = None, + *, + translation_placeholders: dict[str, str] | None = None, + ) -> Self: + """Add an EntityMetadata containing ZCLCommandButtonMetadata and return self. + + This method allows exposing a button entity in Home Assistant that executes + a ZCL command when pressed. + """ + self._add_entity_metadata( + ZCLCommandButtonMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + entity_platform=EntityPlatform.BUTTON, + entity_type=entity_type, + initially_disabled=initially_disabled, + unique_id_suffix=unique_id_suffix, + translation_key=translation_key, + translation_placeholders=translation_placeholders or {}, + fallback_name=fallback_name, + command_name=command_name, + args=command_args if command_args is not None else (), + kwargs=command_kwargs if command_kwargs is not None else frozendict(), + primary=primary, + ) + ) + return self + + def device_automation_triggers( + self, device_automation_triggers: dict[tuple[str, str], dict[str, str]] + ) -> Self: + """Add device automation triggers and returns self.""" + self.device_automation_triggers_metadata.update(device_automation_triggers) + return self + + def friendly_name(self, *, model: str, manufacturer: str) -> Self: + """Rename the device.""" + self.friendly_name_metadata = FriendlyNameMetadata( + model=model, manufacturer=manufacturer + ) + return self + + def exposes_feature( + self, feature: str, config: dict[str, Any] | None = None + ) -> Self: + """Add an exposed feature.""" + self.exposes_features.append( + ExposesFeatureMetadata(feature=feature, config=config or {}) + ) + return self + + def device_alert(self, *, level: DeviceAlertLevel, message: str) -> Self: + """Add a device alert.""" + self.device_alerts.append(DeviceAlertMetadata(level=level, message=message)) + return self + + def prevent_default_entity_creation( + self, + *, + endpoint_id: int | None = None, + cluster_id: int | None = None, + cluster_type: ClusterType | None = None, + unique_id_suffix: str | None = None, + function: Callable[[Any], bool] | None = None, + ) -> Self: + """Do not create default entities.""" + if cluster_id is not None and cluster_type is None: + cluster_type = ClusterType.Server + + self.disabled_default_entities.append( + PreventDefaultEntityCreationMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + unique_id_suffix=unique_id_suffix, + function=function, + ), + ) + return self + + def change_entity_metadata( + self, + *, + endpoint_id: int | None = None, + cluster_id: int | None = None, + cluster_type: ClusterType | None = None, + unique_id_suffix: str | None = None, + function: Callable[[Any], bool] | None = None, + new_primary: bool | None = None, + new_unique_id: str | None = None, + new_translation_key: str | None = None, + new_translation_placeholders: dict[str, str] | None = None, + new_device_class: ( + BinarySensorDeviceClass | NumberDeviceClass | SensorDeviceClass | None + ) = None, + new_state_class: SensorStateClass | None = None, + new_entity_category: EntityType | None = None, + new_entity_registry_enabled_default: bool | None = None, + new_fallback_name: str | None = None, + ) -> Self: + """Change entity metadata for matching entities.""" + if cluster_id is not None and cluster_type is None: + cluster_type = ClusterType.Server + + self.changed_entity_metadata.append( + ChangedEntityMetadata( + endpoint_id=endpoint_id, + cluster_id=cluster_id, + cluster_type=cluster_type, + unique_id_suffix=unique_id_suffix, + function=function, + new_primary=new_primary, + new_unique_id=new_unique_id, + new_translation_key=new_translation_key, + new_translation_placeholders=new_translation_placeholders, + new_device_class=new_device_class, + new_state_class=new_state_class, + new_entity_category=new_entity_category, + new_entity_registry_enabled_default=new_entity_registry_enabled_default, + new_fallback_name=new_fallback_name, + ), + ) + return self + + def _compile_ops(self) -> tuple[ZigpyOp, ...]: + """Compile the accumulated cluster/endpoint operations, in v2 order.""" + ops: list[ZigpyOp] = [] + if self.device_node_descriptor is not None: + ops.append(SetNodeDescriptor(node_descriptor=self.device_node_descriptor)) + # endpoints need to be modified before clusters + ops.extend(self.removes_endpoint_ops) + ops.extend(self.adds_endpoint_ops) + ops.extend(self.replaces_endpoint_ops) + ops.extend(self.removes_ops) + ops.extend(self.adds_ops) + ops.extend(self.replaces_ops) + ops.extend(self.replace_occurrences_ops) + return tuple(ops) + + def add_to_registry(self) -> type[Device]: + """Compile the quirk into a `Device` subclass and register it.""" + if not self.manufacturer_model_metadata: + raise ValueError( + "At least one manufacturer and model must be specified for a v2 quirk." + ) + + device_match = DeviceMatch( + applies_to=tuple(self.manufacturer_model_metadata), + filters=tuple(self.filters), + firmware_version_min=self.firmware_version_min, + firmware_version_max=self.firmware_version_max, + firmware_version_allow_missing=self.firmware_version_allow_missing, + ) + + quirk_definition = QuirkDefinition( + quirk_file=str(self.quirk_file), + quirk_file_line=self.quirk_file_line, + friendly_name=self.friendly_name_metadata, + exposes_features=tuple(self.exposes_features), + device_alerts=tuple(self.device_alerts), + disabled_default_entities=tuple(self.disabled_default_entities), + changed_entity_metadata=tuple(self.changed_entity_metadata), + entity_metadata=tuple(self.entity_metadata), + device_automation_triggers=self.device_automation_triggers_metadata, + skip_configuration=self.skip_device_configuration, + ) + + base = self.custom_device_class if self.custom_device_class else Device + manufacturer, model = self.manufacturer_model_metadata[0] + name = re.sub(r"\W|^(?=\d)", "_", f"Quirk_{manufacturer}_{model}") + + quirk_cls = type( + name, + (base,), + { + "__module__": self.quirk_module, + "_device_match": device_match, + "_zigpy_device_class": self.custom_zigpy_device_class, + "_zigpy_ops": self._compile_ops(), + "_quirk_definition": quirk_definition, + }, + ) + + self.registry.register(quirk_cls) + + if self in UNBUILT_QUIRK_BUILDERS: + UNBUILT_QUIRK_BUILDERS.remove(self) + + return quirk_cls + + def clone(self, omit_man_model_data: bool = True) -> Self: + """Clone this QuirkBuilder potentially omitting manufacturer and model data.""" + new_builder = deepcopy(self) + new_builder.registry = self.registry + if omit_man_model_data: + new_builder.manufacturer_model_metadata = [] + UNBUILT_QUIRK_BUILDERS.append(new_builder) + return new_builder From bc57fc6a288014d64adf47f305bf8954b59e89b6 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:51:16 -0400 Subject: [PATCH 10/41] Migrate quirk logic to zha-device-handlers? --- tests/common.py | 6 +- tests/conftest.py | 4 +- tests/test_climate.py | 4 +- tests/test_discover.py | 41 +- tests/test_switch.py | 8 +- zha/application/discovery.py | 140 +-- zha/application/gateway.py | 41 +- zha/application/helpers.py | 6 + zha/application/platforms/__init__.py | 82 +- .../platforms/binary_sensor/__init__.py | 31 +- zha/application/platforms/button/__init__.py | 45 +- zha/application/platforms/number/__init__.py | 70 +- zha/application/platforms/select.py | 15 +- zha/application/platforms/sensor/__init__.py | 68 +- zha/application/platforms/switch.py | 34 +- zha/quirks/__init__.py | 404 ++------ zha/quirks/metadata.py | 245 ----- zha/quirks/v2.py | 890 ------------------ zha/zigbee/device.py | 103 +- 19 files changed, 383 insertions(+), 1854 deletions(-) delete mode 100644 zha/quirks/metadata.py delete mode 100644 zha/quirks/v2.py diff --git a/tests/common.py b/tests/common.py index 78c6c6693..ea86dbd5d 100644 --- a/tests/common.py +++ b/tests/common.py @@ -19,7 +19,7 @@ import zigpy.zdo.types as zdo_t from zha.application import Platform -from zha.quirks import resolve_device +from zha.quirks import resolve_zigpy_device from zha.application.gateway import Gateway from zha.application.platforms import BaseEntity, GroupEntity, PlatformEntity from zha.zigbee.device import Device @@ -423,7 +423,7 @@ def zigpy_device_from_device_data( if quirk: device = quirk(app, device.ieee, device.nwk, device) else: - device = resolve_device(device) + device = resolve_zigpy_device(device) for epid, ep in device_data["endpoints"].items(): try: @@ -601,7 +601,7 @@ def create_mock_zigpy_device( if quirk: device = quirk(zigpy_app_controller, device.ieee, device.nwk, device) else: - device = resolve_device(device) + device = resolve_zigpy_device(device) if patch_cluster: for endpoint in (ep for epid, ep in device.endpoints.items() if epid): diff --git a/tests/conftest.py b/tests/conftest.py index f2d1a3e40..be3ad1576 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -353,9 +353,7 @@ def globally_load_quirks(): zhaquirks.setup() - # Disable gateway built in quirks loading - with patch("zha.application.gateway.setup_quirks"): - yield + yield def pytest_collection_modifyitems(config, items): diff --git a/tests/test_climate.py b/tests/test_climate.py index 437ff2d9c..4fc3a1770 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -39,7 +39,7 @@ PRESET_TEMP_MANUAL, ) from zha.application.gateway import Gateway -from zha.quirks import resolve_device +from zha.quirks import resolve_zigpy_device from zha.application.platforms.climate import ( HVAC_MODE_2_SYSTEM, SEQ_OF_OPERATION, @@ -1657,7 +1657,7 @@ async def test_thermostat_quirkv2_local_temperature_calibration_config_overwrite .add_to_registry() ) - zigpy_device = resolve_device(zigpy_device) + zigpy_device = resolve_zigpy_device(zigpy_device) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert zha_device.model == "FakeModel" diff --git a/tests/test_discover.py b/tests/test_discover.py index e696ee917..d1569f2e0 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -1,7 +1,6 @@ """Test ZHA device discovery.""" import asyncio -import attrs from collections import defaultdict from collections.abc import Callable import contextlib @@ -13,8 +12,15 @@ from unittest.mock import AsyncMock import warnings +import attrs import pytest from zhaquirks.ikea import PowerConfig1CRCluster, ScenesCluster +from zhaquirks.v2 import QuirkBuilder +from zhaquirks.v2.metadata import ( + BinarySensorMetadata, + NumberMetadata, + ZCLSensorMetadata, +) from zhaquirks.xiaomi import ( BasicCluster, LocalIlluminanceMeasurementCluster, @@ -27,14 +33,6 @@ import zigpy.device import zigpy.profiles.zha import zigpy.quirks -from zigpy.quirks.v2 import ( - BinarySensorMetadata, - EntityType, - NumberMetadata, - QuirkBuilder, - ZCLSensorMetadata, -) -from zigpy.quirks.v2.homeassistant import UnitOfTime import zigpy.types from zigpy.zcl import ClusterType import zigpy.zcl.clusters.closures @@ -56,18 +54,19 @@ zigpy_device_from_device_data, zigpy_device_from_json, ) -from zha.application import Platform +from zha.application import EntityType, Platform from zha.application.gateway import Gateway -from zha.quirks import ( - DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, - ZHA_DEVICE_CLASS_ATTRIBUTE, - resolve_device, -) from zha.application.helpers import DeviceOverridesConfiguration from zha.application.platforms import PlatformEntity, binary_sensor, sensor from zha.application.platforms.const import PHILIPS_REMOTE_CLUSTER from zha.application.platforms.light import HueLight from zha.application.platforms.number import BaseNumber, NumberMode +from zha.quirks import ( + DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, + ZHA_DEVICE_FACTORY_ATTRIBUTE, + resolve_zigpy_device, +) +from zha.units import UnitOfTime def _get_identify_cluster(zigpy_device): @@ -247,7 +246,7 @@ async def test_quirks_v2_entity_discovery( .add_to_registry() ) - zigpy_device = resolve_device(zigpy_device) + zigpy_device = resolve_zigpy_device(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -349,7 +348,7 @@ class FakeXiaomiAqaraDriverE1(XiaomiAqaraDriverE1): manufacturer="LUMI", model="lumi.curtain.agl006", ) - aqara_E1_device = resolve_device(aqara_E1_device) + aqara_E1_device = resolve_zigpy_device(aqara_E1_device) aqara_E1_device.endpoints[1].opple_cluster.PLUGGED_ATTR_READS = { "hand_open": 0, @@ -474,7 +473,7 @@ def _get_test_device( quirk_builder.add_to_registry() - zigpy_device = resolve_device(zigpy_device) + zigpy_device = resolve_zigpy_device(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -496,7 +495,7 @@ async def test_quirks_v2_entity_no_metadata( zigpy_device = _get_test_device( zha_gateway, "Ikea of Sweden2", "TRADFRI remote control2" ) - quirk_cls = getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE) + quirk_cls = getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE) quirk_cls._quirk_definition = attrs.evolve( quirk_cls._quirk_definition, entity_metadata=() ) @@ -524,7 +523,7 @@ class UnknownEntityMetadata: cluster_type = ClusterType.Server entity_platform = Platform.UPDATE - quirk_cls = getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE) + quirk_cls = getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE) quirk_cls._quirk_definition = attrs.evolve( quirk_cls._quirk_definition, entity_metadata=( @@ -647,7 +646,7 @@ async def test_quirks_v2_metadata_bad_device_classes( assert expected_exception_string in caplog.text # remove the quirk so we don't pollute the rest of the tests - QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE)) + QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE)) async def test_quirks_v2_fallback_name(zha_gateway: Gateway) -> None: diff --git a/tests/test_switch.py b/tests/test_switch.py index 68149eea0..1b497306f 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -39,7 +39,7 @@ ) from zha.application import Platform from zha.application.gateway import Gateway -from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_device +from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_zigpy_device from zha.application.platforms import GroupEntity, PlatformEntity from zha.exceptions import ZHAException from zha.zigbee.device import Device @@ -510,7 +510,7 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> .add_to_registry() ) - zigpy_device_ = resolve_device(zigpy_dev) + zigpy_device_ = resolve_zigpy_device(zigpy_dev) assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer @@ -591,7 +591,7 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( .add_to_registry() ) - zigpy_device_ = resolve_device(zigpy_dev) + zigpy_device_ = resolve_zigpy_device(zigpy_dev) assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer @@ -672,7 +672,7 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( .add_to_registry() ) - zigpy_device_ = resolve_device(zigpy_dev) + zigpy_device_ = resolve_zigpy_device(zigpy_dev) assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer diff --git a/zha/application/discovery.py b/zha/application/discovery.py index d7cd366a2..c5ce8bc8f 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -9,9 +9,9 @@ import logging from typing import TYPE_CHECKING, Any -from zigpy.zcl import Cluster, ClusterType, ReportingConfig +from zigpy.zcl import Cluster -from zha.application import Platform, const as zha_const +from zha.application import Platform from zha.application.platforms import ( # noqa: F401 pylint: disable=unused-import ENTITY_REGISTRY, GROUP_ENTITY_REGISTRY, @@ -38,20 +38,10 @@ update, virtual, ) -from zha.quirks.metadata import ( - BinarySensorMetadata, - NumberMetadata, - SwitchMetadata, - WriteAttributeButtonMetadata, - ZCLCommandButtonMetadata, - ZCLEnumMetadata, - ZCLSensorMetadata, -) from zha.zigbee.group import Group if TYPE_CHECKING: from zha.application.platforms import GroupEntity - from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint _LOGGER = logging.getLogger(__name__) @@ -98,18 +88,6 @@ def _pick_primary_cluster(endpoint: Endpoint, match: ClusterMatch) -> Cluster | return None -QUIRKS_ENTITY_META_TO_ENTITY_CLASS = { - (Platform.BUTTON, WriteAttributeButtonMetadata): button.WriteAttributeButton, - (Platform.BUTTON, ZCLCommandButtonMetadata): button.Button, - (Platform.BINARY_SENSOR, BinarySensorMetadata): binary_sensor.BinarySensor, - (Platform.SENSOR, ZCLEnumMetadata): sensor.EnumSensor, - (Platform.SENSOR, ZCLSensorMetadata): sensor.Sensor, - (Platform.SELECT, ZCLEnumMetadata): select.ZCLEnumSelectEntity, - (Platform.NUMBER, NumberMetadata): number.NumberConfigurationEntity, - (Platform.SWITCH, SwitchMetadata): switch.ConfigurableAttributeSwitch, -} - - def ignore_exceptions_during_iteration[**P, T]( func: Callable[P, Iterator[T]], ) -> Callable[P, Iterator[T]]: @@ -167,120 +145,6 @@ def discover_group_entities(group: Group) -> Iterator[GroupEntity]: yield group_entity_class(group) -def discover_quirks_v2_entities(device: Device) -> Iterator[PlatformEntity]: - """Discover entities for a ZHA device exposed by quirks v2 metadata.""" - if device.quirk_metadata is None or not device.quirk_metadata.entity_metadata: - _LOGGER.debug( - "Device: %s-%s does not expose any quirks v2 entities", - str(device.ieee), - device.name, - ) - return - - for entity_metadata in device.quirk_metadata.entity_metadata: - endpoint_id = entity_metadata.endpoint_id - cluster_id = entity_metadata.cluster_id - cluster_type = entity_metadata.cluster_type - - if endpoint_id not in device.endpoints: - _LOGGER.warning( - "Device: %s-%s does not have an endpoint with id: %s - unable to " - "create entity with metadata: %s", - str(device.ieee), - device.name, - endpoint_id, - entity_metadata, - ) - continue - - endpoint: Endpoint = device.endpoints[endpoint_id] - cluster = ( - endpoint.zigpy_endpoint.in_clusters.get(cluster_id) - if cluster_type is ClusterType.Server - else endpoint.zigpy_endpoint.out_clusters.get(cluster_id) - ) - - if cluster is None: - _LOGGER.warning( - "Device: %s-%s does not have a cluster with id: %s - " - "unable to create entity with metadata: %s", - str(device.ieee), - device.name, - cluster_id, - entity_metadata, - ) - continue - - platform = Platform(entity_metadata.entity_platform.value) - metadata_type = type(entity_metadata) - entity_class = QUIRKS_ENTITY_META_TO_ENTITY_CLASS.get((platform, metadata_type)) - - if entity_class is None: - _LOGGER.warning( - "Device: %s-%s has an entity with details: %s that does not" - " have an entity class mapping - unable to create entity", - str(device.ieee), - device.name, - {zha_const.ENTITY_METADATA: entity_metadata}, - ) - continue - - entity = entity_class( - endpoint=endpoint, - device=device, - cluster=cluster, - entity_metadata=entity_metadata, - ) - - # Translate quirks v2 reporting/attribute-init metadata into a - # per-instance cluster config that the cluster_config aggregator - # picks up alongside the entity's normal (class-level) declarations. - if attr_name := getattr(entity_metadata, "attribute_name", None): - rep_conf = getattr(entity_metadata, "reporting_config", None) - if rep_conf is not None: - attr_config = AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=rep_conf.min_interval, - max_interval=rep_conf.max_interval, - reportable_change=rep_conf.reportable_change, - ), - ) - bind = True - else: - attr_config = AttrConfig( - read_on_startup=( - not entity_metadata.attribute_initialized_from_cache - ), - ) - bind = False - - # Keep attr_name as a string here - quirks v2 entities can reference - # attribute names that aren't part of the cluster's attribute schema - # (e.g. manufacturer-specific extensions); aggregation/configure handle - # both name and ZCLAttributeDef. - config = { - cluster.cluster_id: ClusterConfig( - bind=bind, - attributes={attr_name: attr_config}, - ), - } - - if cluster_type is ClusterType.Server: - entity._server_cluster_config = config - else: - entity._client_cluster_config = config - - yield entity - - _LOGGER.debug( - "'%s' platform -> '%s' using cluster 0x%04x", - platform, - entity_class.__name__, - cluster.cluster_id, - ) - - def _is_renamed_cluster(cluster: Cluster) -> bool: """Return True if a quirk has renamed the cluster's ep_attribute.""" standard = Cluster._registry.get(cluster.cluster_id) diff --git a/zha/application/gateway.py b/zha/application/gateway.py index 672d0b786..c1a5532ed 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -9,11 +9,9 @@ from datetime import timedelta from enum import Enum import logging -from pathlib import Path import time from typing import Any, Final, Self, TypeVar, cast -from zhaquirks import setup as setup_quirks from zigpy.application import ControllerApplication from zigpy.config import ( CONF_DEVICE, @@ -55,8 +53,7 @@ gather_with_limited_concurrency, ) from zha.event import EventBase -from zha.quirks import DEVICE_REGISTRY, resolve_device -from zha.quirks.v2 import UNBUILT_QUIRK_BUILDERS +from zha.quirks import resolve_zigpy_device from zha.zigbee.device import Device, DeviceInfo, DeviceStatus, ExtendedDeviceInfo from zha.zigbee.group import Group, GroupInfo, GroupMemberReference @@ -224,24 +221,22 @@ async def async_from_config(cls, config: ZHAData) -> Self: """Create an instance of a gateway from config objects.""" instance = cls(config) - if config.config.quirks_configuration.enabled: - for quirk in UNBUILT_QUIRK_BUILDERS: - # v2 quirks with no manufacturer model metadata explicitly do not call - # add_to_registry. They are used to share code between v2 quirks. - if quirk.manufacturer_model_metadata: - _LOGGER.warning( - "Found a v2 quirk that was not added to the registry: %s", - quirk, - ) - quirk.add_to_registry() - - UNBUILT_QUIRK_BUILDERS.clear() - - custom_quirks_path = config.config.quirks_configuration.custom_quirks_path - if custom_quirks_path is not None: - DEVICE_REGISTRY.purge_custom_quirks(Path(custom_quirks_path)) - - await instance.async_add_executor_job(setup_quirks, custom_quirks_path) + # Load quirks via the injected provider (e.g. `zhaquirks.setup`). ZHA + # never imports a quirks package itself; the consumer supplies it. The + # provider owns registry population, the custom-quirks purge and the + # unbuilt-builder flush — see `zhaquirks.setup`. + quirks_config = config.config.quirks_configuration + if quirks_config.enabled: + if quirks_config.setup_function is None: + _LOGGER.warning( + "Quirks are enabled but no setup function was provided; " + "no quirks will be loaded" + ) + else: + await instance.async_add_executor_job( + quirks_config.setup_function, + quirks_config.custom_quirks_path, + ) return instance @@ -254,7 +249,7 @@ async def _async_initialize(self) -> None: config=app_config, auto_form=False, start_radio=False, - device_resolver=resolve_device, + device_resolver=resolve_zigpy_device, ) await self.application_controller.startup(auto_form=True) diff --git a/zha/application/helpers.py b/zha/application/helpers.py index 7a8a81911..d4b49ac87 100644 --- a/zha/application/helpers.py +++ b/zha/application/helpers.py @@ -374,6 +374,12 @@ class QuirksConfiguration: enabled: bool = dataclasses.field(default=True) custom_quirks_path: str | None = dataclasses.field(default=None) + # Injected by the consumer (e.g. the Home Assistant integration) to load the + # quirks provider, typically `zhaquirks.setup`. ZHA itself never imports a + # quirks package; when `enabled` is True this must be set or no quirks load. + setup_function: Callable[[str | None], None] | None = dataclasses.field( + default=None + ) @dataclass(kw_only=True, slots=True) diff --git a/zha/application/platforms/__init__.py b/zha/application/platforms/__init__.py index 5bbfb2c9e..3c0c210cd 100644 --- a/zha/application/platforms/__init__.py +++ b/zha/application/platforms/__init__.py @@ -31,7 +31,6 @@ from zha.mixins import LogMixin if TYPE_CHECKING: - from zha.quirks.metadata import EntityMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint from zha.zigbee.group import Group @@ -495,16 +494,37 @@ def __init__( device: Device, *, cluster: zigpy.zcl.Cluster, - entity_metadata: EntityMetadata | None = None, + from_quirk: bool = False, + fallback_name: str | None = None, + translation_key: str | None = None, + translation_placeholders: Mapping[str, str] | None = None, + unique_id_suffix: str | None = None, + entity_type: EntityType | None = None, + primary: bool | None = None, + initially_disabled: bool = False, legacy_discovery_unique_id: str | None = None, **kwargs: Any, ): - """Initialize the platform entity.""" - if entity_metadata is not None: - self._init_from_quirks_metadata(entity_metadata) + """Initialize the platform entity. + + Quirk entities are constructed with `from_quirk=True` and the generic + config keywords (`fallback_name`, `translation_key`, `entity_type`, etc.); + the platform subclasses add their own keywords. Default-discovery + entities pass none of these. + """ + if from_quirk: + self._apply_quirk_entity_config( + fallback_name=fallback_name, + translation_key=translation_key, + translation_placeholders=translation_placeholders, + unique_id_suffix=unique_id_suffix, + entity_type=entity_type, + primary=primary, + initially_disabled=initially_disabled, + ) if legacy_discovery_unique_id is None: - if entity_metadata is not None: + if from_quirk: legacy_discovery_unique_id = f"{device.ieee}-{endpoint.id}" else: legacy_discovery_unique_id = ( @@ -522,45 +542,45 @@ def __init__( self._endpoint = endpoint self._cluster: zigpy.zcl.Cluster = cluster - def _init_from_quirks_metadata(self, entity_metadata: EntityMetadata) -> None: - """Init this entity from the quirks metadata.""" - if entity_metadata.initially_disabled: + def _apply_quirk_entity_config( + self, + *, + fallback_name: str | None, + translation_key: str | None, + translation_placeholders: Mapping[str, str] | None, + unique_id_suffix: str | None, + entity_type: EntityType | None, + primary: bool | None, + initially_disabled: bool, + ) -> None: + """Apply the generic quirk entity configuration keywords.""" + if initially_disabled: self._attr_entity_registry_enabled_default = False - # v2 quirks entities are assumed to always be supported + # quirk entities are assumed to always be supported self._attr_always_supported = True - has_attribute_name = hasattr(entity_metadata, "attribute_name") - has_command_name = hasattr(entity_metadata, "command_name") - has_fallback_name = hasattr(entity_metadata, "fallback_name") + if fallback_name: + self._attr_fallback_name = fallback_name - if has_fallback_name: - self._attr_fallback_name = entity_metadata.fallback_name + if translation_key: + self._attr_translation_key = translation_key - if entity_metadata.translation_key: - self._attr_translation_key = entity_metadata.translation_key - - if entity_metadata.translation_placeholders: - self._attr_translation_placeholders = ( - entity_metadata.translation_placeholders - ) + if translation_placeholders: + self._attr_translation_placeholders = translation_placeholders - if unique_id_suffix := entity_metadata.unique_id_suffix: + if unique_id_suffix is not None: self._unique_id_suffix = unique_id_suffix - elif has_attribute_name: - self._unique_id_suffix = entity_metadata.attribute_name - elif has_command_name: - self._unique_id_suffix = entity_metadata.command_name - if entity_metadata.entity_type == EntityType.CONFIG: + if entity_type == EntityType.CONFIG: self._attr_entity_category = EntityCategory.CONFIG - elif entity_metadata.entity_type == EntityType.DIAGNOSTIC: + elif entity_type == EntityType.DIAGNOSTIC: self._attr_entity_category = EntityCategory.DIAGNOSTIC else: self._attr_entity_category = None - if entity_metadata.primary is not None: - self._attr_primary = entity_metadata.primary + if primary is not None: + self._attr_primary = primary @cached_property def identifiers(self) -> PlatformEntityIdentifiers: diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index 79b5df03f..35f9d658a 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -48,7 +48,6 @@ from zha.application.platforms.helpers import validate_device_class if TYPE_CHECKING: - from zha.quirks.metadata import BinarySensorMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint @@ -92,9 +91,25 @@ def __init__( self, endpoint: Endpoint, device: Device, + *, + attribute_name: str | None = None, + attribute_converter: Callable[[Any], Any] | None = None, + device_class: BinarySensorDeviceClass | None = None, **kwargs, ) -> None: """Initialize the ZHA binary sensor.""" + if attribute_name is not None: + self._attribute_name = attribute_name + if attribute_converter is not None: + self._attribute_converter = attribute_converter + if device_class is not None: + self._attr_device_class = validate_device_class( + BinarySensorDeviceClass, + device_class, + Platform.BINARY_SENSOR.value, + _LOGGER, + ) + super().__init__(endpoint=endpoint, device=device, **kwargs) self._state: bool = self.is_on self.recompute_capabilities() @@ -119,20 +134,6 @@ def on_add(self) -> None: ) ) - def _init_from_quirks_metadata(self, entity_metadata: BinarySensorMetadata) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - if entity_metadata.attribute_converter is not None: - self._attribute_converter = entity_metadata.attribute_converter - if entity_metadata.device_class is not None: - self._attr_device_class = validate_device_class( - BinarySensorDeviceClass, - entity_metadata.device_class, - Platform.BINARY_SENSOR.value, - _LOGGER, - ) - @functools.cached_property def info_object(self) -> BinarySensorEntityInfo: """Return a representation of the binary sensor.""" diff --git a/zha/application/platforms/button/__init__.py b/zha/application/platforms/button/__init__.py index b5df2cc2d..bae359918 100644 --- a/zha/application/platforms/button/__init__.py +++ b/zha/application/platforms/button/__init__.py @@ -27,10 +27,6 @@ ) if TYPE_CHECKING: - from zha.quirks.metadata import ( - WriteAttributeButtonMetadata, - ZCLCommandButtonMetadata, - ) from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint @@ -81,14 +77,25 @@ class Button(BaseButton): _args: list[Any] _kwargs: dict[str, Any] - def _init_from_quirks_metadata( - self, entity_metadata: ZCLCommandButtonMetadata + def __init__( + self, + endpoint: Endpoint, + device: Device, + *, + command_name: str | None = None, + command_args: tuple | None = None, + command_kwargs: dict[str, Any] | None = None, + **kwargs: Any, ) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._command_name = entity_metadata.command_name - self._args = entity_metadata.args - self._kwargs = entity_metadata.kwargs + """Init this button.""" + if command_name is not None: + self._command_name = command_name + if command_args is not None: + self._args = command_args + if command_kwargs is not None: + self._kwargs = command_kwargs + + super().__init__(endpoint=endpoint, device=device, **kwargs) @functools.cached_property def info_object(self) -> CommandButtonEntityInfo: @@ -149,20 +156,20 @@ def __init__( self, endpoint: Endpoint, device: Device, + *, + attribute_name: str | None = None, + attribute_value: Any = None, **kwargs: Any, ) -> None: """Init this button.""" + if attribute_name is not None: + self._attribute_name = attribute_name + if attribute_value is not None: + self._attribute_value = attribute_value + super().__init__(endpoint=endpoint, device=device, **kwargs) self.recompute_capabilities() - def _init_from_quirks_metadata( - self, entity_metadata: WriteAttributeButtonMetadata - ) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - self._attribute_value = entity_metadata.attribute_value - @functools.cached_property def info_object(self) -> WriteAttributeButtonEntityInfo: """Return a representation of the button.""" diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index cdd1271f0..ef7e9cce6 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -46,9 +46,8 @@ from zha.units import UnitOfMass, UnitOfTemperature, UnitOfTime if TYPE_CHECKING: - from zha.quirks.metadata import NumberMetadata - - pass + from zha.zigbee.device import Device + from zha.zigbee.endpoint import Endpoint _LOGGER = logging.getLogger(__name__) @@ -261,6 +260,46 @@ class NumberConfigurationEntity(BaseNumber): _multiplier: float = 1 _attribute_name: str + def __init__( + self, + endpoint: Endpoint, + device: Device, + *, + attribute_name: str | None = None, + min_value: float | None = None, + max_value: float | None = None, + step: float | None = None, + multiplier: float | None = None, + device_class: NumberDeviceClass | None = None, + unit: str | None = None, + mode: str | None = None, + **kwargs: Any, + ) -> None: + """Init this number configuration entity.""" + if attribute_name is not None: + self._attribute_name = attribute_name + if min_value is not None: + self._attr_native_min_value = min_value + if max_value is not None: + self._attr_native_max_value = max_value + if step is not None: + self._attr_native_step = step + if multiplier is not None: + self._multiplier = multiplier + if device_class is not None: + self._attr_device_class = validate_device_class( + NumberDeviceClass, + device_class, + Platform.NUMBER.value, + _LOGGER, + ) + if unit is not None: + self._attr_native_unit_of_measurement = unit + if mode is not None and mode in NumberMode: + self._attr_mode = NumberMode(mode) + + super().__init__(endpoint=endpoint, device=device, **kwargs) + def _is_supported(self) -> bool: """Return if the entity is supported for the device, internal.""" if ( @@ -292,31 +331,6 @@ def on_add(self) -> None: ) ) - def _init_from_quirks_metadata(self, entity_metadata: NumberMetadata) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - - if entity_metadata.min is not None: - self._attr_native_min_value = entity_metadata.min - if entity_metadata.max is not None: - self._attr_native_max_value = entity_metadata.max - if entity_metadata.step is not None: - self._attr_native_step = entity_metadata.step - if entity_metadata.multiplier is not None: - self._multiplier = entity_metadata.multiplier - if entity_metadata.device_class is not None: - self._attr_device_class = validate_device_class( - NumberDeviceClass, - entity_metadata.device_class, - Platform.NUMBER.value, - _LOGGER, - ) - if entity_metadata.unit is not None: - self._attr_native_unit_of_measurement = entity_metadata.unit - if entity_metadata.mode in NumberMode: - self._attr_mode = NumberMode(entity_metadata.mode) - @property def state(self) -> dict[str, Any]: """Return the state of the entity.""" diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index b8195c548..893917cf5 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -59,7 +59,6 @@ from zha.application.platforms.siren import AdvancedSiren if TYPE_CHECKING: - from zha.quirks.metadata import ZCLEnumMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint @@ -248,9 +247,17 @@ def __init__( self, endpoint: Endpoint, device: Device, + *, + attribute_name: str | None = None, + enum: type[Enum] | None = None, **kwargs: Any, ) -> None: """Init this select entity.""" + if attribute_name is not None: + self._attribute_name = attribute_name + if enum is not None: + self._enum = enum + super().__init__(endpoint=endpoint, device=device, **kwargs) self._attr_options = [entry.name.replace("_", " ") for entry in self._enum] @@ -284,12 +291,6 @@ def _is_supported(self) -> bool: return super()._is_supported() - def _init_from_quirks_metadata(self, entity_metadata: ZCLEnumMetadata) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - self._enum = entity_metadata.enum - @functools.cached_property def info_object(self) -> EnumSelectInfo: """Return a representation of the select.""" diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 206a7b220..53f13ab12 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -131,7 +131,6 @@ ) if TYPE_CHECKING: - from zha.quirks.metadata import ZCLEnumMetadata, ZCLSensorMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint @@ -245,11 +244,39 @@ def __init__( self, endpoint: Endpoint, device: Device, + *, + attribute_name: str | None = None, + attribute_converter: typing.Callable[[typing.Any], typing.Any] | None = None, + divisor: int | None = None, + multiplier: int | None = None, + device_class: SensorDeviceClass | None = None, + state_class: SensorStateClass | None = None, + unit: str | None = None, **kwargs: Any, ) -> None: """Init this sensor.""" self._attr_def: foundation.ZCLAttributeDef | None = None + if attribute_name is not None: + self._attribute_name = attribute_name + if attribute_converter is not None: + self._attribute_converter = attribute_converter + if divisor is not None and divisor != 1: + self._divisor = divisor + if multiplier is not None and multiplier != 1: + self._multiplier = multiplier + if device_class is not None: + self._attr_device_class = validate_device_class( + SensorDeviceClass, + device_class, + Platform.SENSOR.value, + _LOGGER, + ) + if state_class is not None: + self._attr_state_class = self._validate_state_class(state_class) + if unit is not None: + self._attr_native_unit_of_measurement = unit + super().__init__(endpoint=endpoint, device=device, **kwargs) # After super() for quirks v2 entities @@ -309,30 +336,6 @@ def _validate_state_class( ) return None - def _init_from_quirks_metadata(self, entity_metadata: ZCLSensorMetadata) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - if entity_metadata.attribute_converter is not None: - self._attribute_converter = entity_metadata.attribute_converter - if entity_metadata.divisor is not None and entity_metadata.divisor != 1: - self._divisor = entity_metadata.divisor - if entity_metadata.multiplier is not None and entity_metadata.multiplier != 1: - self._multiplier = entity_metadata.multiplier - if entity_metadata.device_class is not None: - self._attr_device_class = validate_device_class( - SensorDeviceClass, - entity_metadata.device_class, - Platform.SENSOR.value, - _LOGGER, - ) - if entity_metadata.state_class is not None: - self._attr_state_class = self._validate_state_class( - entity_metadata.state_class - ) - if entity_metadata.unit is not None: - self._attr_native_unit_of_measurement = entity_metadata.unit - @property def native_value(self) -> date | datetime | str | int | float | None: """Return the state of the entity.""" @@ -531,22 +534,23 @@ def __init__( self, endpoint: Endpoint, device: Device, + *, + attribute_name: str | None = None, + enum: type[enum.Enum] | None = None, **kwargs: Any, ) -> None: """Init this sensor.""" + if attribute_name is not None: + self._attribute_name = attribute_name + if enum is not None: + self._enum = enum + super().__init__(endpoint=endpoint, device=device, **kwargs) self._attr_options = [e.name for e in self._enum] # XXX: This class is not meant to be initialized directly, as `unique_id` # depends on the value of `_attribute_name` - def _init_from_quirks_metadata(self, entity_metadata: ZCLEnumMetadata) -> None: - """Init this entity from the quirks metadata.""" - self._attribute_name = entity_metadata.attribute_name - self._enum = entity_metadata.enum - - PlatformEntity._init_from_quirks_metadata(self, entity_metadata) # pylint: disable=protected-access - def formatter(self, value: int) -> str | None: """Use name of enum.""" assert self._enum is not None diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index 19bcade18..381a870f6 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -51,7 +51,6 @@ from zha.zigbee.group import Group if TYPE_CHECKING: - from zha.quirks.metadata import EntityMetadata, SwitchMetadata from zha.zigbee.device import Device from zha.zigbee.endpoint import Endpoint @@ -404,12 +403,17 @@ def __init__( device: Device, *, cluster: zigpy.zcl.Cluster, - entity_metadata: EntityMetadata | None = None, + from_quirk: bool = False, + attribute_name: str | None = None, + invert_attribute_name: str | None = None, + force_inverted: bool = False, + off_value: int = 0, + on_value: int = 1, legacy_discovery_unique_id: str | None = None, **kwargs: Any, ) -> None: - """Init this number configuration entity.""" - if legacy_discovery_unique_id is None and entity_metadata is None: + """Init this switch configuration entity.""" + if legacy_discovery_unique_id is None and not from_quirk: legacy_discovery_unique_id = ( f"{endpoint.device.ieee}-{endpoint.id}" if ( @@ -425,11 +429,20 @@ def __init__( else f"{endpoint.device.ieee}-{endpoint.id}-{int(cluster.cluster_id)}" ) + if attribute_name is not None: + self._attribute_name = attribute_name + if invert_attribute_name: + self._inverter_attribute_name = invert_attribute_name + if force_inverted: + self._force_inverted = force_inverted + self._off_value = off_value + self._on_value = on_value + super().__init__( endpoint=endpoint, device=device, cluster=cluster, - entity_metadata=entity_metadata, + from_quirk=from_quirk, legacy_discovery_unique_id=legacy_discovery_unique_id, **kwargs, ) @@ -449,17 +462,6 @@ def on_add(self) -> None: ) ) - def _init_from_quirks_metadata(self, entity_metadata: SwitchMetadata) -> None: - """Init this entity from the quirks metadata.""" - super()._init_from_quirks_metadata(entity_metadata) - self._attribute_name = entity_metadata.attribute_name - if entity_metadata.invert_attribute_name: - self._inverter_attribute_name = entity_metadata.invert_attribute_name - if entity_metadata.force_inverted: - self._force_inverted = entity_metadata.force_inverted - self._off_value = entity_metadata.off_value - self._on_value = entity_metadata.on_value - def _is_supported(self) -> bool: if ( self._attribute_name not in self._cluster.attributes_by_name diff --git a/zha/quirks/__init__.py b/zha/quirks/__init__.py index fda359f86..5d94c7c0e 100644 --- a/zha/quirks/__init__.py +++ b/zha/quirks/__init__.py @@ -17,32 +17,27 @@ from collections import defaultdict from collections.abc import Callable -from dataclasses import dataclass, field +from dataclasses import dataclass import inspect import logging from pathlib import Path -from typing import TYPE_CHECKING, Any, NamedTuple, Protocol +from typing import TYPE_CHECKING, NamedTuple import zigpy.device import zigpy.profiles.zha import zigpy.quirks -from zigpy.zcl import Cluster, ClusterType +from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import Ota -from zigpy.zcl.foundation import ZCLAttributeDef -from zigpy.zdo.types import NodeDescriptor if TYPE_CHECKING: from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) +QUIRK_REGISTRY_ENTRY_ATTR = "_quirk_registry_entry" FilterType = Callable[[zigpy.device.Device], bool] -# Attribute stamped onto a zigpy device by `resolve_device` so that -# `Device.new` dispatches to the class that already matched during resolution, -# instead of running a second, potentially divergent, match pass. -ZHA_DEVICE_CLASS_ATTRIBUTE = "zha_device_class" - +DEVICE_REGISTRY: DeviceRegistry class ModelInfo(NamedTuple): """A (manufacturer, model) pair to match. `None` is a wildcard.""" @@ -51,9 +46,7 @@ class ModelInfo(NamedTuple): model: str | None -def _read_current_firmware_version( - zigpy_device: zigpy.device.Device, -) -> int | None: +def _read_current_firmware_version(zigpy_device: zigpy.device.Device) -> int | None: """Read `current_file_version` from the device's OTA cluster, or None.""" try: ota = zigpy_device.find_cluster( @@ -81,7 +74,7 @@ class DeviceMatch: def matches(self, zigpy_device: zigpy.device.Device) -> bool: """Return True if `zigpy_device` satisfies all criteria.""" - if not any( + if self.applies_to and not any( (manufacturer is None or manufacturer == zigpy_device.manufacturer) and (model is None or model == zigpy_device.model) for manufacturer, model in self.applies_to @@ -110,337 +103,112 @@ def matches(self, zigpy_device: zigpy.device.Device) -> bool: return True -class ZigpyOp(Protocol): - """A single modification applied to a zigpy device during resolution.""" - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - - -@dataclass(frozen=True) -class AddCluster: - """Add a cluster to an endpoint. - - `cluster` is either a bare cluster id or a `Cluster` subclass. When - `constant_attributes` is provided (mapping `ZCLAttributeDef` to value), the - values are served by the cluster without contacting the device; this - requires `cluster` to be a `CustomCluster` subclass. - """ - - cluster: int | type[Cluster] - endpoint_id: int = 1 - cluster_type: ClusterType = ClusterType.Server - constant_attributes: dict[ZCLAttributeDef, Any] = field(default_factory=dict) - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - endpoint = device.endpoints[self.endpoint_id] - is_server = self.cluster_type == ClusterType.Server - - if isinstance(self.cluster, int): - cluster = None - cluster_id = self.cluster - else: - cluster = self.cluster(endpoint, is_server=is_server) - cluster_id = cluster.cluster_id - - if is_server: - cluster = endpoint.add_input_cluster(cluster_id, cluster) - else: - cluster = endpoint.add_output_cluster(cluster_id, cluster) - - if self.constant_attributes: - cluster._CONSTANT_ATTRIBUTES = { - attribute.id: value - for attribute, value in self.constant_attributes.items() - } - - -@dataclass(frozen=True) -class RemoveCluster: - """Remove a cluster from an endpoint.""" - - cluster_id: int - endpoint_id: int = 1 - cluster_type: ClusterType = ClusterType.Server - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - endpoint = device.endpoints[self.endpoint_id] - if self.cluster_type == ClusterType.Server: - endpoint.in_clusters.pop(self.cluster_id, None) - else: - endpoint.out_clusters.pop(self.cluster_id, None) - - @dataclass(frozen=True) -class ReplaceCluster: - """Replace a cluster on an endpoint with a `Cluster` subclass. +class QuirkRegistryEntry: + """A registered device quirk: matching criteria plus how to build the device.""" - `cluster_id` identifies the cluster to remove and defaults to the - replacement's own cluster id. Cached attribute values of the replaced - cluster (e.g. restored from the database) carry over to the replacement. - """ - - cluster: type[Cluster] - cluster_id: int | None = None - endpoint_id: int = 1 - cluster_type: ClusterType = ClusterType.Server - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - endpoint = device.endpoints[self.endpoint_id] - is_server = self.cluster_type == ClusterType.Server - removed_cluster_id = ( - self.cluster.cluster_id if self.cluster_id is None else self.cluster_id - ) + # Matcher for this device + device_match: DeviceMatch - if is_server: - old_cluster = endpoint.in_clusters.pop(removed_cluster_id, None) - else: - old_cluster = endpoint.out_clusters.pop(removed_cluster_id, None) + # Zigpy device class replacement + zigpy_device_cls: type[zigpy.device.Device] | None = None - new_cluster = self.cluster(endpoint, is_server=is_server) - if is_server: - endpoint.add_input_cluster(new_cluster.cluster_id, new_cluster) - else: - endpoint.add_output_cluster(new_cluster.cluster_id, new_cluster) + # Transformations to apply to the zigpy device object. Used byzha-device-handlers to + # avoid creating unique subclasses at runtime. + zigpy_device_transforms: tuple[Callable[[zigpy.device.Device], zigpy.device.Device], ...] = () - if old_cluster is not None: - new_cluster._attr_cache_internal = old_cluster._attr_cache.clone( - new_cluster - ) + # ZHA device class replacement + zha_device_cls: type[Device] | None = None - -@dataclass(frozen=True) -class ReplaceClusterOccurrences: - """Replace a cluster with a `Cluster` subclass on every endpoint.""" - - cluster: type[Cluster] - cluster_types: tuple[ClusterType, ...] = (ClusterType.Server, ClusterType.Client) - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - for endpoint in device.non_zdo_endpoints: - if ( - ClusterType.Server in self.cluster_types - and self.cluster.cluster_id in endpoint.in_clusters - ): - ReplaceCluster( - cluster=self.cluster, - endpoint_id=endpoint.endpoint_id, - cluster_type=ClusterType.Server, - ).apply(device) - if ( - ClusterType.Client in self.cluster_types - and self.cluster.cluster_id in endpoint.out_clusters - ): - ReplaceCluster( - cluster=self.cluster, - endpoint_id=endpoint.endpoint_id, - cluster_type=ClusterType.Client, - ).apply(device) - - -@dataclass(frozen=True) -class AddEndpoint: - """Add an endpoint to a device, if it does not already exist.""" - - endpoint_id: int - profile_id: int = zigpy.profiles.zha.PROFILE_ID - device_type: int = 0xFF - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - if self.endpoint_id in device.endpoints: - return - endpoint = device.add_endpoint(self.endpoint_id) - endpoint.profile_id = self.profile_id - endpoint.device_type = self.device_type - - -@dataclass(frozen=True) -class RemoveEndpoint: - """Remove an endpoint from a device.""" - - endpoint_id: int - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - device.endpoints.pop(self.endpoint_id, None) - - -@dataclass(frozen=True) -class ReplaceEndpoint: - """Set the profile and device type of an endpoint, creating it if needed.""" - - endpoint_id: int - profile_id: int = zigpy.profiles.zha.PROFILE_ID - device_type: int = 0xFF - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - if self.endpoint_id in device.endpoints: - endpoint = device.endpoints[self.endpoint_id] - else: - endpoint = device.add_endpoint(self.endpoint_id) - endpoint.profile_id = self.profile_id - endpoint.device_type = self.device_type - - -@dataclass(frozen=True) -class SetNodeDescriptor: - """Replace the node descriptor of a device.""" - - node_descriptor: NodeDescriptor - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - device.node_desc = self.node_descriptor.freeze() - - -@dataclass(frozen=True) -class SetModelInfo: - """Override the manufacturer and/or model of a device.""" - - manufacturer: str | None = None - model: str | None = None - - def apply(self, device: zigpy.device.Device) -> None: - """Apply this operation to the given zigpy device.""" - if self.manufacturer is not None: - device.manufacturer = self.manufacturer - if self.model is not None: - device.model = self.model + # A somewhat human-readable way to find the quirk defining this device + quirk_file: str | None = None class DeviceRegistry: - """Registry of quirked `Device` subclasses, keyed by (manufacturer, model).""" + """Registry of quirk entries, keyed by (manufacturer, model).""" def __init__(self) -> None: """Initialize the registry.""" - self._registry: defaultdict[ModelInfo, list[type[Device]]] = defaultdict(list) - - def register(self, cls: type[Device]) -> type[Device]: - """Add a `Device` subclass to the registry.""" - match = cls._device_match - if match is None: - raise ValueError(f"{cls!r} does not define `_device_match`") - if not match.applies_to: - raise ValueError(f"{cls!r} must apply to at least one model") - for manufacturer, model in match.applies_to: + self._registry: defaultdict[ModelInfo, list[QuirkRegistryEntry]] = defaultdict( + list + ) + + def register(self, entry: QuirkRegistryEntry) -> QuirkRegistryEntry: + """Add a quirk entry to the registry.""" + for manufacturer, model in entry.device_match.applies_to: if manufacturer is None and model is None: raise ValueError( - f"{cls!r} must specify a manufacturer and/or model to match" + f"{entry!r} must specify a manufacturer and/or model to match" ) + # Most recently registered quirks take precedence, so quirks loaded # from the custom quirks directory override built-in ones. - self._registry[ModelInfo(manufacturer, model)].insert(0, cls) - return cls + self._registry[ModelInfo(manufacturer, model)].insert(0, entry) + + return entry - def get(self, zigpy_device: zigpy.device.Device) -> type[Device] | None: - """Return the first registered class matching `zigpy_device`.""" + def get(self, zigpy_device: zigpy.device.Device) -> QuirkRegistryEntry | None: + """Return the first registered entry matching `zigpy_device`.""" for key in ( ModelInfo(zigpy_device.manufacturer, zigpy_device.model), ModelInfo(zigpy_device.manufacturer, None), ModelInfo(None, zigpy_device.model), ): - for cls in self._registry[key]: - if cls._device_match.matches(zigpy_device): - return cls + for entry in self._registry[key]: + if entry.device_match.matches(zigpy_device): + return entry + return None - def remove(self, cls: type[Device]) -> None: - """Remove a `Device` subclass from the registry.""" - for manufacturer, model in cls._device_match.applies_to: - self._registry[ModelInfo(manufacturer, model)].remove(cls) + def remove(self, entry: QuirkRegistryEntry) -> None: + """Remove a quirk entry from the registry.""" + for manufacturer, model in entry.device_match.applies_to: + self._registry[ModelInfo(manufacturer, model)].remove(entry) def purge_custom_quirks(self, custom_quirks_root: Path) -> None: """Remove quirks loaded from the custom quirks directory.""" - for classes in self._registry.values(): - for cls in list(classes): - if ( - cls._quirk_definition is not None - and cls._quirk_definition.quirk_file is not None - ): - quirk_file = Path(cls._quirk_definition.quirk_file) - else: - module = inspect.getmodule(cls) - quirk_file = Path(module.__file__) - - if quirk_file.is_relative_to(custom_quirks_root): - _LOGGER.debug("Removing stale custom quirk: %s", cls) - classes.remove(cls) + for entries in self._registry.values(): + for entry in list(entries): + if entry.quirk_file is None: + continue + if Path(entry.quirk_file).is_relative_to(custom_quirks_root): + _LOGGER.debug("Removing stale custom quirk: %s", entry) + entries.remove(entry) DEVICE_REGISTRY = DeviceRegistry() def register_device(cls: type[Device]) -> type[Device]: - """Class decorator registering a `Device` subclass as a quirk.""" - return DEVICE_REGISTRY.register(cls) + """Class decorator registering a hand-written `Device` subclass as a quirk.""" + if cls._device_match is None: + raise ValueError(f"{cls!r} does not define `_device_match`") + + module = inspect.getmodule(cls) + + DEVICE_REGISTRY.register( + QuirkRegistryEntry( + device_match=cls._device_match, + zigpy_device_cls=cls._zigpy_device_cls, + zigpy_device_transforms=(), + zha_device_cls=cls, + quirk_file=module.__file__ if module is not None else None, + ) + ) + return cls -def _snapshot_zigpy_device(zigpy_device: zigpy.device.Device) -> tuple: - """Capture the device state that quirk operations can modify.""" - return ( - zigpy_device.manufacturer, - zigpy_device.model, - zigpy_device.node_desc, - dict(zigpy_device.endpoints), - { - endpoint.endpoint_id: ( - endpoint.profile_id, - endpoint.device_type, - dict(endpoint.in_clusters), - dict(endpoint.out_clusters), - ) - for endpoint in zigpy_device.non_zdo_endpoints - }, - ) +def resolve_zigpy_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: + """Provide zigpy a way to resolve a bare ZCL-compliant device to its final form.""" -def _restore_zigpy_device(zigpy_device: zigpy.device.Device, snapshot: tuple) -> None: - """Restore device state captured by `_snapshot_zigpy_device`.""" - manufacturer, model, node_desc, endpoints, endpoint_state = snapshot - zigpy_device._manufacturer = manufacturer - zigpy_device._model = model - zigpy_device.node_desc = node_desc - zigpy_device.endpoints.clear() - zigpy_device.endpoints.update(endpoints) - for endpoint_id, ( - profile_id, - device_type, - in_clusters, - out_clusters, - ) in endpoint_state.items(): - endpoint = zigpy_device.endpoints[endpoint_id] - endpoint.profile_id = profile_id - endpoint.device_type = device_type - endpoint.in_clusters.clear() - endpoint.in_clusters.update(in_clusters) - endpoint.out_clusters.clear() - endpoint.out_clusters.update(out_clusters) - - -def resolve_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: - """Resolve a freshly-constructed zigpy device into its final object. - - Registered with zigpy as the application's device resolver. The matching - quirk class applies its zigpy-level modifications (and may return a - replacement device object); the class is then stamped onto the zigpy - device so `Device.new` dispatches to it without a second match pass. - - Devices without a matching ZHA quirk go through zigpy's legacy v1/v2 - quirks registry instead. - """ # Resolution is idempotent: an already-quirked device is returned as-is - # (re-applying in-place operations would clobber cluster state). - if hasattr(zigpy_device, ZHA_DEVICE_CLASS_ATTRIBUTE): + if hasattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR): return zigpy_device - quirk_cls = DEVICE_REGISTRY.get(zigpy_device) - if quirk_cls is None: + # Fall back to legacy zigpy v1 quirks + entry = DEVICE_REGISTRY.get(zigpy_device) + if entry is None: return zigpy.quirks.get_device(zigpy_device) _LOGGER.debug( @@ -448,23 +216,17 @@ def resolve_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: zigpy_device.manufacturer, zigpy_device.model, zigpy_device.ieee, - quirk_cls.__name__, + entry, ) - # A quirk that fails to apply leaves the device unquirked, matching the - # zigpy registry's behavior. Operations apply in place, so the prior state - # is restored on failure. - snapshot = _snapshot_zigpy_device(zigpy_device) - try: - resolved = quirk_cls.apply_to_zigpy_device(zigpy_device) - except Exception: # noqa: BLE001 - _restore_zigpy_device(zigpy_device, snapshot) - _LOGGER.exception( - "Failed to apply quirk %s to %r. This is a bug, please report it", - quirk_cls.__name__, - zigpy_device, - ) - return zigpy_device + resolved_device = zigpy_device + + if entry.zigpy_device_cls is not None: + resolved_device = entry.zigpy_device_cls.from_zigpy_device(zigpy_device) + + for transform in entry.zigpy_device_transforms: + resolved_device = transform(resolved_device) - setattr(resolved, ZHA_DEVICE_CLASS_ATTRIBUTE, quirk_cls) - return resolved + # Sneak the registry entry in with the device so ZHA can use it + setattr(resolved_device, QUIRK_REGISTRY_ENTRY_ATTR, entry) + return resolved_device diff --git a/zha/quirks/metadata.py b/zha/quirks/metadata.py deleted file mode 100644 index 77178b853..000000000 --- a/zha/quirks/metadata.py +++ /dev/null @@ -1,245 +0,0 @@ -"""ZHA quirk metadata model. - -The declarative metadata produced by `zha.quirks.v2.QuirkBuilder`: exposed -entities, default-entity changes, alerts and naming, aggregated into a -`QuirkDefinition` stored on the compiled `Device` subclass. -""" - -from __future__ import annotations - -from collections.abc import Callable -from enum import Enum -from typing import Any - -import attrs -from frozendict import frozendict -from zigpy.zcl import ClusterType - -from zha.application import EntityPlatform, EntityType -from zha.application.platforms.binary_sensor.device_class import BinarySensorDeviceClass -from zha.application.platforms.number.device_class import NumberDeviceClass -from zha.application.platforms.sensor.device_class import ( - SensorDeviceClass, - SensorStateClass, -) - -# pylint: disable=too-many-instance-attributes - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ReportingConfig: - """Reporting config for an entity attribute.""" - - min_interval: int - max_interval: int - reportable_change: int - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class EntityMetadata: - """Metadata for an exposed entity.""" - - entity_platform: EntityPlatform = attrs.field() - entity_type: EntityType = attrs.field() - cluster_id: int = attrs.field() - endpoint_id: int = attrs.field(default=1) - cluster_type: ClusterType = attrs.field(default=ClusterType.Server) - initially_disabled: bool = attrs.field(default=False) - attribute_initialized_from_cache: bool = attrs.field(default=True) - unique_id_suffix: str | None = attrs.field(default=None) - translation_key: str | None = attrs.field(default=None) - translation_placeholders: frozendict[str, str] = attrs.field( - factory=frozendict, converter=frozendict - ) - fallback_name: str = attrs.field(validator=attrs.validators.instance_of(str)) - primary: bool | None = attrs.field(default=None) - - def __attrs_post_init__(self) -> None: - """Validate the entity metadata.""" - has_device_class: bool = getattr(self, "device_class", None) is not None - if self.translation_key is None and not has_device_class: - raise ValueError( - f"EntityMetadata must have a translation_key or device_class: {self}" - ) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ZCLEnumMetadata(EntityMetadata): - """Metadata for exposed ZCL enum based entity.""" - - enum: type[Enum] = attrs.field() - attribute_name: str = attrs.field() - reporting_config: ReportingConfig | None = attrs.field(default=None) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ZCLSensorMetadata(EntityMetadata): - """Metadata for exposed ZCL attribute based sensor entity.""" - - attribute_name: str | None = attrs.field(default=None) - attribute_converter: Callable[[Any], Any] | None = attrs.field(default=None) - reporting_config: ReportingConfig | None = attrs.field(default=None) - divisor: int | None = attrs.field(default=None) - multiplier: int | None = attrs.field(default=None) - suggested_display_precision: int | None = attrs.field(default=None) - unit: str | None = attrs.field(default=None) - device_class: SensorDeviceClass | None = attrs.field(default=None) - state_class: SensorStateClass | None = attrs.field(default=None) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class SwitchMetadata(EntityMetadata): - """Metadata for exposed switch entity.""" - - attribute_name: str = attrs.field() - reporting_config: ReportingConfig | None = attrs.field(default=None) - force_inverted: bool = attrs.field(default=False) - invert_attribute_name: str | None = attrs.field(default=None) - off_value: int = attrs.field(default=0) - on_value: int = attrs.field(default=1) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class NumberMetadata(EntityMetadata): - """Metadata for exposed number entity.""" - - attribute_name: str = attrs.field() - reporting_config: ReportingConfig | None = attrs.field(default=None) - min: float | None = attrs.field(default=None) - max: float | None = attrs.field(default=None) - step: float | None = attrs.field(default=None) - unit: str | None = attrs.field(default=None) - mode: str | None = attrs.field(default=None) - multiplier: float | None = attrs.field(default=None) - device_class: NumberDeviceClass | None = attrs.field(default=None) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class BinarySensorMetadata(EntityMetadata): - """Metadata for exposed binary sensor entity.""" - - attribute_name: str = attrs.field() - attribute_converter: Callable[[Any], Any] | None = attrs.field(default=None) - reporting_config: ReportingConfig | None = attrs.field(default=None) - device_class: BinarySensorDeviceClass | None = attrs.field(default=None) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class WriteAttributeButtonMetadata(EntityMetadata): - """Metadata for exposed button entity that writes an attribute when pressed.""" - - attribute_name: str = attrs.field() - attribute_value: int = attrs.field() - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ZCLCommandButtonMetadata(EntityMetadata): - """Metadata for exposed button entity that executes a ZCL command when pressed.""" - - command_name: str = attrs.field() - args: tuple = attrs.field(default=tuple) - kwargs: frozendict[str, Any] = attrs.field(factory=frozendict, converter=frozendict) - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class FriendlyNameMetadata: - """Metadata to rename a device.""" - - model: str = attrs.field() - manufacturer: str = attrs.field() - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ExposesFeatureMetadata: - """Metadata for an exposed feature to match against in ZHA.""" - - feature: str = attrs.field() - config: frozendict[str, Any] = attrs.field(factory=frozendict, converter=frozendict) - - -class DeviceAlertLevel(Enum): - """Device alert level.""" - - INFO = "info" - WARNING = "warning" - ERROR = "error" - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class DeviceAlertMetadata: - """Metadata for device-specific alerts.""" - - level: DeviceAlertLevel = attrs.field(converter=DeviceAlertLevel) - message: str = attrs.field() - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class PreventDefaultEntityCreationMetadata: - """Metadata to prevent the default creation of an entity.""" - - endpoint_id: int | None = attrs.field() - cluster_id: int | None = attrs.field() - cluster_type: ClusterType | None = attrs.field() - unique_id_suffix: str | None = attrs.field() - function: Callable[[Any], bool] | None = attrs.field() - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class ChangedEntityMetadata: - """Metadata to change entity metadata for matching entities.""" - - endpoint_id: int | None = attrs.field() - cluster_id: int | None = attrs.field() - cluster_type: ClusterType | None = attrs.field() - unique_id_suffix: str | None = attrs.field() - function: Callable[[Any], bool] | None = attrs.field() - # Entity metadata changes - new_primary: bool | None = attrs.field(default=None) - new_unique_id: str | None = attrs.field(default=None) - new_translation_key: str | None = attrs.field(default=None) - new_translation_placeholders: frozendict[str, str] | None = attrs.field( - default=None, converter=lambda d: None if d is None else frozendict(d) - ) - new_device_class: ( - BinarySensorDeviceClass | NumberDeviceClass | SensorDeviceClass | None - ) = attrs.field(default=None) - new_state_class: SensorStateClass | None = attrs.field(default=None) - new_entity_category: EntityType | None = attrs.field(default=None) - new_entity_registry_enabled_default: bool | None = attrs.field(default=None) - new_fallback_name: str | None = attrs.field(default=None) - - -def recursive_freeze(obj: Any) -> Any: - """Recursively convert mutable collections to immutable ones.""" - if isinstance(obj, dict): - return frozendict({k: recursive_freeze(v) for k, v in obj.items()}) - if isinstance(obj, tuple | list | set): - return tuple(recursive_freeze(v) for v in obj) - return obj - - -@attrs.define(frozen=True, kw_only=True, repr=True) -class QuirkDefinition: - """ZHA-level metadata of a quirk. - - Everything a quirk expresses about the ZHA device model: entities, - automation triggers, alerts and naming. Zigbee-level modifications live in - `Device._zigpy_ops` instead. - """ - - quirk_file: str | None = attrs.field(default=None, eq=False) - quirk_file_line: int | None = attrs.field(default=None, eq=False) - friendly_name: FriendlyNameMetadata | None = attrs.field(default=None) - exposes_features: tuple[ExposesFeatureMetadata, ...] = attrs.field(factory=tuple) - device_alerts: tuple[DeviceAlertMetadata, ...] = attrs.field(factory=tuple) - disabled_default_entities: tuple[PreventDefaultEntityCreationMetadata, ...] = ( - attrs.field(factory=tuple) - ) - changed_entity_metadata: tuple[ChangedEntityMetadata, ...] = attrs.field( - factory=tuple - ) - entity_metadata: tuple[EntityMetadata, ...] = attrs.field(factory=tuple) - device_automation_triggers: frozendict[tuple[str, str], frozendict[str, str]] = ( - attrs.field(factory=frozendict, converter=recursive_freeze) - ) - skip_configuration: bool = attrs.field(default=False) diff --git a/zha/quirks/v2.py b/zha/quirks/v2.py deleted file mode 100644 index 151d545f4..000000000 --- a/zha/quirks/v2.py +++ /dev/null @@ -1,890 +0,0 @@ -"""ZHA-native quirks v2 builder. - -`QuirkBuilder` keeps the declarative quirks v2 authoring API but compiles to -the ZHA device model: `add_to_registry()` synthesizes a `zha.zigbee.device.Device` -subclass whose `_device_match` carries the matching criteria, whose -`_zigpy_ops` carry the Zigbee-level modifications, and whose -`_quirk_definition` carries the ZHA-level metadata (entities, triggers, -alerts, naming). -""" - -from __future__ import annotations - -from collections.abc import Callable -from copy import deepcopy -from enum import Enum -import inspect -import logging -import pathlib -import re -from types import FrameType -from typing import Any, Self, overload - -from frozendict import frozendict -import zigpy.device -import zigpy.profiles.zha -from zigpy.typing import UNDEFINED, UndefinedType -from zigpy.zcl import Cluster, ClusterType -from zigpy.zcl.foundation import ZCLAttributeDef -from zigpy.zdo.types import NodeDescriptor - -from zha.application import ( # noqa: F401 - EntityPlatform, - EntityType, - # `discovery` must be imported before `zha.zigbee.device`: the platform - # modules it loads participate in an import cycle with the device module - # and cannot be loaded with `zha.zigbee.device` partially initialized. - discovery, -) -from zha.application.platforms.binary_sensor.device_class import BinarySensorDeviceClass -from zha.application.platforms.number.device_class import NumberDeviceClass -from zha.application.platforms.sensor.device_class import ( - SensorDeviceClass, - SensorStateClass, -) -from zha.quirks import ( - DEVICE_REGISTRY, - AddCluster, - AddEndpoint, - DeviceMatch, - DeviceRegistry, - FilterType, - ModelInfo, - RemoveCluster, - RemoveEndpoint, - ReplaceCluster, - ReplaceClusterOccurrences, - ReplaceEndpoint, - SetNodeDescriptor, - ZigpyOp, -) -from zha.quirks.metadata import ( - BinarySensorMetadata, - ChangedEntityMetadata, - DeviceAlertLevel, - DeviceAlertMetadata, - EntityMetadata, - ExposesFeatureMetadata, - FriendlyNameMetadata, - NumberMetadata, - PreventDefaultEntityCreationMetadata, - QuirkDefinition, - ReportingConfig, - SwitchMetadata, - WriteAttributeButtonMetadata, - ZCLCommandButtonMetadata, - ZCLEnumMetadata, - ZCLSensorMetadata, -) -from zha.zigbee.device import Device - -_LOGGER = logging.getLogger(__name__) - -UNBUILT_QUIRK_BUILDERS: list[QuirkBuilder] = [] - -# pylint: disable=too-many-instance-attributes -# pylint: disable=too-many-arguments - - -class QuirkBuilder: - """Builder compiling a declarative quirk into a registered `Device` subclass.""" - - def __init__( - self, - manufacturer: str | None | UndefinedType = UNDEFINED, - model: str | None | UndefinedType = UNDEFINED, - registry: DeviceRegistry | None = None, - ) -> None: - """Initialize the quirk builder.""" - self.registry: DeviceRegistry = ( - registry if registry is not None else DEVICE_REGISTRY - ) - self.manufacturer_model_metadata: list[ModelInfo] = [] - self.friendly_name_metadata: FriendlyNameMetadata | None = None - self.exposes_features: list[ExposesFeatureMetadata] = [] - self.device_alerts: list[DeviceAlertMetadata] = [] - self.disabled_default_entities: list[PreventDefaultEntityCreationMetadata] = [] - self.changed_entity_metadata: list[ChangedEntityMetadata] = [] - self.filters: list[FilterType] = [] - self.firmware_version_min: int | None = None - self.firmware_version_max: int | None = None - self.firmware_version_allow_missing: bool = True - self.custom_device_class: type[Device] | None = None - self.custom_zigpy_device_class: type[zigpy.device.Device] | None = None - self.device_node_descriptor: NodeDescriptor | None = None - self.skip_device_configuration: bool = False - self.removes_endpoint_ops: list[RemoveEndpoint] = [] - self.adds_endpoint_ops: list[AddEndpoint] = [] - self.replaces_endpoint_ops: list[ReplaceEndpoint] = [] - self.removes_ops: list[RemoveCluster] = [] - self.adds_ops: list[AddCluster] = [] - self.replaces_ops: list[ReplaceCluster] = [] - self.replace_occurrences_ops: list[ReplaceClusterOccurrences] = [] - self.entity_metadata: list[EntityMetadata] = [] - self.device_automation_triggers_metadata: dict[ - tuple[str, str], dict[str, str] - ] = {} - - current_frame: FrameType = inspect.currentframe() - caller: FrameType = current_frame.f_back - self.quirk_file = pathlib.Path(caller.f_code.co_filename) - self.quirk_file_line = caller.f_lineno - self.quirk_module: str = caller.f_globals["__name__"] - - if manufacturer is not UNDEFINED or model is not UNDEFINED: - self.applies_to( - manufacturer=manufacturer if manufacturer is not UNDEFINED else None, - model=model if model is not UNDEFINED else None, - ) - - UNBUILT_QUIRK_BUILDERS.append(self) - - def _add_entity_metadata(self, entity_metadata: EntityMetadata) -> Self: - """Register new entity metadata and validate config.""" - if entity_metadata.primary and any( - entity.primary for entity in self.entity_metadata - ): - raise ValueError("Only one primary entity can be defined per device") - - self.entity_metadata.append(entity_metadata) - return self - - @overload - def applies_to(self, manufacturer: str, model: str) -> Self: ... - - @overload - def applies_to(self, manufacturer: str, model: str | None) -> Self: ... - - @overload - def applies_to(self, manufacturer: None, model: str) -> Self: ... - - def applies_to(self, manufacturer: str | None, model: str | None) -> Self: - """Register this quirk for the specified manufacturer and model.""" - if manufacturer is None and model is None: - raise ValueError( - "A manufacturer and/or model must be specified for a v2 quirk." - ) - - self.manufacturer_model_metadata.append(ModelInfo(manufacturer, model)) - return self - - # backward compatibility - also_applies_to = applies_to - - def filter(self, filter_function: FilterType) -> Self: - """Add a filter and returns self. - - The filter function should take a single argument, a zigpy.device.Device - instance, and return a boolean if the condition the filter is testing - passes. - - Ex: def some_filter(device: zigpy.device.Device) -> bool: - """ - self.filters.append(filter_function) - return self - - def firmware_version_filter( - self, - min_version: int | None = None, - max_version: int | None = None, - allow_missing: bool = True, - ) -> Self: - """Add a firmware version filter and returns self. - - The min_version and max_version are integers representing the firmware version, - minimum inclusive but maximum exclusive. If allow_missing is True, the filter - will pass if the device does not have a firmware version. - """ - self.firmware_version_min = min_version - self.firmware_version_max = max_version - self.firmware_version_allow_missing = allow_missing - return self - - def device_class( - self, custom_device_class: type[Device] | type[zigpy.device.Device] - ) -> Self: - """Set the `Device` subclass used as the base of this quirk's class. - - For compatibility with quirks v2, a `zigpy.device.Device` subclass is - also accepted and is treated as `zigpy_device_class` instead. - """ - if issubclass(custom_device_class, Device): - self.custom_device_class = custom_device_class - return self - - assert issubclass(custom_device_class, zigpy.device.Device), ( - f"{custom_device_class} is not a subclass of Device" - ) - return self.zigpy_device_class(custom_device_class) - - def zigpy_device_class( - self, custom_device_class: type[zigpy.device.Device] - ) -> Self: - """Replace the zigpy device object with an instance of the given class. - - The class must accept `(application, ieee, nwk, replaces)`, like - `zigpy.quirks.v2.CustomDeviceV2`, and is used to override low-level - zigpy device behavior. The replacement happens before any cluster or - endpoint operations are applied. - """ - self.custom_zigpy_device_class = custom_device_class - return self - - def node_descriptor(self, node_descriptor: NodeDescriptor) -> Self: - """Set the node descriptor and returns self. - - The node descriptor must be a NodeDescriptor instance and it will be used - to replace the node descriptor of the device when the quirk is applied. - """ - self.device_node_descriptor = node_descriptor.freeze() - return self - - def skip_configuration(self, skip_configuration: bool = True) -> Self: - """Set the skip_configuration and returns self. - - If skip_configuration is True, reporting configuration will not be - applied to any cluster on this device. - """ - self.skip_device_configuration = skip_configuration - return self - - def adds( - self, - cluster: int | type[Cluster], - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - constant_attributes: dict[ZCLAttributeDef, Any] | None = None, - ) -> Self: - """Add an `AddCluster` operation and return self. - - This method allows adding a cluster to a device when the quirk is applied. - - If cluster is an int, it will be used as the cluster_id. If cluster is a - subclass of Cluster or CustomCluster, it will be used to create a new - cluster instance. - - If constant_attributes is provided, it should be a dictionary of ZCLAttributeDef - instances and their values. These attributes will be added to the cluster when - the quirk is applied and the values will be constant. - """ - self.adds_ops.append( - AddCluster( - cluster=cluster, - endpoint_id=endpoint_id, - cluster_type=cluster_type, - constant_attributes=constant_attributes or {}, - ) - ) - return self - - def removes( - self, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - ) -> Self: - """Add a `RemoveCluster` operation and return self. - - This method allows removing a cluster from a device when the quirk is applied. - """ - self.removes_ops.append( - RemoveCluster( - cluster_id=cluster_id, - endpoint_id=endpoint_id, - cluster_type=cluster_type, - ) - ) - return self - - def replaces( - self, - replacement_cluster_class: type[Cluster], - cluster_id: int | None = None, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - ) -> Self: - """Add a `ReplaceCluster` operation and return self. - - This method allows replacing a cluster on a device when the quirk is applied. - - replacement_cluster_class should be a subclass of Cluster or CustomCluster and - will be used to create a new cluster instance to replace the existing cluster. - - If cluster_id is provided, it will be used as the cluster_id for the cluster to - be removed. If cluster_id is not provided, the cluster_id of the replacement - cluster will be used. - """ - self.replaces_ops.append( - ReplaceCluster( - cluster=replacement_cluster_class, - cluster_id=cluster_id, - endpoint_id=endpoint_id, - cluster_type=cluster_type, - ) - ) - return self - - def replace_cluster_occurrences( - self, - replacement_cluster_class: type[Cluster], - replace_server_instances: bool = True, - replace_client_instances: bool = True, - ) -> Self: - """Add a `ReplaceClusterOccurrences` operation and return self. - - This method allows replacing a cluster on a device across all endpoints - for the specified cluster types when the quirk is applied. - """ - types = [] - if replace_server_instances: - types.append(ClusterType.Server) - if replace_client_instances: - types.append(ClusterType.Client) - self.replace_occurrences_ops.append( - ReplaceClusterOccurrences( - cluster=replacement_cluster_class, - cluster_types=tuple(types), - ) - ) - return self - - def adds_endpoint( - self, - endpoint_id: int, - profile_id: int = zigpy.profiles.zha.PROFILE_ID, - device_type: int = 0xFF, - ) -> Self: - """Add an `AddEndpoint` operation and return self.""" - self.adds_endpoint_ops.append( - AddEndpoint( - endpoint_id=endpoint_id, - profile_id=profile_id, - device_type=device_type, - ) - ) - return self - - def removes_endpoint(self, endpoint_id: int) -> Self: - """Add a `RemoveEndpoint` operation and return self.""" - self.removes_endpoint_ops.append(RemoveEndpoint(endpoint_id=endpoint_id)) - return self - - def replaces_endpoint( - self, - endpoint_id: int, - profile_id: int = zigpy.profiles.zha.PROFILE_ID, - device_type: int = 0xFF, - ) -> Self: - """Add a `ReplaceEndpoint` operation and return self.""" - self.replaces_endpoint_ops.append( - ReplaceEndpoint( - endpoint_id=endpoint_id, - profile_id=profile_id, - device_type=device_type, - ) - ) - return self - - def enum( - self, - attribute_name: str, - enum_class: type[Enum], - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - entity_platform: EntityPlatform = EntityPlatform.SELECT, - entity_type: EntityType = EntityType.CONFIG, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - reporting_config: ReportingConfig | None = None, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing ZCLEnumMetadata and return self. - - This method allows exposing an enum based entity in Home Assistant. - """ - self._add_entity_metadata( - ZCLEnumMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=entity_platform, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - reporting_config=reporting_config, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - enum=enum_class, - attribute_name=attribute_name, - primary=primary, - ) - ) - return self - - def sensor( - self, - attribute_name: str, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - divisor: int = 1, - multiplier: int = 1, - suggested_display_precision: int = 1, - entity_type: EntityType = EntityType.STANDARD, - device_class: SensorDeviceClass | None = None, - state_class: SensorStateClass | None = None, - unit: str | None = None, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - attribute_converter: Callable[[Any], Any] | None = None, - reporting_config: ReportingConfig | None = None, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing ZCLSensorMetadata and return self. - - This method allows exposing a sensor entity in Home Assistant. - """ - self._add_entity_metadata( - ZCLSensorMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=EntityPlatform.SENSOR, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - reporting_config=reporting_config, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - attribute_name=attribute_name, - attribute_converter=attribute_converter, - divisor=divisor, - multiplier=multiplier, - suggested_display_precision=suggested_display_precision, - unit=unit, - device_class=device_class, - state_class=state_class, - primary=primary, - ) - ) - return self - - def switch( - self, - attribute_name: str, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - force_inverted: bool = False, - invert_attribute_name: str | None = None, - off_value: int = 0, - on_value: int = 1, - entity_platform: EntityPlatform = EntityPlatform.SWITCH, - entity_type: EntityType = EntityType.CONFIG, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - reporting_config: ReportingConfig | None = None, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing SwitchMetadata and return self. - - This method allows exposing a switch entity in Home Assistant. - """ - self._add_entity_metadata( - SwitchMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=entity_platform, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - reporting_config=reporting_config, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - attribute_name=attribute_name, - force_inverted=force_inverted, - invert_attribute_name=invert_attribute_name, - off_value=off_value, - on_value=on_value, - primary=primary, - ) - ) - return self - - def number( - self, - attribute_name: str, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - min_value: float | None = None, - max_value: float | None = None, - step: float | None = None, - unit: str | None = None, - mode: str | None = None, - multiplier: float | None = None, - entity_type: EntityType = EntityType.CONFIG, - device_class: NumberDeviceClass | None = None, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - reporting_config: ReportingConfig | None = None, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing NumberMetadata and return self. - - This method allows exposing a number entity in Home Assistant. - """ - self._add_entity_metadata( - NumberMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=EntityPlatform.NUMBER, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - reporting_config=reporting_config, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - attribute_name=attribute_name, - min=min_value, - max=max_value, - step=step, - unit=unit, - mode=mode, - multiplier=multiplier, - device_class=device_class, - primary=primary, - ) - ) - return self - - def binary_sensor( - self, - attribute_name: str, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - entity_type: EntityType = EntityType.DIAGNOSTIC, - device_class: BinarySensorDeviceClass | None = None, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - attribute_converter: Callable[[Any], Any] | None = None, - reporting_config: ReportingConfig | None = None, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing BinarySensorMetadata and return self. - - This method allows exposing a binary sensor entity in Home Assistant. - """ - self._add_entity_metadata( - BinarySensorMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=EntityPlatform.BINARY_SENSOR, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - reporting_config=reporting_config, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - attribute_name=attribute_name, - attribute_converter=attribute_converter, - device_class=device_class, - primary=primary, - ) - ) - return self - - def write_attr_button( - self, - attribute_name: str, - attribute_value: int, - cluster_id: int, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - entity_type: EntityType = EntityType.CONFIG, - initially_disabled: bool = False, - attribute_initialized_from_cache: bool = True, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing WriteAttributeButtonMetadata and return self. - - This method allows exposing a button entity in Home Assistant that writes - a value to an attribute when pressed. - """ - self._add_entity_metadata( - WriteAttributeButtonMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=EntityPlatform.BUTTON, - entity_type=entity_type, - initially_disabled=initially_disabled, - attribute_initialized_from_cache=attribute_initialized_from_cache, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - attribute_name=attribute_name, - attribute_value=attribute_value, - primary=primary, - ) - ) - return self - - def command_button( - self, - command_name: str, - cluster_id: int, - command_args: tuple | None = None, - command_kwargs: dict[str, Any] | None = None, - cluster_type: ClusterType = ClusterType.Server, - endpoint_id: int = 1, - entity_type: EntityType = EntityType.CONFIG, - initially_disabled: bool = False, - unique_id_suffix: str | None = None, - translation_key: str | None = None, - fallback_name: str | None = None, - primary: bool | None = None, - *, - translation_placeholders: dict[str, str] | None = None, - ) -> Self: - """Add an EntityMetadata containing ZCLCommandButtonMetadata and return self. - - This method allows exposing a button entity in Home Assistant that executes - a ZCL command when pressed. - """ - self._add_entity_metadata( - ZCLCommandButtonMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - entity_platform=EntityPlatform.BUTTON, - entity_type=entity_type, - initially_disabled=initially_disabled, - unique_id_suffix=unique_id_suffix, - translation_key=translation_key, - translation_placeholders=translation_placeholders or {}, - fallback_name=fallback_name, - command_name=command_name, - args=command_args if command_args is not None else (), - kwargs=command_kwargs if command_kwargs is not None else frozendict(), - primary=primary, - ) - ) - return self - - def device_automation_triggers( - self, device_automation_triggers: dict[tuple[str, str], dict[str, str]] - ) -> Self: - """Add device automation triggers and returns self.""" - self.device_automation_triggers_metadata.update(device_automation_triggers) - return self - - def friendly_name(self, *, model: str, manufacturer: str) -> Self: - """Rename the device.""" - self.friendly_name_metadata = FriendlyNameMetadata( - model=model, manufacturer=manufacturer - ) - return self - - def exposes_feature( - self, feature: str, config: dict[str, Any] | None = None - ) -> Self: - """Add an exposed feature.""" - self.exposes_features.append( - ExposesFeatureMetadata(feature=feature, config=config or {}) - ) - return self - - def device_alert(self, *, level: DeviceAlertLevel, message: str) -> Self: - """Add a device alert.""" - self.device_alerts.append(DeviceAlertMetadata(level=level, message=message)) - return self - - def prevent_default_entity_creation( - self, - *, - endpoint_id: int | None = None, - cluster_id: int | None = None, - cluster_type: ClusterType | None = None, - unique_id_suffix: str | None = None, - function: Callable[[Any], bool] | None = None, - ) -> Self: - """Do not create default entities.""" - if cluster_id is not None and cluster_type is None: - cluster_type = ClusterType.Server - - self.disabled_default_entities.append( - PreventDefaultEntityCreationMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - unique_id_suffix=unique_id_suffix, - function=function, - ), - ) - return self - - def change_entity_metadata( - self, - *, - endpoint_id: int | None = None, - cluster_id: int | None = None, - cluster_type: ClusterType | None = None, - unique_id_suffix: str | None = None, - function: Callable[[Any], bool] | None = None, - new_primary: bool | None = None, - new_unique_id: str | None = None, - new_translation_key: str | None = None, - new_translation_placeholders: dict[str, str] | None = None, - new_device_class: ( - BinarySensorDeviceClass | NumberDeviceClass | SensorDeviceClass | None - ) = None, - new_state_class: SensorStateClass | None = None, - new_entity_category: EntityType | None = None, - new_entity_registry_enabled_default: bool | None = None, - new_fallback_name: str | None = None, - ) -> Self: - """Change entity metadata for matching entities.""" - if cluster_id is not None and cluster_type is None: - cluster_type = ClusterType.Server - - self.changed_entity_metadata.append( - ChangedEntityMetadata( - endpoint_id=endpoint_id, - cluster_id=cluster_id, - cluster_type=cluster_type, - unique_id_suffix=unique_id_suffix, - function=function, - new_primary=new_primary, - new_unique_id=new_unique_id, - new_translation_key=new_translation_key, - new_translation_placeholders=new_translation_placeholders, - new_device_class=new_device_class, - new_state_class=new_state_class, - new_entity_category=new_entity_category, - new_entity_registry_enabled_default=new_entity_registry_enabled_default, - new_fallback_name=new_fallback_name, - ), - ) - return self - - def _compile_ops(self) -> tuple[ZigpyOp, ...]: - """Compile the accumulated cluster/endpoint operations, in v2 order.""" - ops: list[ZigpyOp] = [] - if self.device_node_descriptor is not None: - ops.append(SetNodeDescriptor(node_descriptor=self.device_node_descriptor)) - # endpoints need to be modified before clusters - ops.extend(self.removes_endpoint_ops) - ops.extend(self.adds_endpoint_ops) - ops.extend(self.replaces_endpoint_ops) - ops.extend(self.removes_ops) - ops.extend(self.adds_ops) - ops.extend(self.replaces_ops) - ops.extend(self.replace_occurrences_ops) - return tuple(ops) - - def add_to_registry(self) -> type[Device]: - """Compile the quirk into a `Device` subclass and register it.""" - if not self.manufacturer_model_metadata: - raise ValueError( - "At least one manufacturer and model must be specified for a v2 quirk." - ) - - device_match = DeviceMatch( - applies_to=tuple(self.manufacturer_model_metadata), - filters=tuple(self.filters), - firmware_version_min=self.firmware_version_min, - firmware_version_max=self.firmware_version_max, - firmware_version_allow_missing=self.firmware_version_allow_missing, - ) - - quirk_definition = QuirkDefinition( - quirk_file=str(self.quirk_file), - quirk_file_line=self.quirk_file_line, - friendly_name=self.friendly_name_metadata, - exposes_features=tuple(self.exposes_features), - device_alerts=tuple(self.device_alerts), - disabled_default_entities=tuple(self.disabled_default_entities), - changed_entity_metadata=tuple(self.changed_entity_metadata), - entity_metadata=tuple(self.entity_metadata), - device_automation_triggers=self.device_automation_triggers_metadata, - skip_configuration=self.skip_device_configuration, - ) - - base = self.custom_device_class if self.custom_device_class else Device - manufacturer, model = self.manufacturer_model_metadata[0] - name = re.sub(r"\W|^(?=\d)", "_", f"Quirk_{manufacturer}_{model}") - - quirk_cls = type( - name, - (base,), - { - "__module__": self.quirk_module, - "_device_match": device_match, - "_zigpy_device_class": self.custom_zigpy_device_class, - "_zigpy_ops": self._compile_ops(), - "_quirk_definition": quirk_definition, - }, - ) - - self.registry.register(quirk_cls) - - if self in UNBUILT_QUIRK_BUILDERS: - UNBUILT_QUIRK_BUILDERS.remove(self) - - return quirk_cls - - def clone(self, omit_man_model_data: bool = True) -> Self: - """Clone this QuirkBuilder potentially omitting manufacturer and model data.""" - new_builder = deepcopy(self) - new_builder.registry = self.registry - if omit_man_model_data: - new_builder.manufacturer_model_metadata = [] - UNBUILT_QUIRK_BUILDERS.append(new_builder) - return new_builder diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 534001cbc..d119606aa 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -88,8 +88,7 @@ from zha.event import EventBase from zha.exceptions import ZHAException from zha.mixins import LogMixin -from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, DeviceMatch, ZigpyOp -from zha.quirks.metadata import DeviceAlertMetadata, QuirkDefinition +from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch, register_device from zha.zigbee.cluster_config import ( aggregate_cluster_configs, configure_cluster_configs, @@ -365,24 +364,18 @@ class ExtendedDeviceInfo(DeviceInfo): endpoint_names: list[EndpointNameInfo] +@register_device class Device(LogMixin, EventBase): """ZHA Zigbee device object.""" - unique_id: str - - # Quirk matching criteria and zigpy-level modifications. Set by subclasses - # registered with `zha.quirks.register_device`; the base class matches - # nothing. `_zigpy_device_class` optionally replaces the zigpy device - # object with an instance of the given class, which must accept - # `(application, ieee, nwk, replaces)` like `zigpy.quirks.v2.CustomDeviceV2`. - _device_match: DeviceMatch | None = None - _zigpy_device_class: type[zigpy.device.Device] | None = None - _zigpy_ops: tuple[ZigpyOp, ...] = () + # The default ZHA device object matches any device + _device_match = DeviceMatch( + applies_to=(), + filters=(), + ) - # ZHA-level quirk metadata (entities, triggers, alerts, naming), set by - # `zha.quirks.v2.QuirkBuilder` compiled quirks. Hand-written quirks express - # the same things by overriding `Device` directly. - _quirk_definition: QuirkDefinition | None = None + # The default ZHA device object does not need a custom zigpy device subclass + _zigpy_device_cls: type[zigpy.device.Device] | None = None # Cached properties that depend on the zigpy device and must be invalidated # when the underlying device is swapped (e.g. after a re-interview). @@ -432,26 +425,19 @@ def __init__( self._init_from_zigpy_device(zigpy_device) - @classmethod - def apply_to_zigpy_device( - cls, zigpy_device: zigpy.device.Device - ) -> zigpy.device.Device: - """Apply this quirk's modifications to a freshly-constructed zigpy device. - - The default implementation wraps the device with `_zigpy_device_class` - (when set) and then applies `_zigpy_ops` in order. Override this for - arbitrary surgery, including returning a replacement device object. + def _compute_quirk_class(self) -> str: + """Return the dotted path identifying the applied quirk. + + Hand-written quirks report their own `Device` subclass; unquirked and + v1-quirked devices report the underlying zigpy device class. + `QuirkV2Device` overrides this to report the declarative quirk identity. """ - if cls._zigpy_device_class is not None: - zigpy_device = cls._zigpy_device_class( - zigpy_device.application, - zigpy_device.ieee, - zigpy_device.nwk, - zigpy_device, - ) - for op in cls._zigpy_ops: - op.apply(zigpy_device) - return zigpy_device + if type(self)._device_match is not None: + return f"{type(self).__module__}.{type(self).__name__}" + return ( + f"{self._zigpy_device.__class__.__module__}." + f"{self._zigpy_device.__class__.__name__}" + ) def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: """(Re-)initialize device state from a zigpy device. @@ -477,16 +463,12 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: with contextlib.suppress(AttributeError): delattr(self, attr) - self.quirk_applied: bool = type(self)._device_match is not None or isinstance( - self._zigpy_device, zigpy.quirks.BaseCustomDevice + self.quirk_applied: bool = ( + type(self)._device_match is not None + or self.quirk_metadata is not None + or isinstance(self._zigpy_device, zigpy.quirks.BaseCustomDevice) ) - if type(self)._device_match is not None: - self.quirk_class: str = f"{type(self).__module__}.{type(self).__name__}" - else: - self.quirk_class = ( - f"{self._zigpy_device.__class__.__module__}." - f"{self._zigpy_device.__class__.__name__}" - ) + self.quirk_class: str = self._compute_quirk_class() # add v1 quirk exposed features (legacy quirk id) qid: set[str] | str = getattr(self._zigpy_device, ATTR_QUIRK_ID, set()) @@ -548,9 +530,16 @@ def ieee(self) -> EUI64: return self._zigpy_device.ieee @property - def quirk_metadata(self) -> QuirkDefinition | None: - """Return the ZHA-level quirk metadata for this device.""" - return self._quirk_definition + def quirk_metadata(self) -> Any | None: + """Return the ZHA-level quirk metadata (a `QuirkDefinition`) or None. + + The base class and hand-written/v1 quirks have no ZHA-level metadata; + zhaquirks' `QuirkV2Device` overrides this to return its `QuirkDefinition`. + The metadata-driven discovery overlay and the `device_alerts` / + `friendly_name` / `skip_configuration` / `device_automation_triggers` + merges below are inert while this is None. + """ + return None @cached_property def manufacturer(self) -> str: @@ -595,8 +584,8 @@ def model(self) -> str: return self._zigpy_device.model @cached_property - def device_alerts(self) -> Iterable[DeviceAlertMetadata]: - """Return device alerts for this device.""" + def device_alerts(self) -> Iterable[Any]: + """Return device alerts (`DeviceAlertMetadata`) for this device.""" if self.quirk_metadata is None: return [] @@ -837,13 +826,13 @@ def new( zigpy_dev: zigpy.device.Device, gateway: Gateway, ) -> Device: - """Create new device, dispatching to the class matched during resolution.""" + """Create new device, dispatching to the factory matched during resolution.""" if zigpy_dev.ieee == gateway.state.node_info.ieee: return CoordinatorDevice(zigpy_dev, gateway) - quirk_cls = getattr(zigpy_dev, ZHA_DEVICE_CLASS_ATTRIBUTE, None) - if quirk_cls is not None: - return quirk_cls(zigpy_dev, gateway) + entry = getattr(zigpy_dev, QUIRK_REGISTRY_ENTRY_ATTR, None) + if entry is not None: + return entry.zha_device_cls(zigpy_dev, gateway) return cls(zigpy_dev, gateway) @@ -1196,7 +1185,11 @@ def _apply_entity_metadata_changes(self, entity: PlatformEntity) -> None: entity._attr_fallback_name = meta.new_fallback_name def discover_entities(self) -> Iterator[BaseEntity]: - """Yield entities for this device.""" + """Yield the default (ZCL) entities for this device. + + Declarative quirks add their exposed entities by overriding this in + zhaquirks' `QuirkV2Device`; hand-written quirks override it directly. + """ # TODO: purge old coordinator entities if self.is_coordinator: return @@ -1212,8 +1205,6 @@ def discover_entities(self) -> Iterator[BaseEntity]: ) yield from discovery.discover_entities_for_endpoint(endpoint) - yield from discovery.discover_quirks_v2_entities(self) - def _discover_new_entities(self) -> None: self._discovered_entities.clear() From a2771a731a90fb4042e0a15feb7183e3a1e23843 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:43:23 -0400 Subject: [PATCH 11/41] Get device tests passing --- tests/test_discover.py | 22 +++++++------ zha/quirks/__init__.py | 70 +++++++++++++++++++++++++++--------------- zha/zigbee/device.py | 49 ++++++++++++----------------- 3 files changed, 78 insertions(+), 63 deletions(-) diff --git a/tests/test_discover.py b/tests/test_discover.py index d1569f2e0..c5ddba9d2 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -63,7 +63,7 @@ from zha.application.platforms.number import BaseNumber, NumberMode from zha.quirks import ( DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, - ZHA_DEVICE_FACTORY_ATTRIBUTE, + QUIRK_REGISTRY_ENTRY_ATTR, resolve_zigpy_device, ) from zha.units import UnitOfTime @@ -495,9 +495,11 @@ async def test_quirks_v2_entity_no_metadata( zigpy_device = _get_test_device( zha_gateway, "Ikea of Sweden2", "TRADFRI remote control2" ) - quirk_cls = getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE) - quirk_cls._quirk_definition = attrs.evolve( - quirk_cls._quirk_definition, entity_metadata=() + factory_kwargs = getattr( + zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR + ).zha_device_factory.keywords + factory_kwargs["quirk_definition"] = attrs.evolve( + factory_kwargs["quirk_definition"], entity_metadata=() ) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert ( @@ -523,11 +525,13 @@ class UnknownEntityMetadata: cluster_type = ClusterType.Server entity_platform = Platform.UPDATE - quirk_cls = getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE) - quirk_cls._quirk_definition = attrs.evolve( - quirk_cls._quirk_definition, + factory_kwargs = getattr( + zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR + ).zha_device_factory.keywords + factory_kwargs["quirk_definition"] = attrs.evolve( + factory_kwargs["quirk_definition"], entity_metadata=( - *quirk_cls._quirk_definition.entity_metadata, + *factory_kwargs["quirk_definition"].entity_metadata, UnknownEntityMetadata(), ), ) @@ -646,7 +650,7 @@ async def test_quirks_v2_metadata_bad_device_classes( assert expected_exception_string in caplog.text # remove the quirk so we don't pollute the rest of the tests - QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, ZHA_DEVICE_FACTORY_ATTRIBUTE)) + QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR)) async def test_quirks_v2_fallback_name(zha_gateway: Gateway) -> None: diff --git a/zha/quirks/__init__.py b/zha/quirks/__init__.py index 5d94c7c0e..7b8472ff7 100644 --- a/zha/quirks/__init__.py +++ b/zha/quirks/__init__.py @@ -39,6 +39,7 @@ DEVICE_REGISTRY: DeviceRegistry + class ModelInfo(NamedTuple): """A (manufacturer, model) pair to match. `None` is a wildcard.""" @@ -104,24 +105,46 @@ def matches(self, zigpy_device: zigpy.device.Device) -> bool: @dataclass(frozen=True) -class QuirkRegistryEntry: - """A registered device quirk: matching criteria plus how to build the device.""" +class QuirkSource: + """Where a quirk is defined: provenance for diagnostics and custom-quirk purging.""" + + module: str + file: str | None + line: int | None + label: str + + @classmethod + def from_class(cls, target: type) -> QuirkSource: + """Derive provenance from a hand-written quirk class.""" + return cls( + module=target.__module__, + file=inspect.getsourcefile(target), + line=inspect.getsourcelines(target)[1], + label=target.__qualname__, + ) - # Matcher for this device - device_match: DeviceMatch - # Zigpy device class replacement - zigpy_device_cls: type[zigpy.device.Device] | None = None +def make_zigpy_device_replacement( + device_cls: type[zigpy.device.Device], +) -> Callable[[zigpy.device.Device], zigpy.device.Device]: + """Return a transform wrapping a device in `device_cls` (a `BaseCustomDevice`).""" + + def _replace(device: zigpy.device.Device) -> zigpy.device.Device: + return device_cls(device.application, device.ieee, device.nwk, device) - # Transformations to apply to the zigpy device object. Used byzha-device-handlers to - # avoid creating unique subclasses at runtime. - zigpy_device_transforms: tuple[Callable[[zigpy.device.Device], zigpy.device.Device], ...] = () + return _replace - # ZHA device class replacement - zha_device_cls: type[Device] | None = None - # A somewhat human-readable way to find the quirk defining this device - quirk_file: str | None = None +@dataclass(frozen=True) +class QuirkRegistryEntry: + """A registered quirk: how to match, mutate, build and locate a device.""" + + device_match: DeviceMatch + zigpy_transforms: tuple[ + Callable[[zigpy.device.Device], zigpy.device.Device], ... + ] = () + zha_device_factory: Callable[..., Device] | None = None + source: QuirkSource | None = None class DeviceRegistry: @@ -169,9 +192,9 @@ def purge_custom_quirks(self, custom_quirks_root: Path) -> None: """Remove quirks loaded from the custom quirks directory.""" for entries in self._registry.values(): for entry in list(entries): - if entry.quirk_file is None: + if entry.source is None or entry.source.file is None: continue - if Path(entry.quirk_file).is_relative_to(custom_quirks_root): + if Path(entry.source.file).is_relative_to(custom_quirks_root): _LOGGER.debug("Removing stale custom quirk: %s", entry) entries.remove(entry) @@ -184,15 +207,17 @@ def register_device(cls: type[Device]) -> type[Device]: if cls._device_match is None: raise ValueError(f"{cls!r} does not define `_device_match`") - module = inspect.getmodule(cls) + transforms: list[Callable[[zigpy.device.Device], zigpy.device.Device]] = [] + if cls._zigpy_device_cls is not None: + transforms.append(make_zigpy_device_replacement(cls._zigpy_device_cls)) + transforms.extend(cls._zigpy_device_transforms) DEVICE_REGISTRY.register( QuirkRegistryEntry( device_match=cls._device_match, - zigpy_device_cls=cls._zigpy_device_cls, - zigpy_device_transforms=(), - zha_device_cls=cls, - quirk_file=module.__file__ if module is not None else None, + zigpy_transforms=tuple(transforms), + zha_device_factory=cls, + source=QuirkSource.from_class(cls), ) ) @@ -221,10 +246,7 @@ def resolve_zigpy_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Devi resolved_device = zigpy_device - if entry.zigpy_device_cls is not None: - resolved_device = entry.zigpy_device_cls.from_zigpy_device(zigpy_device) - - for transform in entry.zigpy_device_transforms: + for transform in entry.zigpy_transforms: resolved_device = transform(resolved_device) # Sneak the registry entry in with the device so ZHA can use it diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index d119606aa..06892ed1f 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -88,7 +88,7 @@ from zha.event import EventBase from zha.exceptions import ZHAException from zha.mixins import LogMixin -from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch, register_device +from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch from zha.zigbee.cluster_config import ( aggregate_cluster_configs, configure_cluster_configs, @@ -364,18 +364,15 @@ class ExtendedDeviceInfo(DeviceInfo): endpoint_names: list[EndpointNameInfo] -@register_device class Device(LogMixin, EventBase): """ZHA Zigbee device object.""" - # The default ZHA device object matches any device - _device_match = DeviceMatch( - applies_to=(), - filters=(), - ) - - # The default ZHA device object does not need a custom zigpy device subclass + # Authoring surface for hand-written quirks; `None` marks the unquirked fallback. + _device_match: DeviceMatch | None = None _zigpy_device_cls: type[zigpy.device.Device] | None = None + _zigpy_device_transforms: tuple[ + Callable[[zigpy.device.Device], zigpy.device.Device], ... + ] = () # Cached properties that depend on the zigpy device and must be invalidated # when the underlying device is swapped (e.g. after a re-interview). @@ -425,20 +422,6 @@ def __init__( self._init_from_zigpy_device(zigpy_device) - def _compute_quirk_class(self) -> str: - """Return the dotted path identifying the applied quirk. - - Hand-written quirks report their own `Device` subclass; unquirked and - v1-quirked devices report the underlying zigpy device class. - `QuirkV2Device` overrides this to report the declarative quirk identity. - """ - if type(self)._device_match is not None: - return f"{type(self).__module__}.{type(self).__name__}" - return ( - f"{self._zigpy_device.__class__.__module__}." - f"{self._zigpy_device.__class__.__name__}" - ) - def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: """(Re-)initialize device state from a zigpy device. @@ -463,12 +446,18 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: with contextlib.suppress(AttributeError): delattr(self, attr) - self.quirk_applied: bool = ( - type(self)._device_match is not None - or self.quirk_metadata is not None - or isinstance(self._zigpy_device, zigpy.quirks.BaseCustomDevice) + # ZHA quirks stash their registry entry on the device; v1 produces a BaseCustomDevice. + entry = getattr(self._zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR, None) + self.quirk_applied: bool = entry is not None or isinstance( + self._zigpy_device, zigpy.quirks.BaseCustomDevice ) - self.quirk_class: str = self._compute_quirk_class() + if entry is not None and entry.source is not None: + self.quirk_class: str = f"{entry.source.module}.{entry.source.label}" + else: + self.quirk_class = ( + f"{self._zigpy_device.__class__.__module__}." + f"{self._zigpy_device.__class__.__name__}" + ) # add v1 quirk exposed features (legacy quirk id) qid: set[str] | str = getattr(self._zigpy_device, ATTR_QUIRK_ID, set()) @@ -831,8 +820,8 @@ def new( return CoordinatorDevice(zigpy_dev, gateway) entry = getattr(zigpy_dev, QUIRK_REGISTRY_ENTRY_ATTR, None) - if entry is not None: - return entry.zha_device_cls(zigpy_dev, gateway) + if entry is not None and entry.zha_device_factory is not None: + return entry.zha_device_factory(zigpy_dev, gateway) return cls(zigpy_dev, gateway) From 79c66e3d82021b14c68eba423864f5ee0d88e307 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:42:31 -0400 Subject: [PATCH 12/41] Rename `CustomDeviceV2` to `CustomZigpyDevice` --- tests/test_binary_sensor.py | 4 ++-- tests/test_button.py | 8 ++++---- tests/test_discover.py | 8 ++++---- tests/test_select.py | 4 ++-- tests/test_sensor.py | 16 ++++++++-------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 3f019c5e0..9b387a578 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -9,7 +9,7 @@ from zigpy.profiles import zha import zigpy.profiles.zha from zigpy.quirks import DeviceRegistry -from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder +from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import general, measurement, security from zigpy.zcl.clusters.general import OnOff @@ -287,7 +287,7 @@ async def test_quirks_binary_sensor_attr_converter(zha_gateway: Gateway) -> None zigpy_device_ = registry.get_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert isinstance(zigpy_device_, CustomZigpyDevice) cluster = zigpy_device_.endpoints[1].on_off zha_device = await join_zigpy_device(zha_gateway, zigpy_device_) diff --git a/tests/test_button.py b/tests/test_button.py index 0ec745c1a..d8a4acca9 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -16,7 +16,7 @@ from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry -from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder +from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import general, security @@ -232,7 +232,7 @@ async def custom_button_device(zha_gateway: Gateway): zigpy_device = registry.get_device(zigpy_device) - assert isinstance(zigpy_device, CustomDeviceV2) + assert isinstance(zigpy_device, CustomZigpyDevice) # XXX: this should be handled automatically, patch quirks added cluster patch_cluster_for_testing(zigpy_device.endpoints[1].mfg_identify) @@ -336,7 +336,7 @@ async def test_quirks_write_attr_buttons_uid(zha_gateway: Gateway) -> None: zigpy_device_ = registry.get_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert isinstance(zigpy_device_, CustomZigpyDevice) zha_device = await join_zigpy_device(zha_gateway, zigpy_device_) entity_btn_1 = get_entity(zha_device, platform=Platform.BUTTON, qualifier="btn_1") @@ -417,7 +417,7 @@ async def test_quirks_v2_button_only_cluster_is_not_configured( with patch("zha.application.discovery.discover_entities_for_endpoint"): zha_device = await join_zigpy_device(zha_gateway, zigpy_device) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) # The quirks v2 button entity is created and usable entity = get_entity(zha_device, platform=Platform.BUTTON, entity_type=Button) diff --git a/tests/test_discover.py b/tests/test_discover.py index c5ddba9d2..d731b2375 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -14,13 +14,13 @@ import attrs import pytest -from zhaquirks.ikea import PowerConfig1CRCluster, ScenesCluster -from zhaquirks.v2 import QuirkBuilder -from zhaquirks.v2.metadata import ( +from zhaquirks.builder import QuirkBuilder +from zhaquirks.builder.metadata import ( BinarySensorMetadata, NumberMetadata, ZCLSensorMetadata, ) +from zhaquirks.ikea import PowerConfig1CRCluster, ScenesCluster from zhaquirks.xiaomi import ( BasicCluster, LocalIlluminanceMeasurementCluster, @@ -762,7 +762,7 @@ async def test_devices_from_files( ) # The quirk class path varies with the quirks implementation (v2 quirks - # used to all be `zigpy.quirks.v2.CustomDeviceV2`, compiled ZHA quirks + # used to all be `zigpy.quirks.v2.CustomZigpyDevice`, compiled ZHA quirks # name the defining module); `quirk_applied` still has to match. del loaded_device_data["quirk_class"] del device_data["quirk_class"] diff --git a/tests/test_select.py b/tests/test_select.py index 770d4e4ea..0685d566c 100644 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -13,7 +13,7 @@ from zigpy.const import SIG_EP_PROFILE from zigpy.profiles import zha from zigpy.quirks import CustomCluster, CustomDevice, get_device -from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder +from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl import foundation @@ -191,7 +191,7 @@ async def test_on_off_select_attribute_report_v2( zha_device = await join_zigpy_device(zha_gateway, zigpy_device) cluster = zigpy_device.endpoints[1].opple_cluster - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) entity = get_entity( zha_device, diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 14587a60a..2f1aadf44 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -14,7 +14,7 @@ import zigpy.profiles.zha from zigpy.quirks import CustomCluster, DeviceRegistry, get_device from zigpy.quirks.v2 import ( - CustomDeviceV2, + CustomZigpyDevice, QuirkBuilder, ReportingConfig as QuirksReportingConfig, ) @@ -1460,7 +1460,7 @@ async def test_timestamp_sensor_v2(zha_gateway: Gateway) -> None: """Test quirks defined sensor.""" zha_device, cluster = await zigpy_device_timestamp_sensor_v2_mock(zha_gateway) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) entity = get_entity(zha_device, platform=Platform.SENSOR, qualifier="start_time") await send_attributes_report(zha_gateway, cluster, {"start_time": 781355715}) @@ -1562,7 +1562,7 @@ async def test_last_feeding_size_sensor_v2(zha_gateway: Gateway) -> None: """Test quirks defined sensor.""" zha_device, cluster = await zigpy_device_aqara_sensor_v2_mock(zha_gateway) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) entity = get_entity( zha_device, platform=Platform.SENSOR, qualifier="last_feeding_size" ) @@ -1580,7 +1580,7 @@ async def test_state_class( """Test quirks defined sensor.""" zha_device, cluster = await zigpy_device_aqara_sensor_v2_mock(zha_gateway) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) power_entity = get_entity( zha_device, platform=Platform.SENSOR, @@ -1615,7 +1615,7 @@ async def test_quirks_v2_sensor_reporting_configures_cluster( with patch("zha.application.discovery.discover_entities_for_endpoint"): zha_device, opple_cluster = await zigpy_device_aqara_sensor_v2_mock(zha_gateway) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) # The quirks v2 sensor entity is created last_feeding_size = get_entity( @@ -1686,7 +1686,7 @@ async def test_quirks_v2_sensor_attribute_init_reads_cluster( with patch("zha.application.discovery.discover_entities_for_endpoint"): zha_device = await join_zigpy_device(zha_gateway, zigpy_device) - assert isinstance(zha_device.device, CustomDeviceV2) + assert isinstance(zha_device.device, CustomZigpyDevice) # The quirks v2 sensor entity is created last_feeding_size = get_entity( @@ -1920,7 +1920,7 @@ async def test_quirks_sensor_attr_converter(zha_gateway: Gateway) -> None: zigpy_device_ = registry.get_device(zigpy_dev) - assert isinstance(zigpy_device_, CustomDeviceV2) + assert isinstance(zigpy_device_, CustomZigpyDevice) cluster = zigpy_device_.endpoints[1].analog_input zha_device = await join_zigpy_device(zha_gateway, zigpy_device_) @@ -1972,7 +1972,7 @@ async def test_ignore_non_value_quirks_v2(zha_gateway: Gateway) -> None: zha_gateway.application_controller, "tests/data/devices/frient-a-s-aqszb-110.json", ) - assert isinstance(zigpy_dev, CustomDeviceV2) + assert isinstance(zigpy_dev, CustomZigpyDevice) zha_device = await join_zigpy_device(zha_gateway, zigpy_dev) cluster = zha_device.device.endpoints[38].develco_voc_level From af74f23328a96ac8af88756a857dfb7965771dbd Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:42:41 -0400 Subject: [PATCH 13/41] Forward `uninitialized_packet_handler` --- zha/application/gateway.py | 3 +++ zha/application/helpers.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/zha/application/gateway.py b/zha/application/gateway.py index c1a5532ed..a7f7e368a 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -250,6 +250,9 @@ async def _async_initialize(self) -> None: auto_form=False, start_radio=False, device_resolver=resolve_zigpy_device, + uninitialized_packet_handler=( + self.config.config.quirks_configuration.uninitialized_packet_handler + ), ) await self.application_controller.startup(auto_form=True) diff --git a/zha/application/helpers.py b/zha/application/helpers.py index d4b49ac87..df65e5b55 100644 --- a/zha/application/helpers.py +++ b/zha/application/helpers.py @@ -380,6 +380,11 @@ class QuirksConfiguration: setup_function: Callable[[str | None], None] | None = dataclasses.field( default=None ) + # Injected by the consumer to short-circuit flaky joins (Xiaomi quick-init); + # forwarded to the controller as its uninitialized-packet handler. + uninitialized_packet_handler: Callable[..., None] | None = dataclasses.field( + default=None + ) @dataclass(kw_only=True, slots=True) From 4feb202a34cc5bf2580c0a3002da9628c6cd5d80 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:00:03 -0400 Subject: [PATCH 14/41] Get ZHA tests passing --- tests/test_discover.py | 72 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/tests/test_discover.py b/tests/test_discover.py index d731b2375..2a5aba484 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -64,6 +64,8 @@ from zha.quirks import ( DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, QUIRK_REGISTRY_ENTRY_ATTR, + DeviceMatch, + ModelInfo, resolve_zigpy_device, ) from zha.units import UnitOfTime @@ -553,7 +555,7 @@ class UnknownEntityMetadata: device_info = f"{zigpy_device.ieee}-{zha_device.name}" device_regex = ( - rf"Device: {re.escape(device_info)} has an entity with details: (.*?) that" + rf"Device: {re.escape(device_info)} has an entity with metadata: (.*?) that" rf" does not have an entity class mapping - unable to create entity" ) assert re.search(device_regex, caplog.text) @@ -677,6 +679,74 @@ async def test_quirks_v2_fallback_name(zha_gateway: Gateway) -> None: assert entity.fallback_name == "Fallback name" +async def test_device_match_firmware_version(zha_gateway: Gateway) -> None: + """Test DeviceMatch firmware-version filtering against the OTA file version.""" + zigpy_device = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [zigpy.zcl.clusters.general.Basic.cluster_id], + SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id], + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.PUMP, + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, + } + }, + manufacturer="Some Manufacturer", + model="Some Model", + ) + ota = zigpy_device.endpoints[1].out_clusters[ + zigpy.zcl.clusters.general.Ota.cluster_id + ] + ota.update_attribute( + zigpy.zcl.clusters.general.Ota.AttributeDefs.current_file_version.id, 0x12345678 + ) + + applies_to = (ModelInfo("Some Manufacturer", "Some Model"),) + + # In range [min, max) + assert DeviceMatch( + applies_to=applies_to, + firmware_version_min=0x12345678, + firmware_version_max=0x12345679, + ).matches(zigpy_device) + + # Below min + assert not DeviceMatch( + applies_to=applies_to, firmware_version_min=0x12345679 + ).matches(zigpy_device) + + # max is exclusive + assert not DeviceMatch( + applies_to=applies_to, firmware_version_max=0x12345678 + ).matches(zigpy_device) + + # Missing firmware version honors `allow_missing` + no_ota_device = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [zigpy.zcl.clusters.general.Basic.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.PUMP, + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, + } + }, + manufacturer="Some Manufacturer", + model="Some Model", + ieee="01:2d:6f:00:0a:90:69:e9", + ) + assert DeviceMatch( + applies_to=applies_to, + firmware_version_min=0x12345678, + firmware_version_allow_missing=True, + ).matches(no_ota_device) + assert not DeviceMatch( + applies_to=applies_to, + firmware_version_min=0x12345678, + firmware_version_allow_missing=False, + ).matches(no_ota_device) + + def pytest_generate_tests(metafunc): """Generate tests for all device files.""" if "file_path" in metafunc.fixturenames: From 810340e945841f6c9cb58f164a1bdf701f9c52a8 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:57:17 -0400 Subject: [PATCH 15/41] Provide hooks for quirks to implement entity filtering/metadata changes --- zha/zigbee/device.py | 203 +++++++++---------------------------------- 1 file changed, 40 insertions(+), 163 deletions(-) diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 06892ed1f..6d1b4236c 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -104,44 +104,6 @@ DIAGNOSTICS_JSON_VERSION = 2 -def _entity_targets_cluster( - entity: PlatformEntity, - cluster_id: int, - cluster_type: zigpy.zcl.ClusterType | None = None, -) -> bool: - """Return True if `entity` targets the given cluster (and direction).""" - match = entity._cluster_match - if match is None: - # Generated quirks-v2 entities have no class-level `_cluster_match` but - # do have a concrete backing cluster; match against it directly. - cluster = entity.cluster - if cluster.cluster_id != cluster_id: - return False - if cluster_type is None: - return True - actual_type = ( - zigpy.zcl.ClusterType.Client - if cluster.is_client - else zigpy.zcl.ClusterType.Server - ) - return cluster_type == actual_type - - if cluster_type is None or cluster_type == zigpy.zcl.ClusterType.Server: - if ( - cluster_id in match.server_clusters - or cluster_id in match.optional_server_clusters - ): - return True - if cluster_type is not None: - return False - if (cluster_type is None or cluster_type == zigpy.zcl.ClusterType.Client) and ( - cluster_id in match.client_clusters - or cluster_id in match.optional_client_clusters - ): - return True - return False - - def get_cluster_attr_data(cluster: Cluster) -> list[dict]: """Return cluster attribute data.""" attributes_info = [] @@ -463,11 +425,8 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: qid: set[str] | str = getattr(self._zigpy_device, ATTR_QUIRK_ID, set()) self.exposes_features: set[str] = {qid} if isinstance(qid, str) else set(qid) - # add v2 quirk exposed features - if self.quirk_metadata is not None: - self.exposes_features.update( - f.feature for f in self.quirk_metadata.exposes_features - ) + # add quirk-exposed features (declarative quirks override this hook) + self.exposes_features |= self._quirk_exposes_features() self._firmware_version: str | None = None @@ -520,32 +479,47 @@ def ieee(self) -> EUI64: @property def quirk_metadata(self) -> Any | None: - """Return the ZHA-level quirk metadata (a `QuirkDefinition`) or None. + """Return the ZHA-level quirk metadata, or None. - The base class and hand-written/v1 quirks have no ZHA-level metadata; - zhaquirks' `QuirkV2Device` overrides this to return its `QuirkDefinition`. - The metadata-driven discovery overlay and the `device_alerts` / - `friendly_name` / `skip_configuration` / `device_automation_triggers` - merges below are inert while this is None. + The base class and hand-written/v1 quirks have none; zhaquirks' + `QuirkV2Device` overrides this (and the `_quirk_*`/`_resolve_*` hooks + below) to surface its `QuirkDefinition`. """ return None + def _quirk_exposes_features(self) -> set[str]: + """Extra exposed features contributed by a quirk.""" + return set() + + def _quirk_skip_configuration(self) -> bool: + """Whether a quirk forces configuration to be skipped.""" + return False + + def _quirk_device_automation_triggers( + self, + ) -> dict[tuple[str, str], dict[str, str]]: + """Device automation triggers contributed by a quirk.""" + return {} + + def _is_entity_removed_by_quirk(self, entity: PlatformEntity) -> bool: + """Whether a quirk hides this default entity (declarative quirks override).""" + return False + + def _apply_entity_metadata_changes(self, entity: PlatformEntity) -> None: + """Apply a quirk's metadata overrides to an entity (declarative quirks override).""" + @cached_property def manufacturer(self) -> str: """Return manufacturer for device.""" + return self._resolve_manufacturer() + + def _resolve_manufacturer(self) -> str: + """Resolve the manufacturer name (declarative quirks override this).""" if self.is_active_coordinator: manufacturer = ( self.gateway.application_controller.state.node_info.manufacturer ) - if manufacturer is None: - return "" - return manufacturer - - if ( - self.quirk_metadata is not None - and self.quirk_metadata.friendly_name is not None - ): - return self.quirk_metadata.friendly_name.manufacturer + return manufacturer if manufacturer is not None else "" if self._zigpy_device.manufacturer is None: return UNKNOWN_MANUFACTURER @@ -555,18 +529,16 @@ def manufacturer(self) -> str: @cached_property def model(self) -> str: """Return model for device.""" + return self._resolve_model() + + def _resolve_model(self) -> str: + """Resolve the model name (declarative quirks override this).""" if self.is_active_coordinator: model = self.gateway.application_controller.state.node_info.model if model is None: return f"Generic Zigbee Coordinator ({self.gateway.radio_type.pretty_name})" return model - if ( - self.quirk_metadata is not None - and self.quirk_metadata.friendly_name is not None - ): - return self.quirk_metadata.friendly_name.model - if self._zigpy_device.model is None: return UNKNOWN_MODEL @@ -574,11 +546,8 @@ def model(self) -> str: @cached_property def device_alerts(self) -> Iterable[Any]: - """Return device alerts (`DeviceAlertMetadata`) for this device.""" - if self.quirk_metadata is None: - return [] - - return self.quirk_metadata.device_alerts + """Return device alerts for this device (declarative quirks override this).""" + return [] @cached_property def manufacturer_code(self) -> int | None: @@ -673,7 +642,7 @@ def is_groupable(self) -> bool: @cached_property def skip_configuration(self) -> bool: """Return true if the device should not issue configuration related commands.""" - if self.quirk_metadata is not None and self.quirk_metadata.skip_configuration: + if self._quirk_skip_configuration(): return True return self._zigpy_device.skip_configuration or bool(self.is_active_coordinator) @@ -695,8 +664,7 @@ def device_automation_commands(self) -> dict[str, list[tuple[str, str]]]: def device_automation_triggers(self) -> dict[tuple[str, str], dict[str, str]]: """Return the device automation triggers for this device.""" triggers = get_device_automation_triggers(self._zigpy_device) - if self.quirk_metadata is not None: - triggers.update(self.quirk_metadata.device_automation_triggers) + triggers.update(self._quirk_device_automation_triggers()) return triggers @property @@ -1082,97 +1050,6 @@ def emit_reconfigure_done(self) -> None: DeviceConfiguredEvent(device_ieee=self.ieee), ) - def _is_entity_removed_by_quirk(self, entity: PlatformEntity) -> bool: - if self.quirk_metadata is None: - return False - - if entity.PLATFORM == Platform.VIRTUAL: - return False - - for meta in self.quirk_metadata.disabled_default_entities: - _LOGGER.debug("Checking if entity %s is removed by %s", entity, meta) - - if meta.unique_id_suffix is not None and not entity.unique_id.endswith( - meta.unique_id_suffix - ): - continue - - if meta.endpoint_id is not None and entity.endpoint.id != meta.endpoint_id: - continue - - if meta.cluster_id is not None and not _entity_targets_cluster( - entity, meta.cluster_id - ): - continue - - if meta.function is not None and not meta.function(entity): - continue - - return True - - return False - - def _apply_entity_metadata_changes(self, entity: PlatformEntity) -> None: - """Apply entity metadata changes from quirks v2.""" - if self.quirk_metadata is None: - return - - if entity.PLATFORM == Platform.VIRTUAL: - return - - for meta in self.quirk_metadata.changed_entity_metadata: - if meta.unique_id_suffix is not None and not entity.unique_id.endswith( - meta.unique_id_suffix - ): - continue - - if meta.endpoint_id is not None and entity.endpoint.id != meta.endpoint_id: - continue - - if meta.cluster_id is not None and not _entity_targets_cluster( - entity, meta.cluster_id, cluster_type=meta.cluster_type - ): - continue - - if meta.function is not None and not meta.function(entity): - continue - - # Apply metadata changes - _LOGGER.debug( - "Applying metadata changes from %s to entity %s", meta, entity - ) - - if meta.new_primary is not None: - entity._attr_primary = meta.new_primary - - if meta.new_unique_id is not None: - entity._unique_id = meta.new_unique_id - - if meta.new_translation_key is not None: - entity._attr_translation_key = meta.new_translation_key - - if meta.new_translation_placeholders is not None: - entity._attr_translation_placeholders = ( - meta.new_translation_placeholders - ) - - if meta.new_device_class is not None: - entity._attr_device_class = meta.new_device_class - - if meta.new_state_class is not None: - entity._attr_state_class = meta.new_state_class - - if meta.new_entity_category is not None: - entity._attr_entity_category = meta.new_entity_category - - if meta.new_entity_registry_enabled_default is not None: - entity._attr_entity_registry_enabled_default = ( - meta.new_entity_registry_enabled_default - ) - - if meta.new_fallback_name is not None: - entity._attr_fallback_name = meta.new_fallback_name - def discover_entities(self) -> Iterator[BaseEntity]: """Yield the default (ZCL) entities for this device. From 817de692a4fa8561a4e6b833cce7d4fb555abb9f Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:52:58 -0400 Subject: [PATCH 16/41] Register legacy v1 quirks using the same system --- zha/quirks/__init__.py | 42 ++++++++++++++++++++++-------------------- zha/zigbee/device.py | 9 +++------ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/zha/quirks/__init__.py b/zha/quirks/__init__.py index 7b8472ff7..c1e3b03e1 100644 --- a/zha/quirks/__init__.py +++ b/zha/quirks/__init__.py @@ -1,17 +1,4 @@ -"""Quirks support for ZHA. - -This package owns the matching, registration and dispatch of quirked `Device` -subclasses. A quirk is a `zha.zigbee.device.Device` subclass decorated with -`@register_device`: its `_device_match` decides which zigpy devices it wraps, -its `_zigpy_ops` describe the modifications applied to the zigpy device during -resolution, and everything else (entities, triggers, alerts, configuration) is -expressed by overriding the `Device` class itself. - -`resolve_device` is registered with zigpy as the application's device resolver. -It is handed a freshly-constructed zigpy device exactly once (on join and on -database load), applies the matching quirk's zigpy-level modifications, and -falls back to zigpy's legacy v1/v2 quirks registry when no ZHA quirk matches. -""" +"""Quirks support for ZHA, allowing custom `Device` objects to be swapped at runtime.""" from __future__ import annotations @@ -25,7 +12,6 @@ import zigpy.device import zigpy.profiles.zha -import zigpy.quirks from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import Ota @@ -152,20 +138,27 @@ class DeviceRegistry: def __init__(self) -> None: """Initialize the registry.""" + + # Normal registry that matches based on at least a model or manufacturer. self._registry: defaultdict[ModelInfo, list[QuirkRegistryEntry]] = defaultdict( list ) + # Matched against every device by their filters alone, used mostly for legacy v1 + # quirks without model/manufacturer filters. + self._wildcard_registry: list[QuirkRegistryEntry] = [] def register(self, entry: QuirkRegistryEntry) -> QuirkRegistryEntry: """Add a quirk entry to the registry.""" + if not entry.device_match.applies_to: + self._wildcard_registry.insert(0, entry) + return entry + for manufacturer, model in entry.device_match.applies_to: if manufacturer is None and model is None: raise ValueError( f"{entry!r} must specify a manufacturer and/or model to match" ) - # Most recently registered quirks take precedence, so quirks loaded - # from the custom quirks directory override built-in ones. self._registry[ModelInfo(manufacturer, model)].insert(0, entry) return entry @@ -181,16 +174,26 @@ def get(self, zigpy_device: zigpy.device.Device) -> QuirkRegistryEntry | None: if entry.device_match.matches(zigpy_device): return entry + for entry in self._wildcard_registry: + if entry.device_match.matches(zigpy_device): + return entry + return None def remove(self, entry: QuirkRegistryEntry) -> None: """Remove a quirk entry from the registry.""" + if not entry.device_match.applies_to: + self._wildcard_registry.remove(entry) + return + for manufacturer, model in entry.device_match.applies_to: self._registry[ModelInfo(manufacturer, model)].remove(entry) def purge_custom_quirks(self, custom_quirks_root: Path) -> None: """Remove quirks loaded from the custom quirks directory.""" - for entries in self._registry.values(): + + # Prefer the explicit registry to the wildcard registry + for entries in (*self._registry.values(), self._wildcard_registry): for entry in list(entries): if entry.source is None or entry.source.file is None: continue @@ -231,10 +234,9 @@ def resolve_zigpy_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Devi if hasattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR): return zigpy_device - # Fall back to legacy zigpy v1 quirks entry = DEVICE_REGISTRY.get(zigpy_device) if entry is None: - return zigpy.quirks.get_device(zigpy_device) + return zigpy_device _LOGGER.debug( "Resolved %s/%s (%s) to quirk %s", diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 6d1b4236c..7fd510df0 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -20,7 +20,6 @@ from zigpy.device import Device as ZigpyDevice import zigpy.exceptions from zigpy.profiles import PROFILES -import zigpy.quirks from zigpy.types import uint1_t, uint8_t, uint16_t from zigpy.types.named import EUI64, NWK, ExtendedPanId from zigpy.typing import UNDEFINED, UndefinedType @@ -408,11 +407,9 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: with contextlib.suppress(AttributeError): delattr(self, attr) - # ZHA quirks stash their registry entry on the device; v1 produces a BaseCustomDevice. + # Both v1 and v2 quirks stash their registry entry on the resolved device. entry = getattr(self._zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR, None) - self.quirk_applied: bool = entry is not None or isinstance( - self._zigpy_device, zigpy.quirks.BaseCustomDevice - ) + self.quirk_applied: bool = entry is not None if entry is not None and entry.source is not None: self.quirk_class: str = f"{entry.source.module}.{entry.source.label}" else: @@ -997,7 +994,7 @@ async def async_configure(self) -> None: """Configure the device.""" self.debug("started configuration") - if isinstance(self._zigpy_device, zigpy.quirks.BaseCustomDevice): + if hasattr(self._zigpy_device, "apply_custom_configuration"): self.debug("applying quirks custom device configuration") await self._zigpy_device.apply_custom_configuration() From 5e57140180c02489db5bd0e350fc8a3697e9f0cc Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:57:33 -0400 Subject: [PATCH 17/41] Fix quirk regeneration --- tests/conftest.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index be3ad1576..5dfcb57be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,6 +12,8 @@ import looptime import pytest +import zhaquirks +import zhaquirks.legacy import zigpy from zigpy.application import ControllerApplication import zigpy.config @@ -30,6 +32,7 @@ AlarmControlPanelOptions, CoordinatorConfiguration, LightOptions, + QuirksConfiguration, ZHAConfiguration, ZHAData, ) @@ -278,6 +281,13 @@ def make_zha_data() -> ZHAData: master_code="4321", failed_tries=2, ), + quirks_configuration=QuirksConfiguration( + enabled=True, + setup_function=zhaquirks.setup, + uninitialized_packet_handler=( + zhaquirks.legacy.handle_message_from_uninitialized_sender + ), + ), ) ) From a2772d253557f51ca11ce8e26b26f94f14a3ae65 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 11:03:00 -0400 Subject: [PATCH 18/41] Use a different format for quirk labels --- zha/zigbee/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 7fd510df0..02102f339 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -411,7 +411,7 @@ def _init_from_zigpy_device(self, zigpy_device: zigpy.device.Device) -> None: entry = getattr(self._zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR, None) self.quirk_applied: bool = entry is not None if entry is not None and entry.source is not None: - self.quirk_class: str = f"{entry.source.module}.{entry.source.label}" + self.quirk_class: str = f"{entry.source.module}:{entry.source.label}" else: self.quirk_class = ( f"{self._zigpy_device.__class__.__module__}." From 7c5a6c45c53e93d9781dadd7e04a67c6b27b8c6b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 11:28:32 -0400 Subject: [PATCH 19/41] Fix `zigpy.quirks` -> `zhaquirks` --- tests/conftest.py | 8 ++++++ tests/test_binary_sensor.py | 5 ++-- tests/test_button.py | 6 +++-- tests/test_climate.py | 49 +++++++++++++++++++++++-------------- tests/test_device.py | 18 ++++++++------ tests/test_discover.py | 1 - tests/test_select.py | 6 +++-- tests/test_sensor.py | 21 ++++++++-------- tests/test_switch.py | 7 +++--- zha/quirks/__init__.py | 15 +++++++++++- 10 files changed, 88 insertions(+), 48 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5dfcb57be..9eb760efa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,6 +37,7 @@ ZHAData, ) from zha.async_ import ZHAJob +from zha.quirks import DEVICE_REGISTRY FIXTURE_GRP_ID = 0x1001 FIXTURE_GRP_NAME = "fixture group" @@ -122,6 +123,13 @@ def long_repr_strings() -> Generator[None, None, None]: arepr.maxother = original_maxother +@pytest.fixture(autouse=True) +def preserve_quirk_registry() -> Generator[None, None, None]: + """Roll back any quirks a test registers so they don't leak into later tests.""" + with DEVICE_REGISTRY.preserve_state(): + yield + + @pytest.fixture(autouse=True) def expected_lingering_tasks() -> bool: """Temporary ability to bypass test failures. diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 9b387a578..925cede58 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -6,10 +6,11 @@ from unittest.mock import call import pytest +from zhaquirks.builder import QuirkBuilder +from zhaquirks.device import CustomZigpyDevice +from zhaquirks.legacy import DeviceRegistry from zigpy.profiles import zha import zigpy.profiles.zha -from zigpy.quirks import DeviceRegistry -from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import general, measurement, security from zigpy.zcl.clusters.general import OnOff diff --git a/tests/test_button.py b/tests/test_button.py index d8a4acca9..f955fec60 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -4,6 +4,8 @@ from unittest.mock import call, patch import pytest +from zhaquirks.builder import QuirkBuilder +from zhaquirks.clusters import CustomCluster from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, @@ -11,12 +13,12 @@ OUTPUT_CLUSTERS, PROFILE_ID, ) +from zhaquirks.device import CustomZigpyDevice +from zhaquirks.legacy import CustomDevice, DeviceRegistry from zhaquirks.tuya.tuya_valve import ParksideTuyaValveManufCluster import zigpy from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha -from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry -from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import general, security diff --git a/tests/test_climate.py b/tests/test_climate.py index 4fc3a1770..b61e56133 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -3,6 +3,7 @@ # pylint: disable=redefined-outer-name,too-many-lines import asyncio +from collections.abc import Iterator import logging from typing import Any from unittest.mock import AsyncMock, MagicMock, call, patch @@ -10,12 +11,11 @@ from freezegun import freeze_time import pytest +from zhaquirks.legacy import CustomDevice import zhaquirks.sinope.thermostat from zhaquirks.sinope.thermostat import SinopeTechnologiesThermostatCluster import zhaquirks.tuya.ts0601_trv import zigpy.profiles -import zigpy.quirks -from zigpy.quirks.v2 import QuirkBuilder import zigpy.zcl.clusters from zigpy.zcl.clusters.hvac import Thermostat import zigpy.zcl.foundation as zcl_f @@ -39,7 +39,7 @@ PRESET_TEMP_MANUAL, ) from zha.application.gateway import Gateway -from zha.quirks import resolve_zigpy_device +from zha.application.platforms import BaseEntity from zha.application.platforms.climate import ( HVAC_MODE_2_SYSTEM, SEQ_OF_OPERATION, @@ -55,6 +55,7 @@ ) from zha.const import STATE_CHANGED from zha.exceptions import ZHAException +from zha.quirks import DeviceMatch, ModelInfo, register_device, resolve_zigpy_device from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) @@ -222,7 +223,7 @@ async def device_climate_mock( endpoints: dict[int, dict[str, Any]], plug: dict[str, Any] | None = None, manuf: str | None = None, - quirk: type[zigpy.quirks.CustomDevice] | None = None, + quirk: type[CustomDevice] | None = None, ) -> Device: """Test regular thermostat device.""" @@ -1641,21 +1642,33 @@ async def test_thermostat_quirkv2_local_temperature_calibration_config_overwrite zigpy_device.node_desc.mac_capability_flags |= 0b_0000_0100 zigpy_device.endpoints[1].thermostat.PLUGGED_ATTR_READS = ZCL_ATTR_PLUG - ( - QuirkBuilder("unk_manufacturer", "FakeModel") - # Local temperature calibration. - .number( - Thermostat.AttributeDefs.local_temperature_calibration.name, - zigpy.zcl.clusters.hvac.Thermostat.cluster_id, - min_value=-5, - max_value=5, - step=0.1, - multiplier=0.1, - translation_key="local_temperature_calibration", - fallback_name="Local temperature offset", + @register_device + class FakeThermostatQuirk(Device): + _device_match = DeviceMatch( + applies_to=(ModelInfo("unk_manufacturer", "FakeModel"),) ) - .add_to_registry() - ) + + def discover_entities(self) -> Iterator[BaseEntity]: + yield from super().discover_entities() + + endpoint = self.endpoints[1] + cluster = endpoint.zigpy_endpoint.in_clusters[Thermostat.cluster_id] + calibration = Thermostat.AttributeDefs.local_temperature_calibration.name + + yield NumberConfigurationEntity( + endpoint=endpoint, + device=self, + cluster=cluster, + from_quirk=True, + attribute_name=calibration, + unique_id_suffix=calibration, + min_value=-5, + max_value=5, + step=0.1, + multiplier=0.1, + translation_key="local_temperature_calibration", + fallback_name="Local temperature offset", + ) zigpy_device = resolve_zigpy_device(zigpy_device) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) diff --git a/tests/test_device.py b/tests/test_device.py index 1c508a868..8556e6ccb 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -7,17 +7,15 @@ from unittest.mock import AsyncMock, call, patch import pytest -from zigpy.exceptions import ZigbeeException -import zigpy.profiles.zha -from zigpy.quirks.registry import DeviceRegistry -from zigpy.quirks.v2 import ( +from zhaquirks.builder import QuirkBuilder +from zhaquirks.builder.metadata import ( DeviceAlertLevel, DeviceAlertMetadata, ExposesFeatureMetadata, - QuirkBuilder, ) -from zigpy.quirks.v2.homeassistant import EntityType -from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass +from zhaquirks.legacy import DeviceRegistry +from zigpy.exceptions import ZigbeeException +import zigpy.profiles.zha import zigpy.types from zigpy.typing import UNDEFINED from zigpy.zcl import ClusterType @@ -39,7 +37,7 @@ join_zigpy_device, zigpy_device_from_json, ) -from zha.application import Platform +from zha.application import EntityType, Platform from zha.application.const import ( CLUSTER_COMMAND_SERVER, CLUSTER_COMMANDS_CLIENT, @@ -53,6 +51,10 @@ from zha.application.platforms.binary_sensor import IASZone from zha.application.platforms.light import Light from zha.application.platforms.sensor import LQISensor, RSSISensor +from zha.application.platforms.sensor.device_class import ( + SensorDeviceClass, + SensorStateClass, +) from zha.application.platforms.switch import Switch from zha.exceptions import ZHAException from zha.zigbee.device import ( diff --git a/tests/test_discover.py b/tests/test_discover.py index 2a5aba484..274abe82b 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -32,7 +32,6 @@ ) import zigpy.device import zigpy.profiles.zha -import zigpy.quirks import zigpy.types from zigpy.zcl import ClusterType import zigpy.zcl.clusters.closures diff --git a/tests/test_select.py b/tests/test_select.py index 0685d566c..8f23e49f1 100644 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -10,10 +10,12 @@ OUTPUT_CLUSTERS, PROFILE_ID, ) +from zhaquirks.builder import QuirkBuilder +from zhaquirks.clusters import CustomCluster +from zhaquirks.device import CustomZigpyDevice +from zhaquirks.legacy import CustomDevice, get_device from zigpy.const import SIG_EP_PROFILE from zigpy.profiles import zha -from zigpy.quirks import CustomCluster, CustomDevice, get_device -from zigpy.quirks.v2 import CustomZigpyDevice, QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl import foundation diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 2f1aadf44..338e1efff 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -8,20 +8,15 @@ from unittest.mock import MagicMock, call, patch import pytest +from zhaquirks.builder import QuirkBuilder +from zhaquirks.builder.metadata import ReportingConfig as QuirksReportingConfig +from zhaquirks.clusters import CustomCluster from zhaquirks.danfoss import thermostat as danfoss_thermostat +from zhaquirks.device import CustomZigpyDevice +from zhaquirks.legacy import DeviceRegistry, get_device from zigpy.device import Device as ZigpyDevice from zigpy.profiles import zha import zigpy.profiles.zha -from zigpy.quirks import CustomCluster, DeviceRegistry, get_device -from zigpy.quirks.v2 import ( - CustomZigpyDevice, - QuirkBuilder, - ReportingConfig as QuirksReportingConfig, -) -from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType, UnitOfMass -from zigpy.quirks.v2.homeassistant.sensor import ( - SensorDeviceClass as SensorDeviceClassV2, -) import zigpy.types as t from zigpy.zcl import Cluster, ReportingConfig from zigpy.zcl.clusters import general, homeautomation, hvac, measurement, smartenergy @@ -41,7 +36,7 @@ send_attributes_report, zigpy_device_from_json, ) -from zha.application import Platform +from zha.application import EntityPlatform, EntityType, Platform from zha.application.gateway import Gateway from zha.application.platforms import PlatformEntity, sensor from zha.application.platforms.sensor import ( @@ -50,11 +45,15 @@ Temperature, ) from zha.application.platforms.sensor.const import SensorDeviceClass, SensorStateClass +from zha.application.platforms.sensor.device_class import ( + SensorDeviceClass as SensorDeviceClassV2, +) from zha.application.platforms.sensor.helpers import resolution_to_decimal_precision from zha.units import ( PERCENTAGE, UnitOfElectricPotential, UnitOfEnergy, + UnitOfMass, UnitOfPressure, UnitOfVolume, ) diff --git a/tests/test_switch.py b/tests/test_switch.py index 1b497306f..62c527710 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -5,6 +5,8 @@ from unittest.mock import call, patch import pytest +from zhaquirks.builder import QuirkBuilder +from zhaquirks.clusters import CustomCluster from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, @@ -12,10 +14,9 @@ OUTPUT_CLUSTERS, PROFILE_ID, ) +from zhaquirks.legacy import CustomDevice from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha -from zigpy.quirks import CustomCluster, CustomDevice -from zigpy.quirks.v2 import QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import closures, general @@ -39,9 +40,9 @@ ) from zha.application import Platform from zha.application.gateway import Gateway -from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_zigpy_device from zha.application.platforms import GroupEntity, PlatformEntity from zha.exceptions import ZHAException +from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_zigpy_device from zha.zigbee.device import Device from zha.zigbee.group import Group, GroupMemberReference diff --git a/zha/quirks/__init__.py b/zha/quirks/__init__.py index c1e3b03e1..df1fe7bc5 100644 --- a/zha/quirks/__init__.py +++ b/zha/quirks/__init__.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections import defaultdict -from collections.abc import Callable +from collections.abc import Callable, Iterator +import contextlib from dataclasses import dataclass import inspect import logging @@ -201,6 +202,18 @@ def purge_custom_quirks(self, custom_quirks_root: Path) -> None: _LOGGER.debug("Removing stale custom quirk: %s", entry) entries.remove(entry) + @contextlib.contextmanager + def preserve_state(self) -> Iterator[None]: + """Snapshot the registry and restore it on exit.""" + saved = {key: list(entries) for key, entries in self._registry.items()} + saved_wildcard = list(self._wildcard_registry) + try: + yield + finally: + self._registry.clear() + self._registry.update(saved) + self._wildcard_registry[:] = saved_wildcard + DEVICE_REGISTRY = DeviceRegistry() From d39c1a8fdab534de1ac81691d797b88467ad5bcc Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:43:01 -0400 Subject: [PATCH 20/41] Migrate quirk IDs into ZHA --- .../platforms/binary_sensor/__init__.py | 2 +- zha/application/platforms/number/__init__.py | 2 +- zha/application/platforms/select.py | 14 +++++++------- zha/application/platforms/sensor/__init__.py | 2 +- zha/application/platforms/siren.py | 2 +- zha/application/platforms/switch.py | 2 +- zha/application/platforms/virtual.py | 2 +- zha/{quirks/__init__.py => quirks.py} | 10 ++++++++++ 8 files changed, 23 insertions(+), 13 deletions(-) rename zha/{quirks/__init__.py => quirks.py} (95%) diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index 35f9d658a..01f7e2196 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -9,7 +9,6 @@ import logging from typing import TYPE_CHECKING, Any -from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT from zigpy.profiles import zha, zll from zigpy.zcl import ( AttributeReadEvent, @@ -46,6 +45,7 @@ TUYA_MANUFACTURER_CLUSTER, ) from zha.application.platforms.helpers import validate_device_class +from zha.quirks import DANFOSS_ALLY_THERMOSTAT if TYPE_CHECKING: from zha.zigbee.device import Device diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index ef7e9cce6..5afe37360 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -8,7 +8,6 @@ import logging from typing import TYPE_CHECKING, Any -from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT from zigpy.zcl import ( AttributeReadEvent, AttributeReportedEvent, @@ -43,6 +42,7 @@ from zha.application.platforms.helpers import validate_device_class from zha.application.platforms.number.bacnet import BACNET_UNITS_TO_HA_UNITS from zha.application.platforms.number.const import ICONS, NumberDeviceClass, NumberMode +from zha.quirks import DANFOSS_ALLY_THERMOSTAT from zha.units import UnitOfMass, UnitOfTemperature, UnitOfTime if TYPE_CHECKING: diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index 893917cf5..14c271457 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -10,13 +10,6 @@ from typing import TYPE_CHECKING, Any, cast from zhaquirks.danfoss import thermostat as danfoss_thermostat -from zhaquirks.quirk_ids import ( - BEGA_LIGHT_SWITCHABLE_WHITE, - DANFOSS_ALLY_THERMOSTAT, - SIREN_BASIC, - TUYA_PLUG_MANUFACTURER, - TUYA_PLUG_ONOFF, -) from zhaquirks.xiaomi.aqara.magnet_ac01 import OppleCluster as MagnetAC01OppleCluster from zhaquirks.xiaomi.aqara.switch_acn047 import OppleCluster as T2RelayOppleCluster from zigpy import types @@ -57,6 +50,13 @@ TUYA_MANUFACTURER_CLUSTER, ) from zha.application.platforms.siren import AdvancedSiren +from zha.quirks import ( + BEGA_LIGHT_SWITCHABLE_WHITE, + DANFOSS_ALLY_THERMOSTAT, + SIREN_BASIC, + TUYA_PLUG_MANUFACTURER, + TUYA_PLUG_ONOFF, +) if TYPE_CHECKING: from zha.zigbee.device import Device diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 53f13ab12..74c6e3467 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -15,7 +15,6 @@ from typing import TYPE_CHECKING, Any, cast from zhaquirks.danfoss import thermostat as danfoss_thermostat -from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT, SE_POLL_SUMMATION from zigpy import types from zigpy.state import Counter, State from zigpy.zcl import ( @@ -107,6 +106,7 @@ ) from zha.application.platforms.virtual import VirtualEntity from zha.decorators import periodic +from zha.quirks import DANFOSS_ALLY_THERMOSTAT, SE_POLL_SUMMATION from zha.units import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, diff --git a/zha/application/platforms/siren.py b/zha/application/platforms/siren.py index 6075de551..4a3688681 100644 --- a/zha/application/platforms/siren.py +++ b/zha/application/platforms/siren.py @@ -10,7 +10,6 @@ import functools from typing import TYPE_CHECKING, Any, Final -from zhaquirks.quirk_ids import SIREN_BASIC from zigpy.profiles import zha from zigpy.zcl.clusters.security import ( IasWd, @@ -32,6 +31,7 @@ PlatformFeatureGroup, register_entity, ) +from zha.quirks import SIREN_BASIC if TYPE_CHECKING: from zha.zigbee.device import Device diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index 381a870f6..0ba5ae56f 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -8,7 +8,6 @@ import logging from typing import TYPE_CHECKING, Any -from zhaquirks.quirk_ids import DANFOSS_ALLY_THERMOSTAT, TUYA_PLUG_ONOFF from zigpy import types as t from zigpy.profiles import zha, zll import zigpy.zcl @@ -48,6 +47,7 @@ ) from zha.application.platforms.light.const import LIGHT_PROFILE_DEVICE_TYPES from zha.exceptions import ZHAException +from zha.quirks import DANFOSS_ALLY_THERMOSTAT, TUYA_PLUG_ONOFF from zha.zigbee.group import Group if TYPE_CHECKING: diff --git a/zha/application/platforms/virtual.py b/zha/application/platforms/virtual.py index 05c462014..321685dc9 100644 --- a/zha/application/platforms/virtual.py +++ b/zha/application/platforms/virtual.py @@ -12,7 +12,6 @@ import asyncio from typing import TYPE_CHECKING, Any -from zhaquirks.quirk_ids import TUYA_PLUG_MANUFACTURER import zigpy.exceptions import zigpy.types as t import zigpy.zcl @@ -52,6 +51,7 @@ TUYA_MANUFACTURER_CLUSTER, ) from zha.exceptions import ZHAException +from zha.quirks import TUYA_PLUG_MANUFACTURER from zha.zigbee.endpoint import cluster_event_unique_id, split_event_arg ATTRIBUTE_ID = "attribute_id" diff --git a/zha/quirks/__init__.py b/zha/quirks.py similarity index 95% rename from zha/quirks/__init__.py rename to zha/quirks.py index df1fe7bc5..f6e1f7c19 100644 --- a/zha/quirks/__init__.py +++ b/zha/quirks.py @@ -26,6 +26,16 @@ DEVICE_REGISTRY: DeviceRegistry +# Quirk IDs +KONKE_BUTTON = "konke.button_remote" +TUYA_PLUG_ONOFF = "tuya.plug_on_off_attributes" +TUYA_PLUG_MANUFACTURER = "tuya.plug_manufacturer_attributes" +XIAOMI_AQARA_VIBRATION_AQ1 = "xiaomi.aqara_vibration_aq1" +DANFOSS_ALLY_THERMOSTAT = "danfoss.ally_thermostat" +BEGA_LIGHT_SWITCHABLE_WHITE = "bega.light_switchable_white" +SE_POLL_SUMMATION = "se_poll_summation" +SIREN_BASIC = "siren_basic" + class ModelInfo(NamedTuple): """A (manufacturer, model) pair to match. `None` is a wildcard.""" From ede82a1ae531239269393b7481be1da0a12998ca Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:53:59 -0400 Subject: [PATCH 21/41] Allow iteration over the device registry --- zha/quirks.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zha/quirks.py b/zha/quirks.py index f6e1f7c19..1124534b8 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -191,6 +191,15 @@ def get(self, zigpy_device: zigpy.device.Device) -> QuirkRegistryEntry | None: return None + def __iter__(self) -> Iterator[QuirkRegistryEntry]: + """Yield every registered entry once (deduplicated across model keys).""" + seen: set[int] = set() + for entries in (*self._registry.values(), self._wildcard_registry): + for entry in entries: + if id(entry) not in seen: + seen.add(id(entry)) + yield entry + def remove(self, entry: QuirkRegistryEntry) -> None: """Remove a quirk entry from the registry.""" if not entry.device_match.applies_to: From 7e1b99772f1463301470332a675ef077d8fb2189 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:27:22 -0400 Subject: [PATCH 22/41] Typing --- tests/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common.py b/tests/common.py index ea86dbd5d..ba4702a66 100644 --- a/tests/common.py +++ b/tests/common.py @@ -19,9 +19,9 @@ import zigpy.zdo.types as zdo_t from zha.application import Platform -from zha.quirks import resolve_zigpy_device from zha.application.gateway import Gateway from zha.application.platforms import BaseEntity, GroupEntity, PlatformEntity +from zha.quirks import resolve_zigpy_device from zha.zigbee.device import Device from zha.zigbee.group import Group From 53db551ea1a64280726c71040400ab0d47824bdb Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:12:05 -0400 Subject: [PATCH 23/41] Clean up registry usage in tests --- tests/common.py | 8 +- .../data/devices/tz3000-tqlv4ug4-ts0001.json | 2 +- tests/test_binary_sensor.py | 8 +- tests/test_button.py | 23 ++-- tests/test_climate.py | 13 ++- tests/test_device.py | 44 ++++---- tests/test_discover.py | 63 +++++++---- tests/test_select.py | 8 ++ tests/test_sensor.py | 23 ++-- tests/test_siren.py | 2 +- tests/test_switch.py | 23 ++-- zha/application/gateway.py | 4 +- zha/quirks.py | 103 +++++++++--------- 13 files changed, 179 insertions(+), 145 deletions(-) diff --git a/tests/common.py b/tests/common.py index ba4702a66..1e36f9b88 100644 --- a/tests/common.py +++ b/tests/common.py @@ -21,7 +21,7 @@ from zha.application import Platform from zha.application.gateway import Gateway from zha.application.platforms import BaseEntity, GroupEntity, PlatformEntity -from zha.quirks import resolve_zigpy_device +from zha.quirks import DEVICE_REGISTRY, DeviceRegistry from zha.zigbee.device import Device from zha.zigbee.group import Group @@ -363,6 +363,7 @@ def zigpy_device_from_device_data( device_data: dict, patch_cluster: bool = True, quirk: Callable | None = None, + registry: DeviceRegistry = DEVICE_REGISTRY, ) -> zigpy.device.Device: """Make a fake device using the specified cluster classes.""" @@ -423,7 +424,7 @@ def zigpy_device_from_device_data( if quirk: device = quirk(app, device.ieee, device.nwk, device) else: - device = resolve_zigpy_device(device) + device = registry.resolve(device) for epid, ep in device_data["endpoints"].items(): try: @@ -557,6 +558,7 @@ def create_mock_zigpy_device( patch_cluster: bool = True, quirk: Callable | None = None, attributes: dict[int, dict[str, dict[str, Any]]] = None, + registry: DeviceRegistry = DEVICE_REGISTRY, ) -> zigpy.device.Device: """Make a fake device using the specified cluster classes.""" zigpy_app_controller = zha_gateway.application_controller @@ -601,7 +603,7 @@ def create_mock_zigpy_device( if quirk: device = quirk(zigpy_app_controller, device.ieee, device.nwk, device) else: - device = resolve_zigpy_device(device) + device = registry.resolve(device) if patch_cluster: for endpoint in (ep for epid, ep in device.endpoints.items() if epid): diff --git a/tests/data/devices/tz3000-tqlv4ug4-ts0001.json b/tests/data/devices/tz3000-tqlv4ug4-ts0001.json index c9a2071bc..b6dafd8c2 100644 --- a/tests/data/devices/tz3000-tqlv4ug4-ts0001.json +++ b/tests/data/devices/tz3000-tqlv4ug4-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_tqlv4ug4 TS0001", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_1G_Metering", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_1G_Metering", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 925cede58..fbabc3713 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -8,7 +8,6 @@ import pytest from zhaquirks.builder import QuirkBuilder from zhaquirks.device import CustomZigpyDevice -from zhaquirks.legacy import DeviceRegistry from zigpy.profiles import zha import zigpy.profiles.zha from zigpy.typing import UNDEFINED @@ -39,6 +38,7 @@ Occupancy, ) from zha.application.platforms.const import SMARTTHINGS_ACCELERATION_CLUSTER +from zha.quirks import DeviceRegistry DEVICE_IAS = { 1: { @@ -275,7 +275,7 @@ async def test_quirks_binary_sensor_attr_converter(zha_gateway: Gateway) -> None ) ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .binary_sensor( OnOff.AttributeDefs.on_off.name, OnOff.cluster_id, @@ -283,10 +283,10 @@ async def test_quirks_binary_sensor_attr_converter(zha_gateway: Gateway) -> None fallback_name="On/off", attribute_converter=lambda x: not bool(x), # invert value with lambda ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = registry.get_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) assert isinstance(zigpy_device_, CustomZigpyDevice) cluster = zigpy_device_.endpoints[1].on_off diff --git a/tests/test_button.py b/tests/test_button.py index f955fec60..a1b1e543b 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -14,7 +14,7 @@ PROFILE_ID, ) from zhaquirks.device import CustomZigpyDevice -from zhaquirks.legacy import CustomDevice, DeviceRegistry +from zhaquirks.legacy import CustomDevice from zhaquirks.tuya.tuya_valve import ParksideTuyaValveManufCluster import zigpy from zigpy.exceptions import ZigbeeException @@ -46,6 +46,7 @@ WriteAttributeButton, ) from zha.application.platforms.button.const import ButtonDeviceClass +from zha.quirks import DeviceRegistry from zha.zigbee.device import Device ZIGPY_DEVICE = { @@ -213,7 +214,7 @@ async def custom_button_device(zha_gateway: Gateway): ) ( - QuirkBuilder("Fake_Model", "Fake_Manufacturer", registry=registry) + QuirkBuilder("Fake_Model", "Fake_Manufacturer") .replaces(FakeManufacturerCluster) .command_button( FakeManufacturerCluster.ServerCommandDefs.self_test.name, @@ -229,10 +230,10 @@ async def custom_button_device(zha_gateway: Gateway): translation_key="feed", fallback_name="Feed", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device = registry.get_device(zigpy_device) + zigpy_device = registry.resolve(zigpy_device) assert isinstance(zigpy_device, CustomZigpyDevice) # XXX: this should be handled automatically, patch quirks added cluster @@ -315,7 +316,7 @@ async def test_quirks_write_attr_buttons_uid(zha_gateway: Gateway) -> None: ) ( - QuirkBuilder("Fake_Model", "Fake_Manufacturer", registry=registry) + QuirkBuilder("Fake_Model", "Fake_Manufacturer") .replaces(FakeManufacturerCluster) .write_attr_button( FakeManufacturerCluster.AttributeDefs.feed.name, @@ -333,10 +334,10 @@ async def test_quirks_write_attr_buttons_uid(zha_gateway: Gateway) -> None: translation_key="btn_2", fallback_name="Button 2", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = registry.get_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) assert isinstance(zigpy_device_, CustomZigpyDevice) zha_device = await join_zigpy_device(zha_gateway, zigpy_device_) @@ -378,7 +379,7 @@ async def test_quirks_v2_button_only_cluster_is_not_configured( registry = DeviceRegistry() ( QuirkBuilder( - "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2", registry=registry + "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2" ) .replaces(OppleCluster) .command_button( @@ -388,7 +389,7 @@ async def test_quirks_v2_button_only_cluster_is_not_configured( translation_key="self_test", fallback_name="Self test", ) - .add_to_registry() + .add_to_registry(registry) ) zigpy_device = create_mock_zigpy_device( @@ -407,8 +408,8 @@ async def test_quirks_v2_button_only_cluster_is_not_configured( manufacturer="Fake_Manufacturer_sensor_2", model="Fake_Model_sensor_2", ) - zigpy_device = registry.get_device(zigpy_device) - # `registry.get_device` swaps in the replaced OppleCluster instance; patch its + zigpy_device = registry.resolve(zigpy_device) + # `registry.resolve` swaps in the replaced OppleCluster instance; patch its # network methods so we can assert bind/reporting/reading didn't happen. opple_cluster = zigpy_device.endpoints[1].opple_cluster patch_cluster_for_testing(opple_cluster) diff --git a/tests/test_climate.py b/tests/test_climate.py index b61e56133..3109c298c 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -55,7 +55,7 @@ ) from zha.const import STATE_CHANGED from zha.exceptions import ZHAException -from zha.quirks import DeviceMatch, ModelInfo, register_device, resolve_zigpy_device +from zha.quirks import DeviceMatch, DeviceRegistry, ModelInfo from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) @@ -1636,13 +1636,18 @@ async def test_thermostat_quirkv2_local_temperature_calibration_config_overwrite ) -> None: """Test that a quirk v2 local temperature calibration config overwrites the default one.""" + registry = DeviceRegistry() zigpy_device = create_mock_zigpy_device( - zha_gateway, CLIMATE, manufacturer="unk_manufacturer", model="FakeModel" + zha_gateway, + CLIMATE, + manufacturer="unk_manufacturer", + model="FakeModel", + registry=registry, ) zigpy_device.node_desc.mac_capability_flags |= 0b_0000_0100 zigpy_device.endpoints[1].thermostat.PLUGGED_ATTR_READS = ZCL_ATTR_PLUG - @register_device + @registry.register_device class FakeThermostatQuirk(Device): _device_match = DeviceMatch( applies_to=(ModelInfo("unk_manufacturer", "FakeModel"),) @@ -1670,7 +1675,7 @@ def discover_entities(self) -> Iterator[BaseEntity]: fallback_name="Local temperature offset", ) - zigpy_device = resolve_zigpy_device(zigpy_device) + zigpy_device = registry.resolve(zigpy_device) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert zha_device.model == "FakeModel" diff --git a/tests/test_device.py b/tests/test_device.py index 8556e6ccb..9946d978d 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -13,7 +13,6 @@ DeviceAlertMetadata, ExposesFeatureMetadata, ) -from zhaquirks.legacy import DeviceRegistry from zigpy.exceptions import ZigbeeException import zigpy.profiles.zha import zigpy.types @@ -57,6 +56,7 @@ ) from zha.application.platforms.switch import Switch from zha.exceptions import ZHAException +from zha.quirks import DeviceRegistry from zha.zigbee.device import ( ClusterBinding, Device, @@ -838,12 +838,12 @@ async def test_quirks_v2_device_renaming(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .friendly_name(manufacturer="Lowe's", model="IRIS Keypad V2") - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -870,12 +870,12 @@ async def test_quirks_v2_device_alerts(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .device_alert(level=DeviceAlertLevel.WARNING, message="Test warning") - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -953,7 +953,7 @@ async def test_quirks_v2_primary_entity(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .sensor( attribute_name=PowerConfiguration.AttributeDefs.battery_quantity.id, cluster_id=PowerConfiguration.cluster_id, @@ -961,10 +961,10 @@ async def test_quirks_v2_primary_entity(zha_gateway: Gateway) -> None: fallback_name="Battery quantity", primary=True, ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -982,7 +982,7 @@ async def test_quirks_v2_prevent_default_entities(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .prevent_default_entity_creation(endpoint_id=123) .prevent_default_entity_creation(cluster_id=0x4567) .prevent_default_entity_creation(unique_id_suffix="_something") @@ -990,10 +990,10 @@ async def test_quirks_v2_prevent_default_entities(zha_gateway: Gateway) -> None: .prevent_default_entity_creation( function=lambda entity: entity.__class__.__name__ == "IdentifyButton" ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -1023,7 +1023,7 @@ def filter_func(entity) -> bool: return entity.__class__.__name__ == "LQISensor" ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .change_entity_metadata( endpoint_id=1, unique_id_suffix="-lqi", @@ -1062,10 +1062,10 @@ def filter_func(entity) -> bool: unique_id_suffix="generated_battery_voltage", new_translation_key="changed_via_cluster_id", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -1109,7 +1109,7 @@ async def test_quirks_v2_translation_placeholders(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .sensor( PowerConfiguration.AttributeDefs.battery_voltage.name, PowerConfiguration.cluster_id, @@ -1117,10 +1117,10 @@ async def test_quirks_v2_translation_placeholders(zha_gateway: Gateway) -> None: translation_placeholders={"sensor_index": "1"}, fallback_name="Some battery sensor {sensor_index}", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", @@ -1145,13 +1145,13 @@ async def test_quirks_v2_exposed_features(zha_gateway: Gateway) -> None: registry = DeviceRegistry() ( - QuirkBuilder("CentraLite", "3405-L", registry=registry) + QuirkBuilder("CentraLite", "3405-L") .exposes_feature("some_feature") .exposes_feature("another_feature", config={"option": True}) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device( + zigpy_dev = registry.resolve( await zigpy_device_from_json( zha_gateway.application_controller, "tests/data/devices/centralite-3405-l.json", diff --git a/tests/test_discover.py b/tests/test_discover.py index 274abe82b..9b8591ad0 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -4,6 +4,7 @@ from collections import defaultdict from collections.abc import Callable import contextlib +import dataclasses import enum import json import pathlib @@ -61,11 +62,10 @@ from zha.application.platforms.light import HueLight from zha.application.platforms.number import BaseNumber, NumberMode from zha.quirks import ( - DEVICE_REGISTRY as QUIRKS_DEVICE_REGISTRY, QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch, + DeviceRegistry, ModelInfo, - resolve_zigpy_device, ) from zha.units import UnitOfTime @@ -207,6 +207,7 @@ async def test_quirks_v2_entity_discovery( ) -> None: """Test quirks v2 discovery.""" + registry = DeviceRegistry() zigpy_device = create_mock_zigpy_device( zha_gateway, { @@ -226,6 +227,7 @@ async def test_quirks_v2_entity_discovery( ieee="01:2d:6f:00:0a:90:69:e8", manufacturer="Ikea of Sweden", model="TRADFRI remote control", + registry=registry, ) ( @@ -244,10 +246,10 @@ async def test_quirks_v2_entity_discovery( translation_key="off_wait_time", fallback_name="Off wait time", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device = resolve_zigpy_device(zigpy_device) + zigpy_device = registry.resolve(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -288,6 +290,7 @@ class FakeXiaomiAqaraDriverE1(XiaomiAqaraDriverE1): } ) + registry = DeviceRegistry() ( QuirkBuilder("LUMI", "lumi.curtain.agl006") .adds(LocalIlluminanceMeasurementCluster) @@ -320,7 +323,7 @@ class FakeXiaomiAqaraDriverE1(XiaomiAqaraDriverE1): translation_key="error_detected", fallback_name="Error detected", ) - .add_to_registry() + .add_to_registry(registry) ) aqara_E1_device = create_mock_zigpy_device( @@ -348,8 +351,9 @@ class FakeXiaomiAqaraDriverE1(XiaomiAqaraDriverE1): ieee="01:2d:6f:00:0a:90:69:e8", manufacturer="LUMI", model="lumi.curtain.agl006", + registry=registry, ) - aqara_E1_device = resolve_zigpy_device(aqara_E1_device) + aqara_E1_device = registry.resolve(aqara_E1_device) aqara_E1_device.endpoints[1].opple_cluster.PLUGGED_ATTR_READS = { "hand_open": 0, @@ -412,6 +416,7 @@ def _get_test_device( model: str, augment_method: Callable[[QuirkBuilder], QuirkBuilder] | None = None, ): + registry = DeviceRegistry() zigpy_device = create_mock_zigpy_device( zha_gateway, { @@ -431,6 +436,7 @@ def _get_test_device( ieee="01:2d:6f:00:0a:90:69:e8", manufacturer=manufacturer, model=model, + registry=registry, ) quirk_builder = ( @@ -472,9 +478,9 @@ def _get_test_device( if augment_method: quirk_builder = augment_method(quirk_builder) - quirk_builder.add_to_registry() + quirk_builder.add_to_registry(registry) - zigpy_device = resolve_zigpy_device(zigpy_device) + zigpy_device = registry.resolve(zigpy_device) zigpy_device.endpoints[1].power.PLUGGED_ATTR_READS = { "battery_voltage": 3, "battery_percentage_remaining": 100, @@ -496,11 +502,16 @@ async def test_quirks_v2_entity_no_metadata( zigpy_device = _get_test_device( zha_gateway, "Ikea of Sweden2", "TRADFRI remote control2" ) - factory_kwargs = getattr( - zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR - ).zha_device_factory.keywords - factory_kwargs["quirk_definition"] = attrs.evolve( - factory_kwargs["quirk_definition"], entity_metadata=() + entry = getattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR) + factory = entry.zha_device_factory + new_factory = dataclasses.replace( + factory, + quirk_definition=attrs.evolve(factory.quirk_definition, entity_metadata=()), + ) + setattr( + zigpy_device, + QUIRK_REGISTRY_ENTRY_ATTR, + dataclasses.replace(entry, zha_device_factory=new_factory), ) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) assert ( @@ -526,16 +537,23 @@ class UnknownEntityMetadata: cluster_type = ClusterType.Server entity_platform = Platform.UPDATE - factory_kwargs = getattr( - zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR - ).zha_device_factory.keywords - factory_kwargs["quirk_definition"] = attrs.evolve( - factory_kwargs["quirk_definition"], - entity_metadata=( - *factory_kwargs["quirk_definition"].entity_metadata, - UnknownEntityMetadata(), + entry = getattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR) + factory = entry.zha_device_factory + new_factory = dataclasses.replace( + factory, + quirk_definition=attrs.evolve( + factory.quirk_definition, + entity_metadata=( + *factory.quirk_definition.entity_metadata, + UnknownEntityMetadata(), + ), ), ) + setattr( + zigpy_device, + QUIRK_REGISTRY_ENTRY_ATTR, + dataclasses.replace(entry, zha_device_factory=new_factory), + ) zha_device = await join_zigpy_device(zha_gateway, zigpy_device) @@ -650,9 +668,6 @@ async def test_quirks_v2_metadata_bad_device_classes( assert expected_exception_string in caplog.text - # remove the quirk so we don't pollute the rest of the tests - QUIRKS_DEVICE_REGISTRY.remove(getattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR)) - async def test_quirks_v2_fallback_name(zha_gateway: Gateway) -> None: """Test quirks v2 fallback name.""" diff --git a/tests/test_select.py b/tests/test_select.py index 8f23e49f1..e141f50b3 100644 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -188,6 +188,14 @@ async def test_on_off_select_attribute_report_v2( }, manufacturer="Fake_Manufacturer", model="Fake_Model", + attributes={ + 1: { + "opple_cluster": { + "motion_sensitivity": AqaraMotionSensitivities.Medium, + "motion_sensitivity_disabled": AqaraMotionSensitivities.Medium, + } + } + }, ) zigpy_device = get_device(zigpy_device) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 338e1efff..ca743e7e3 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -13,7 +13,7 @@ from zhaquirks.clusters import CustomCluster from zhaquirks.danfoss import thermostat as danfoss_thermostat from zhaquirks.device import CustomZigpyDevice -from zhaquirks.legacy import DeviceRegistry, get_device +from zhaquirks.legacy import get_device from zigpy.device import Device as ZigpyDevice from zigpy.profiles import zha import zigpy.profiles.zha @@ -49,6 +49,7 @@ SensorDeviceClass as SensorDeviceClassV2, ) from zha.application.platforms.sensor.helpers import resolution_to_decimal_precision +from zha.quirks import DeviceRegistry from zha.units import ( PERCENTAGE, UnitOfElectricPotential, @@ -1645,7 +1646,7 @@ async def test_quirks_v2_sensor_attribute_init_reads_cluster( registry = DeviceRegistry() ( QuirkBuilder( - "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2", registry=registry + "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2" ) .replaces(OppleCluster) .sensor( @@ -1654,7 +1655,7 @@ async def test_quirks_v2_sensor_attribute_init_reads_cluster( translation_key="last_feeding_size", fallback_name="Last feeding size", ) - .add_to_registry() + .add_to_registry(registry) ) zigpy_device = create_mock_zigpy_device( @@ -1673,8 +1674,8 @@ async def test_quirks_v2_sensor_attribute_init_reads_cluster( manufacturer="Fake_Manufacturer_sensor_2", model="Fake_Model_sensor_2", ) - zigpy_device = registry.get_device(zigpy_device) - # `registry.get_device` swaps in the replaced OppleCluster instance; patch its + zigpy_device = registry.resolve(zigpy_device) + # `registry.resolve` swaps in the replaced OppleCluster instance; patch its # network methods so we can assert bind/reporting/reading behavior. opple_cluster = zigpy_device.endpoints[1].opple_cluster patch_cluster_for_testing(opple_cluster) @@ -1906,7 +1907,7 @@ async def test_quirks_sensor_attr_converter(zha_gateway: Gateway) -> None: ) ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .sensor( AnalogInput.AttributeDefs.present_value.name, AnalogInput.cluster_id, @@ -1914,10 +1915,10 @@ async def test_quirks_sensor_attr_converter(zha_gateway: Gateway) -> None: fallback_name="Quirks sensor", attribute_converter=lambda x: x + 100, ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = registry.get_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) assert isinstance(zigpy_device_, CustomZigpyDevice) cluster = zigpy_device_.endpoints[1].analog_input @@ -2084,7 +2085,7 @@ async def test_enum_sensor(zha_gateway: Gateway) -> None: ) ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .enum( entity_platform=EntityPlatform.SENSOR, entity_type=EntityType.DIAGNOSTIC, @@ -2095,10 +2096,10 @@ async def test_enum_sensor(zha_gateway: Gateway) -> None: translation_key="battery_size", fallback_name="Battery size", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_dev = registry.get_device(zigpy_dev) + zigpy_dev = registry.resolve(zigpy_dev) zha_device = await join_zigpy_device(zha_gateway, zigpy_dev) entity = get_entity(zha_device, platform=Platform.SENSOR, qualifier="battery_size") diff --git a/tests/test_siren.py b/tests/test_siren.py index 19f8c2e82..997f28d41 100644 --- a/tests/test_siren.py +++ b/tests/test_siren.py @@ -3,7 +3,6 @@ import asyncio from unittest.mock import patch -from zhaquirks.quirk_ids import SIREN_BASIC from zigpy.const import SIG_EP_PROFILE from zigpy.profiles import zha from zigpy.zcl.clusters import general, security @@ -21,6 +20,7 @@ from zha.application import Platform from zha.application.gateway import Gateway from zha.application.platforms.siren import SirenEntityFeature +from zha.quirks import SIREN_BASIC from zha.zigbee.device import Device diff --git a/tests/test_switch.py b/tests/test_switch.py index 62c527710..9c746eb60 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -42,7 +42,7 @@ from zha.application.gateway import Gateway from zha.application.platforms import GroupEntity, PlatformEntity from zha.exceptions import ZHAException -from zha.quirks import ZHA_DEVICE_CLASS_ATTRIBUTE, resolve_zigpy_device +from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceRegistry from zha.zigbee.device import Device from zha.zigbee.group import Group, GroupMemberReference @@ -497,6 +497,7 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> model="model", ) + registry = DeviceRegistry() ( QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) @@ -508,12 +509,12 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> translation_key="window_detection_function", fallback_name="Window detection function", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = resolve_zigpy_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) - assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None + assert getattr(zigpy_device_, QUIRK_REGISTRY_ENTRY_ATTR, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = {"window_detection_function": 5} update_attribute_cache(cluster) @@ -577,6 +578,7 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( model="model2", ) + registry = DeviceRegistry() ( QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) @@ -589,12 +591,12 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( translation_key="window_detection_function", fallback_name="Window detection function", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = resolve_zigpy_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) - assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None + assert getattr(zigpy_device_, QUIRK_REGISTRY_ENTRY_ATTR, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = {"window_detection_function": 5} update_attribute_cache(cluster) @@ -658,6 +660,7 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( model="model3", ) + registry = DeviceRegistry() ( QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) @@ -670,12 +673,12 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( translation_key="window_detection_function", fallback_name="Window detection function", ) - .add_to_registry() + .add_to_registry(registry) ) - zigpy_device_ = resolve_zigpy_device(zigpy_dev) + zigpy_device_ = registry.resolve(zigpy_dev) - assert getattr(zigpy_device_, ZHA_DEVICE_CLASS_ATTRIBUTE, None) is not None + assert getattr(zigpy_device_, QUIRK_REGISTRY_ENTRY_ATTR, None) is not None cluster = zigpy_device_.endpoints[1].tuya_manufacturer cluster.PLUGGED_ATTR_READS = { "window_detection_function": 5, diff --git a/zha/application/gateway.py b/zha/application/gateway.py index a7f7e368a..66b8c6731 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -53,7 +53,7 @@ gather_with_limited_concurrency, ) from zha.event import EventBase -from zha.quirks import resolve_zigpy_device +from zha.quirks import DEVICE_REGISTRY from zha.zigbee.device import Device, DeviceInfo, DeviceStatus, ExtendedDeviceInfo from zha.zigbee.group import Group, GroupInfo, GroupMemberReference @@ -249,7 +249,7 @@ async def _async_initialize(self) -> None: config=app_config, auto_form=False, start_radio=False, - device_resolver=resolve_zigpy_device, + device_resolver=DEVICE_REGISTRY.resolve, uninitialized_packet_handler=( self.config.config.quirks_configuration.uninitialized_packet_handler ), diff --git a/zha/quirks.py b/zha/quirks.py index 1124534b8..e57ad61a4 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -174,7 +174,30 @@ def register(self, entry: QuirkRegistryEntry) -> QuirkRegistryEntry: return entry - def get(self, zigpy_device: zigpy.device.Device) -> QuirkRegistryEntry | None: + def register_device(self, cls: type[Device]) -> type[Device]: + """Register a hand-written `Device` subclass as a quirk in this registry.""" + if cls._device_match is None: + raise ValueError(f"{cls!r} does not define `_device_match`") + + transforms: list[Callable[[zigpy.device.Device], zigpy.device.Device]] = [] + if cls._zigpy_device_cls is not None: + transforms.append(make_zigpy_device_replacement(cls._zigpy_device_cls)) + transforms.extend(cls._zigpy_device_transforms) + + self.register( + QuirkRegistryEntry( + device_match=cls._device_match, + zigpy_transforms=tuple(transforms), + zha_device_factory=cls, + source=QuirkSource.from_class(cls), + ) + ) + + return cls + + def match_entry( + self, zigpy_device: zigpy.device.Device + ) -> QuirkRegistryEntry | None: """Return the first registered entry matching `zigpy_device`.""" for key in ( ModelInfo(zigpy_device.manufacturer, zigpy_device.model), @@ -191,6 +214,33 @@ def get(self, zigpy_device: zigpy.device.Device) -> QuirkRegistryEntry | None: return None + def resolve(self, zigpy_device: zigpy.device.Device) -> zigpy.device.Device: + """Apply the quirk transforms registered for `zigpy_device` and return the result.""" + + # Resolution is idempotent: an already-quirked device is returned as-is + if hasattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR): + return zigpy_device + + entry = self.match_entry(zigpy_device) + if entry is None: + return zigpy_device + + _LOGGER.debug( + "Resolved %s/%s (%s) to quirk %s", + zigpy_device.manufacturer, + zigpy_device.model, + zigpy_device.ieee, + entry, + ) + + resolved_device = zigpy_device + for transform in entry.zigpy_transforms: + resolved_device = transform(resolved_device) + + setattr(resolved_device, QUIRK_REGISTRY_ENTRY_ATTR, entry) + + return resolved_device + def __iter__(self) -> Iterator[QuirkRegistryEntry]: """Yield every registered entry once (deduplicated across model keys).""" seen: set[int] = set() @@ -235,54 +285,3 @@ def preserve_state(self) -> Iterator[None]: DEVICE_REGISTRY = DeviceRegistry() - - -def register_device(cls: type[Device]) -> type[Device]: - """Class decorator registering a hand-written `Device` subclass as a quirk.""" - if cls._device_match is None: - raise ValueError(f"{cls!r} does not define `_device_match`") - - transforms: list[Callable[[zigpy.device.Device], zigpy.device.Device]] = [] - if cls._zigpy_device_cls is not None: - transforms.append(make_zigpy_device_replacement(cls._zigpy_device_cls)) - transforms.extend(cls._zigpy_device_transforms) - - DEVICE_REGISTRY.register( - QuirkRegistryEntry( - device_match=cls._device_match, - zigpy_transforms=tuple(transforms), - zha_device_factory=cls, - source=QuirkSource.from_class(cls), - ) - ) - - return cls - - -def resolve_zigpy_device(zigpy_device: zigpy.device.Device) -> zigpy.device.Device: - """Provide zigpy a way to resolve a bare ZCL-compliant device to its final form.""" - - # Resolution is idempotent: an already-quirked device is returned as-is - if hasattr(zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR): - return zigpy_device - - entry = DEVICE_REGISTRY.get(zigpy_device) - if entry is None: - return zigpy_device - - _LOGGER.debug( - "Resolved %s/%s (%s) to quirk %s", - zigpy_device.manufacturer, - zigpy_device.model, - zigpy_device.ieee, - entry, - ) - - resolved_device = zigpy_device - - for transform in entry.zigpy_transforms: - resolved_device = transform(resolved_device) - - # Sneak the registry entry in with the device so ZHA can use it - setattr(resolved_device, QUIRK_REGISTRY_ENTRY_ATTR, entry) - return resolved_device From 06914baaff5a96bd09b6da74cb797e10258f6cff Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:13:16 -0400 Subject: [PATCH 24/41] Properly rebuild devices on reconfigure --- tests/test_gateway.py | 78 ++++++++++++++++++++++++++++++++++++++ zha/application/gateway.py | 32 ++++++++++++---- zha/zigbee/device.py | 6 +-- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/tests/test_gateway.py b/tests/test_gateway.py index f54558565..423939365 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, PropertyMock, call, patch import pytest +from zhaquirks.builder import QuirkBuilder from zigpy.application import ControllerApplication from zigpy.config import CONF_NWK, CONF_NWK_COUNTRY_CODE from zigpy.profiles import zha @@ -39,6 +40,7 @@ from zha.application.helpers import ZHAData from zha.application.platforms import GroupEntity from zha.application.platforms.light.const import EFFECT_OFF, LightEntityFeature +from zha.quirks import DeviceRegistry from zha.zigbee.device import Device from zha.zigbee.group import Group, GroupMemberReference @@ -645,6 +647,82 @@ async def stalled_configure() -> None: assert zigpy_dev.ieee not in zha_gateway._device_init_tasks +async def test_reinterview_replaces_device_when_quirk_changes( + zha_gateway: Gateway, +) -> None: + """A re-interview resolving a different quirk replaces the device object.""" + registry = DeviceRegistry() + ( + QuirkBuilder("Fake_Manufacturer", "Model_A") + .friendly_name(model="Quirk A", manufacturer="Fake_Manufacturer") + .add_to_registry(registry) + ) + ( + QuirkBuilder("Fake_Manufacturer", "Model_B") + .friendly_name(model="Quirk B", manufacturer="Fake_Manufacturer") + .add_to_registry(registry) + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_A", + registry=registry, + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + assert zha_device.quirk_applied + assert zha_device.model == "Quirk A" + + reinterviewed = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_B", + registry=registry, + ) + zha_gateway.device_reinterviewed(reinterviewed) + await zha_gateway.async_block_till_done() + + new_zha_device = zha_gateway.get_device(zigpy_device.ieee) + assert new_zha_device is not zha_device + assert new_zha_device.quirk_applied + assert new_zha_device.model == "Quirk B" + + +async def test_reinterview_rebuilds_in_place_when_quirk_unchanged( + zha_gateway: Gateway, +) -> None: + """A re-interview resolving the same quirk rebuilds in place, preserving identity.""" + registry = DeviceRegistry() + ( + QuirkBuilder("Fake_Manufacturer", "Model_A") + .friendly_name(model="Quirk A", manufacturer="Fake_Manufacturer") + .add_to_registry(registry) + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_A", + registry=registry, + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + + reinterviewed = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_A", + registry=registry, + ) + zha_gateway.device_reinterviewed(reinterviewed) + await zha_gateway.async_block_till_done() + + assert zha_gateway.get_device(zigpy_device.ieee) is zha_device + + def test_gateway_raw_device_initialized( zha_gateway: Gateway, ) -> None: diff --git a/zha/application/gateway.py b/zha/application/gateway.py index 66b8c6731..0a557fa8c 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -53,7 +53,7 @@ gather_with_limited_concurrency, ) from zha.event import EventBase -from zha.quirks import DEVICE_REGISTRY +from zha.quirks import DEVICE_REGISTRY, QUIRK_REGISTRY_ENTRY_ATTR from zha.zigbee.device import Device, DeviceInfo, DeviceStatus, ExtendedDeviceInfo from zha.zigbee.group import Group, GroupInfo, GroupMemberReference @@ -488,13 +488,31 @@ async def _async_device_reinterviewed( ) return - _LOGGER.debug( - "Rebuilding device %s:%s after reinterview", - new_zigpy_device.nwk, - new_zigpy_device.ieee, - ) + old_entry = getattr(zha_device.device, QUIRK_REGISTRY_ENTRY_ATTR, None) + new_entry = getattr(new_zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR, None) + + if old_entry is new_entry: + _LOGGER.debug( + "Rebuilding device %s:%s after reinterview", + new_zigpy_device.nwk, + new_zigpy_device.ieee, + ) + await zha_device.async_rebuild_from_zigpy_device(new_zigpy_device) + else: + # A different quirk now matches. Replace the object with one dispatched by + # `Device.new` against the freshly-resolved registry entry. + _LOGGER.debug( + "Replacing device %s:%s after reinterview: resolved quirk changed", + new_zigpy_device.nwk, + new_zigpy_device.ieee, + ) + await zha_device.async_teardown(emit_entity_events=True) + + zha_device = Device.new(new_zigpy_device, self) + self._devices[new_zigpy_device.ieee] = zha_device - await zha_device.async_rebuild_from_zigpy_device(new_zigpy_device) + zha_device.available = True + zha_device.on_network = True configure_succeeded = False all_succeeded = False diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 02102f339..51cce76c0 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -1033,7 +1033,7 @@ async def async_rebuild_from_zigpy_device( underlying zigpy device. Emits entity removal events so listeners (e.g. HA) can clean up stale entities. """ - await self._async_teardown(emit_entity_events=True) + await self.async_teardown(emit_entity_events=True) self._init_from_zigpy_device(zigpy_device) def emit_reconfigure_done(self) -> None: @@ -1240,7 +1240,7 @@ def entity_update_listener(event: EntityStateChangedEvent) -> None: self.status = DeviceStatus.INITIALIZED self.debug("completed initialization") - async def _async_teardown(self, *, emit_entity_events: bool) -> None: + async def async_teardown(self, *, emit_entity_events: bool) -> None: """Tear down handlers, entities, and endpoints. Args: @@ -1290,7 +1290,7 @@ async def _async_teardown(self, *, emit_entity_events: bool) -> None: async def on_remove(self) -> None: """Cancel tasks this device owns (shutdown path).""" - await self._async_teardown(emit_entity_events=False) + await self.async_teardown(emit_entity_events=False) def async_get_clusters(self) -> dict[int, dict[str, dict[int, Cluster]]]: """Get all clusters for this device.""" From dcde9081f233ecff346abf87cc7d51d6be9596ec Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:09:25 -0400 Subject: [PATCH 25/41] Regenerate diagnostics --- tests/data/devices/adeo-sin-4-fp-21-equ.json | 2 +- .../data/devices/aqara-lumi-light-acn132.json | 2 +- .../data/devices/aqara-lumi-motion-ac01.json | 2 +- .../aqara-lumi-sensor-occupy-agl1.json | 2 +- .../devices/aqara-lumi-switch-acn047.json | 2 +- ...rink-leuchten-kg-smart-dimmable-light.json | 2 +- tests/data/devices/bosch-rbsh-mms-zb-eu.json | 2 +- .../devices/bosch-rbsh-rth0-bat-zb-eu.json | 2 +- tests/data/devices/bosch-rbsh-rth0-zb-eu.json | 2 +- tests/data/devices/bosch-rbsh-trv0-zb-eu.json | 2 +- tests/data/devices/candeo-c-zb-lc20-dim.json | 2 +- tests/data/devices/candeo-c-zb-lc20-rgb.json | 2 +- tests/data/devices/centralite-3310-g.json | 2 +- tests/data/devices/centralite-3315-seu.json | 2 +- tests/data/devices/centralite-3320-l.json | 2 +- tests/data/devices/centralite-3326-l.json | 2 +- tests/data/devices/d5x84yu-et093wrg.json | 2 +- tests/data/devices/danfoss-etrv0103.json | 2 +- tests/data/devices/digi-xbee3.json | 2 +- .../devices/ecodim-bv-eco-dim-05-zigbee.json | 2 +- .../devices/ecodim-bv-ecodim-zigbee-3-0.json | 2 +- tests/data/devices/ecolink-4655bc0-r.json | 2 +- .../ewelink-ck-tlsr8656-ss5-01-7000.json | 2 +- tests/data/devices/ewelink-snzb-01p.json | 2 +- tests/data/devices/ewelink-snzb-04p.json | 2 +- tests/data/devices/ewelink-wb01.json | 2 +- tests/data/devices/frient-a-s-aqszb-110.json | 2 +- tests/data/devices/frient-a-s-emizb-141.json | 2 +- tests/data/devices/frient-a-s-emizb-151.json | 35 +- tests/data/devices/frient-a-s-flszb-110.json | 2 +- tests/data/devices/frient-a-s-hmszb-120.json | 2 +- tests/data/devices/frient-a-s-iomzb-110.json | 2 +- tests/data/devices/frient-a-s-kepzb-110.json | 2 +- tests/data/devices/frient-a-s-moszb-140.json | 2 +- tests/data/devices/frient-a-s-moszb-153.json | 2 +- tests/data/devices/frient-a-s-sbtzb-110.json | 2 +- tests/data/devices/frient-a-s-scazb-141.json | 2 +- tests/data/devices/frient-a-s-sirzb-111.json | 2 +- tests/data/devices/frient-a-s-smszb-120.json | 2 +- tests/data/devices/frient-a-s-splzb-141.json | 2 +- tests/data/devices/frient-a-s-wiszb-131.json | 2 +- tests/data/devices/gledopto-gl-c-009p.json | 2 +- tests/data/devices/heiman-smokesensor-em.json | 2 +- ...-sweden-fyrtur-block-out-roller-blind.json | 2 +- .../ikea-of-sweden-inspelning-smart-plug.json | 2 +- ...-of-sweden-praktlysing-cellular-blind.json | 2 +- .../ikea-of-sweden-remote-control-n2.json | 2 +- .../devices/ikea-of-sweden-rodret-dimmer.json | 2 +- ...ikea-of-sweden-rodret-wireless-dimmer.json | 2 +- ...ikea-of-sweden-somrig-shortcut-button.json | 2 +- ...f-sweden-starkvind-air-purifier-table.json | 2 +- ...ikea-of-sweden-starkvind-air-purifier.json | 2 +- ...of-sweden-symfonisk-sound-remote-gen2.json | 2 +- ...ikea-of-sweden-tradfri-control-outlet.json | 2 +- .../ikea-of-sweden-tradfri-motion-sensor.json | 2 +- .../ikea-of-sweden-tradfri-on-off-switch.json | 2 +- ...a-of-sweden-tradfri-open-close-remote.json | 2 +- ...ikea-of-sweden-tradfri-remote-control.json | 2 +- ...kea-of-sweden-tradfri-wireless-dimmer.json | 2 +- ...weden-vallhorn-wireless-motion-sensor.json | 2 +- tests/data/devices/innr-sp-120.json | 2 +- tests/data/devices/innr-sp-234.json | 2 +- tests/data/devices/innr-sp-240.json | 2 +- tests/data/devices/inovelli-vzm30-sn.json | 2 +- tests/data/devices/inovelli-vzm31-sn.json | 2 +- tests/data/devices/inovelli-vzm35-sn.json | 2 +- .../devices/ke-tradfri-open-close-remote.json | 2 +- .../keen-home-inc-sv01-410-mp-1-1.json | 2 +- .../keen-home-inc-sv02-610-mp-1-3.json | 2 +- .../keen-home-inc-sv02-612-mp-1-3.json | 2 +- .../king-of-fans-inc-hbuniversalcfremote.json | 2 +- .../king-of-fans-inc-hdc52eastwindfan.json | 2 +- .../data/devices/konke-3afe140103020000.json | 2 +- .../data/devices/konke-3afe220103020000.json | 2 +- .../data/devices/konke-3afe270104020015.json | 2 +- .../data/devices/konke-3afe280100510001.json | 2 +- .../data/devices/konke-3afe28010402000d.json | 2 +- .../data/devices/lds-zbt-cctswitch-d0001.json | 2 +- tests/data/devices/ledvance-flex-rgbw.json | 2 +- .../legrand-dimmer-switch-w-o-neutral.json | 2 +- .../legrand-double-gangs-remote-switch.json | 2 +- .../legrand-light-switch-with-neutral.json | 2 +- tests/data/devices/lixee-zlinky-tic.json | 2 +- .../devices/lumi-lumi-airmonitor-acn01.json | 2 +- .../data/devices/lumi-lumi-airrtc-agl001.json | 2 +- .../devices/lumi-lumi-curtain-acn002.json | 2 +- .../devices/lumi-lumi-curtain-agl001.json | 2 +- .../data/devices/lumi-lumi-flood-acn001.json | 2 +- tests/data/devices/lumi-lumi-flood-agl02.json | 2 +- tests/data/devices/lumi-lumi-magnet-ac01.json | 2 +- .../data/devices/lumi-lumi-magnet-acn001.json | 2 +- .../data/devices/lumi-lumi-magnet-agl02.json | 2 +- tests/data/devices/lumi-lumi-motion-ac02.json | 2 +- .../data/devices/lumi-lumi-motion-agl02.json | 2 +- .../data/devices/lumi-lumi-motion-agl04.json | 2 +- tests/data/devices/lumi-lumi-plug-maus01.json | 2 +- .../data/devices/lumi-lumi-relay-c2acn01.json | 2 +- .../devices/lumi-lumi-remote-b286opcn01.json | 2 +- .../data/devices/lumi-lumi-remote-b28ac1.json | 2 +- .../devices/lumi-lumi-remote-b486opcn01.json | 2 +- .../devices/lumi-lumi-remote-b686opcn01.json | 2 +- .../data/devices/lumi-lumi-remote-cagl02.json | 2 +- .../data/devices/lumi-lumi-sen-ill-agl01.json | 2 +- .../data/devices/lumi-lumi-sen-ill-mgl01.json | 2 +- .../data/devices/lumi-lumi-sensor-86sw2.json | 2 +- .../devices/lumi-lumi-sensor-ht-agl02.json | 2 +- .../devices/lumi-lumi-sensor-magnet-aq2.json | 2 +- .../devices/lumi-lumi-sensor-motion-aq2.json | 2 +- .../devices/lumi-lumi-sensor-smoke-acn03.json | 2 +- .../data/devices/lumi-lumi-sensor-smoke.json | 2 +- .../devices/lumi-lumi-sensor-switch-aq2.json | 2 +- .../devices/lumi-lumi-sensor-switch-aq3.json | 2 +- .../data/devices/lumi-lumi-sensor-switch.json | 2 +- .../data/devices/lumi-lumi-switch-n0agl1.json | 2 +- .../data/devices/lumi-lumi-vibration-aq1.json | 2 +- tests/data/devices/lumi-lumi-weather.json | 2 +- .../devices/lutron-lzl4bwhl01-remote.json | 2 +- .../data/devices/mli-tint-extendedcolor.json | 2 +- .../data/devices/mli-zbt-remote-all-rgbw.json | 2 +- tests/data/devices/nodon-sin-4-fp-21.json | 2 +- tests/data/devices/nodon-sin-4-rs-20.json | 2 +- .../devices/osram-switch-4x-lightify.json | 2 +- .../devices/paulmann-licht-gmbh-501-34.json | 2 +- tests/data/devices/philips-7602031u7.json | 2 +- tests/data/devices/philips-rom001.json | 2 +- tests/data/devices/philips-rwl020.json | 2 +- tests/data/devices/philips-rwl021.json | 2 +- tests/data/devices/philips-sml001.json | 2 +- .../devices/plaid-systems-ps-sprzms-slp3.json | 2 +- tests/data/devices/samjin-button.json | 2 +- tests/data/devices/samjin-multi.json | 2 +- .../devices/schneider-electric-eko07259.json | 2 +- .../schneider-electric-puck-shutter-1.json | 2 +- .../schneider-electric-socket-outlet-1.json | 2 +- .../schneider-electric-socket-outlet-2.json | 2 +- tests/data/devices/shelly-1pm.json | 6 +- tests/data/devices/shelly-2pm.json | 6 +- tests/data/devices/shelly-ecowitt-ws90.json | 2 +- tests/data/devices/shelly-mini1pm.json | 6 +- .../devices/shyugj-dimmer-switch-zb3-0.json | 2 +- .../signify-netherlands-b-v-lcx004.json | 2 +- .../signify-netherlands-b-v-ltb003.json | 2 +- .../signify-netherlands-b-v-rdm001.json | 2 +- .../signify-netherlands-b-v-rdm002.json | 2 +- .../signify-netherlands-b-v-rdm004.json | 2 +- .../signify-netherlands-b-v-rdm005.json | 2 +- .../signify-netherlands-b-v-rwl022.json | 2 +- .../signify-netherlands-b-v-sml003.json | 2 +- .../signify-netherlands-b-v-sml004.json | 2 +- tests/data/devices/silicon-labs-ezsp.json | 171 +++++ .../devices/smarthjemmet-dk-quad-zig-sw.json | 2 +- tests/data/devices/smartthings-multiv4.json | 2 +- tests/data/devices/smartthings-tagv4.json | 2 +- tests/data/devices/smartwings-wm25-l-z.json | 2 +- tests/data/devices/sonoff-mini-zbrbs.json | 2 +- tests/data/devices/sonoff-s60zbtpg.json | 2 +- tests/data/devices/sonoff-snzb-01m.json | 2 +- tests/data/devices/sonoff-snzb-02d.json | 2 +- tests/data/devices/sonoff-snzb-06p.json | 2 +- tests/data/devices/sonoff-swv.json | 2 +- tests/data/devices/sonoff-trvzb.json | 2 +- tests/data/devices/sonoff-zbminir2.json | 2 +- .../devices/texasinstruments-ti-router.json | 2 +- .../devices/third-reality-inc-3rds17bz.json | 2 +- .../devices/third-reality-inc-3rms16bz.json | 2 +- .../devices/third-reality-inc-3rsb015bz.json | 2 +- .../devices/third-reality-inc-3rsb22bz.json | 2 +- .../devices/third-reality-inc-3rsm0147z.json | 2 +- .../third-reality-inc-3rsnl02043z.json | 2 +- .../third-reality-inc-3rsp02028bz.json | 2 +- .../devices/third-reality-inc-3rsp02064z.json | 2 +- .../devices/third-reality-inc-3rss009z.json | 2 +- .../devices/third-reality-inc-3rths24bz.json | 2 +- .../devices/third-reality-inc-3rvs01031z.json | 2 +- .../devices/third-reality-inc-3rws18bz.json | 2 +- .../data/devices/tyst11-i5j6ifxj-5j6ifxj.json | 2 +- .../data/devices/tyzb01-1xktopx6-ts0041a.json | 2 +- .../data/devices/tz3000-1dd0d5yi-ts130f.json | 2 +- .../data/devices/tz3000-303avxxt-ts011f.json | 2 +- .../data/devices/tz3000-6l1pjfqe-ts011f.json | 2 +- .../data/devices/tz3000-8nkb7mof-ts0121.json | 2 +- .../data/devices/tz3000-8nyaanzb-ts011f.json | 2 +- .../data/devices/tz3000-a37eix1s-ts0004.json | 2 +- .../data/devices/tz3000-bjawzodf-ty0201.json | 2 +- .../data/devices/tz3000-dbou1ap4-ts0505a.json | 2 +- .../data/devices/tz3000-drc9tuqb-ts0001.json | 2 +- .../data/devices/tz3000-empogkya-ts0003.json | 2 +- .../data/devices/tz3000-famkxci2-ts0043.json | 2 +- .../data/devices/tz3000-gwkzibhs-ts004f.json | 2 +- .../data/devices/tz3000-idhkkbqj-ts0012.json | 2 +- .../data/devices/tz3000-itnrsufe-ts0201.json | 2 +- .../data/devices/tz3000-j1xl73iw-ts130f.json | 2 +- .../data/devices/tz3000-ja5osu5g-ts004f.json | 2 +- .../data/devices/tz3000-kjfzuycl-ts004f.json | 2 +- .../data/devices/tz3000-kmsbwdol-ts130f.json | 2 +- .../data/devices/tz3000-kqvb5akv-ts0001.json | 2 +- .../data/devices/tz3000-lepzuhto-ts011f.json | 2 +- .../data/devices/tz3000-llfaquvp-ts0012.json | 2 +- .../data/devices/tz3000-ltiqubue-ts130f.json | 2 +- .../data/devices/tz3000-mkhkxx1p-ts0001.json | 2 +- .../data/devices/tz3000-mw1pqqqt-ts0003.json | 2 +- .../data/devices/tz3000-o1jzcxou-ts011f.json | 2 +- .../data/devices/tz3000-okaz9tjs-ts011f.json | 2 +- .../data/devices/tz3000-p26flek3-ts0001.json | 2 +- .../data/devices/tz3000-r6buo8ba-ts011f.json | 2 +- .../data/devices/tz3000-rbl8c85w-ts0012.json | 2 +- .../data/devices/tz3000-sgb0xhwn-ts011f.json | 2 +- .../data/devices/tz3000-tgddllx4-ts0001.json | 2 +- .../data/devices/tz3000-typdpbpg-ts011f.json | 2 +- .../data/devices/tz3000-u3oupgdy-ts0004.json | 2 +- .../data/devices/tz3000-w0qqde0g-ts011f.json | 2 +- .../data/devices/tz3000-wkr3jqmr-ts0004.json | 2 +- .../data/devices/tz3000-wn65ixz9-ts0001.json | 2 +- .../data/devices/tz3000-xkap8wtb-ts000f.json | 2 +- .../data/devices/tz3000-xxacnuab-ts0004.json | 2 +- .../data/devices/tz3000-yj6k7vfo-ts0041.json | 2 +- .../data/devices/tz3000-zl1kmjqx-ty0201.json | 2 +- tests/data/devices/tz3000-zl1kmjqx.json | 2 +- .../data/devices/tz3000-zv6x8bt2-ts011f.json | 2 +- .../data/devices/tz3040-bb6xaihh-ts0202.json | 2 +- .../data/devices/tz3210-0jxeoadc-ts0049.json | 2 +- .../data/devices/tz3210-3ulg9kpo-ts0021.json | 2 +- .../data/devices/tz3210-dwytrmda-ts130f.json | 2 +- .../data/devices/tz3210-j4pdtz9v-ts0001.json | 2 +- .../data/devices/tz3210-tgvtvdoc-ts0207.json | 2 +- .../data/devices/tz3210-up3pngle-ts0205.json | 2 +- .../tz3290-7v1k4vufotpowp9z-ts1201.json | 2 +- .../tz3290-ot6ewjvmejq5ekhl-ts1201.json | 2 +- .../data/devices/tz6210-duv6fhwt-ts0601.json | 2 +- .../data/devices/tze200-1n2zev06-ts0601.json | 2 +- .../data/devices/tze200-2aaelwxk-ts0225.json | 2 +- .../data/devices/tze200-2se8efxh-ts0601.json | 2 +- .../data/devices/tze200-3towulqd-ts0601.json | 2 +- .../data/devices/tze200-4utwozi2-ts0601.json | 2 +- .../data/devices/tze200-6rdj8dzm-ts0601.json | 2 +- .../data/devices/tze200-7bztmfm1-ts0601.json | 2 +- .../data/devices/tze200-7ytb3h8u-ts0601.json | 2 +- .../data/devices/tze200-81isopgh-ts0601.json | 2 +- .../data/devices/tze200-9xfjixap-ts0601.json | 2 +- .../data/devices/tze200-a7sghmms-ts0601.json | 2 +- .../data/devices/tze200-b6wax7g0-ts0601.json | 2 +- .../data/devices/tze200-bkkmqmyo-ts0601.json | 2 +- .../data/devices/tze200-c88teujp-ts0601.json | 2 +- .../data/devices/tze200-cirvgep4-ts0601.json | 2 +- .../tze200-crq3r3la-ck-bl702-mws-01-7016.json | 2 +- .../data/devices/tze200-dwcarsat-ts0601.json | 2 +- .../data/devices/tze200-gaj531w3-ts0601.json | 2 +- .../data/devices/tze200-gjldowol-ts0601.json | 2 +- .../data/devices/tze200-gkfbdvyx-ts0601.json | 2 +- .../data/devices/tze200-h4cgnbzg-ts0601.json | 2 +- .../data/devices/tze200-hr0tdd47-ts0601.json | 2 +- .../data/devices/tze200-icka1clh-ts0601.json | 2 +- .../data/devices/tze200-jva8ink8-ts0601.json | 2 +- .../data/devices/tze200-jwsjbxjs-ts0601.json | 2 +- .../data/devices/tze200-kb5noeto-ts0601.json | 2 +- .../data/devices/tze200-kyfqmmyl-ts0601.json | 2 +- .../data/devices/tze200-locansqn-ts0601.json | 2 +- .../data/devices/tze200-m9skfctm-ts0601.json | 2 +- .../data/devices/tze200-mja3fuja-ts0601.json | 2 +- .../data/devices/tze200-mudxchsu-ts0601.json | 2 +- .../data/devices/tze200-myd45weu-ts0601.json | 2 +- .../data/devices/tze200-ntcy3xu1-ts0601.json | 2 +- .../data/devices/tze200-pay2byax-ts0601.json | 2 +- .../data/devices/tze200-qrztc3ev-ts0601.json | 2 +- .../data/devices/tze200-qyflbnbj-ts0601.json | 2 +- .../data/devices/tze200-rccxox8p-ts0601.json | 2 +- .../data/devices/tze200-rxq4iti9-ts0601.json | 2 +- .../data/devices/tze200-sur6q7ko-ts0601.json | 2 +- .../data/devices/tze200-t1blo2bj-ts0601.json | 2 +- .../data/devices/tze200-vdiuwbkq-ts0601.json | 2 +- .../data/devices/tze200-viy9ihs7-ts0601.json | 2 +- .../data/devices/tze200-vvmbj46n-ts0601.json | 2 +- .../data/devices/tze200-ya4ft0w4-ts0601.json | 2 +- .../data/devices/tze200-yjjdcqsq-ts0601.json | 2 +- .../data/devices/tze200-yojqa8xn-ts0601.json | 2 +- .../data/devices/tze200-yvx5lh6k-ts0601.json | 2 +- .../data/devices/tze200-yw7cahqs-ts0601.json | 2 +- .../data/devices/tze204-1v1dxkck-ts0601.json | 2 +- .../data/devices/tze204-1youk3hj-ts0601.json | 2 +- .../data/devices/tze204-58of2pfn-ts0601.json | 2 +- .../data/devices/tze204-7ytb3h8u-ts0601.json | 2 +- .../data/devices/tze204-9yapgbuv-ts0601.json | 2 +- .../data/devices/tze204-bxoo2swd-ts0601.json | 2 +- .../data/devices/tze204-c2fmom5z-ts0601.json | 2 +- .../data/devices/tze204-chbyv06x-ts0601.json | 2 +- .../data/devices/tze204-cirvgep4-ts0601.json | 2 +- .../data/devices/tze204-dqolcpcp-ts0601.json | 2 +- .../data/devices/tze204-dtzziy1e-ts0601.json | 2 +- .../data/devices/tze204-dwcarsat-ts0601.json | 2 +- .../data/devices/tze204-ex3rcdha-ts0601.json | 2 +- .../data/devices/tze204-gops3slb-ts0601.json | 2 +- .../data/devices/tze204-iaeejhvf-ts0601.json | 2 +- .../data/devices/tze204-k7mfgaen-ts0601.json | 2 +- .../data/devices/tze204-kyhbrfyl-ts0601.json | 2 +- .../data/devices/tze204-ltwbm23f-ts0601.json | 2 +- .../data/devices/tze204-lzriup1j-ts0601.json | 2 +- .../data/devices/tze204-mtoaryre-ts0601.json | 2 +- .../data/devices/tze204-muvkrjr5-ts0601.json | 2 +- .../data/devices/tze204-nlrfgpny-ts0601.json | 2 +- .../data/devices/tze204-nqqylykc-ts0601.json | 2 +- .../data/devices/tze204-ogx8u5z6-ts0601.json | 2 +- .../data/devices/tze204-p3lqqy2r-ts0601.json | 2 +- .../data/devices/tze204-ptaqh9tk-ts0601.json | 2 +- .../data/devices/tze204-qasjif9e-ts0601.json | 2 +- .../data/devices/tze204-qvxrkeif-ts0601.json | 2 +- .../data/devices/tze204-qyr2m29i-ts0601.json | 2 +- .../data/devices/tze204-rtrmfadk-ts0601.json | 2 +- .../data/devices/tze204-sooucan5-ts0601.json | 2 +- .../data/devices/tze204-sxm7l9xa-ts0601.json | 2 +- .../data/devices/tze204-upagmta9-ts0601.json | 2 +- .../data/devices/tze204-uxllnywp-ts0601.json | 2 +- .../data/devices/tze204-vmcgja59-ts0601.json | 2 +- .../data/devices/tze204-xnbkhhdr-ts0601.json | 2 +- .../data/devices/tze204-ya4ft0w4-ts0601.json | 2 +- .../data/devices/tze204-yjjdcqsq-ts0601.json | 2 +- .../data/devices/tze204-yvx5lh6k-ts0601.json | 2 +- .../data/devices/tze204-zenj4lxv-ts0601.json | 2 +- .../data/devices/tze204-ztqnh5cg-ts0601.json | 2 +- .../data/devices/tze284-0zaf1cr8-ts0601.json | 2 +- .../data/devices/tze284-3mzb0sdz-ts0601.json | 2 +- .../data/devices/tze284-aao3yzhs-ts0601.json | 2 +- .../data/devices/tze284-c6wv4xyo-ts0601.json | 2 +- .../data/devices/tze284-dikb3dp6-ts0601.json | 2 +- .../data/devices/tze284-fhvpaltk-ts0601.json | 2 +- .../data/devices/tze284-kyyu8rbj-ts0601.json | 2 +- .../data/devices/tze284-myd45weu-ts0601.json | 2 +- .../data/devices/tze284-ne4pikwm-ts0601.json | 2 +- .../data/devices/tze284-o3x45p96-ts0601.json | 2 +- .../data/devices/tze284-ogx8u5z6-ts0601.json | 2 +- .../data/devices/tze284-oitavov2-ts0601.json | 6 +- .../data/devices/tze284-qyflbnbj-ts0601.json | 2 +- .../data/devices/tze284-rjxqso4a-ts0601.json | 2 +- .../data/devices/tze284-rqcuwlsa-ts0601.json | 2 +- .../data/devices/tze284-sgabhwa6-ts0601.json | 2 +- .../data/devices/tze284-upagmta9-ts0601.json | 2 +- .../data/devices/tze284-xnbkhhdr-ts0601.json | 2 +- tests/data/devices/ubisys-j1-5502.json | 657 +++++++++++++++++- tests/data/devices/ubisys-s1-5501.json | 2 +- ...versal-electronics-inc-urc4460bc0-x-r.json | 2 +- tests/data/devices/visonic-mct-340-sma.json | 2 +- tests/data/devices/xiaomi-lywsd03mmc.json | 2 +- tests/data/devices/xiaoyan-terncy-sd01.json | 2 +- tests/test_inovelli.py.bak | 164 +++++ 343 files changed, 1313 insertions(+), 408 deletions(-) create mode 100644 tests/data/devices/silicon-labs-ezsp.json create mode 100644 tests/test_inovelli.py.bak diff --git a/tests/data/devices/adeo-sin-4-fp-21-equ.json b/tests/data/devices/adeo-sin-4-fp-21-equ.json index 4a6b0d38e..81c8e8c2f 100644 --- a/tests/data/devices/adeo-sin-4-fp-21-equ.json +++ b/tests/data/devices/adeo-sin-4-fp-21-equ.json @@ -8,7 +8,7 @@ "friendly_model": "SIN-4-FP-21_EQU", "name": "Adeo SIN-4-FP-21_EQU", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.nodon.pilot_wire:(Adeo / SIN-4-FP-21_EQU)", "exposes_features": [], "manufacturer_code": 4727, "power_source": "Mains", diff --git a/tests/data/devices/aqara-lumi-light-acn132.json b/tests/data/devices/aqara-lumi-light-acn132.json index 0a52a8ced..4154ef790 100644 --- a/tests/data/devices/aqara-lumi-light-acn132.json +++ b/tests/data/devices/aqara-lumi-light-acn132.json @@ -8,7 +8,7 @@ "friendly_model": "Led strip T1", "name": "Aqara Led strip T1", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.xiaomi.aqara.led_strip_t1:(Aqara / lumi.light.acn132)", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Mains", diff --git a/tests/data/devices/aqara-lumi-motion-ac01.json b/tests/data/devices/aqara-lumi-motion-ac01.json index b1e452276..f1e28f60b 100644 --- a/tests/data/devices/aqara-lumi-motion-ac01.json +++ b/tests/data/devices/aqara-lumi-motion-ac01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.motion.ac01", "name": "aqara lumi.motion.ac01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.motion_ac01.AqaraLumiMotionAc01", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_ac01:AqaraLumiMotionAc01", "exposes_features": [], "manufacturer_code": 4660, "power_source": "Mains", diff --git a/tests/data/devices/aqara-lumi-sensor-occupy-agl1.json b/tests/data/devices/aqara-lumi-sensor-occupy-agl1.json index 30949abb0..664a3382e 100644 --- a/tests/data/devices/aqara-lumi-sensor-occupy-agl1.json +++ b/tests/data/devices/aqara-lumi-sensor-occupy-agl1.json @@ -8,7 +8,7 @@ "friendly_model": "Presence Sensor FP1E", "name": "Aqara Presence Sensor FP1E", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_agl1:(aqara / lumi.sensor_occupy.agl1)", "exposes_features": [], "manufacturer_code": 4660, "power_source": "Mains", diff --git a/tests/data/devices/aqara-lumi-switch-acn047.json b/tests/data/devices/aqara-lumi-switch-acn047.json index ec37a8636..a11774c1e 100644 --- a/tests/data/devices/aqara-lumi-switch-acn047.json +++ b/tests/data/devices/aqara-lumi-switch-acn047.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.switch.acn047", "name": "Aqara lumi.switch.acn047", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.switch_acn047.AqaraT2Relay", + "quirk_class": "zhaquirks.xiaomi.aqara.switch_acn047:AqaraT2Relay", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Mains", diff --git a/tests/data/devices/bega-gantenbrink-leuchten-kg-smart-dimmable-light.json b/tests/data/devices/bega-gantenbrink-leuchten-kg-smart-dimmable-light.json index 1ddb18bc7..ee2058d26 100644 --- a/tests/data/devices/bega-gantenbrink-leuchten-kg-smart-dimmable-light.json +++ b/tests/data/devices/bega-gantenbrink-leuchten-kg-smart-dimmable-light.json @@ -8,7 +8,7 @@ "friendly_model": "Smart Dimmable Light", "name": "BEGA Gantenbrink-Leuchten KG Smart Dimmable Light", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.bega.light:(BEGA Gantenbrink-Leuchten KG / Smart Dimmable Light)", "exposes_features": [ "bega.light_switchable_white" ], diff --git a/tests/data/devices/bosch-rbsh-mms-zb-eu.json b/tests/data/devices/bosch-rbsh-mms-zb-eu.json index ae6ac9f4f..18191dd34 100644 --- a/tests/data/devices/bosch-rbsh-mms-zb-eu.json +++ b/tests/data/devices/bosch-rbsh-mms-zb-eu.json @@ -8,7 +8,7 @@ "friendly_model": "BMCT-SLZ", "name": "Bosch BMCT-SLZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.bosch.relay_rbsh-mms-zb-eu:(Bosch / RBSH-MMS-ZB-EU)", "exposes_features": [], "manufacturer_code": 4617, "power_source": "Mains", diff --git a/tests/data/devices/bosch-rbsh-rth0-bat-zb-eu.json b/tests/data/devices/bosch-rbsh-rth0-bat-zb-eu.json index d33d407f8..69c057474 100644 --- a/tests/data/devices/bosch-rbsh-rth0-bat-zb-eu.json +++ b/tests/data/devices/bosch-rbsh-rth0-bat-zb-eu.json @@ -8,7 +8,7 @@ "friendly_model": "RBSH-RTH0-BAT-ZB-EU", "name": "Bosch RBSH-RTH0-BAT-ZB-EU", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.bosch.rbsh_rth0_zb_eu:(Bosch / RBSH-RTH0-ZB-EU)", "exposes_features": [], "manufacturer_code": 4617, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/bosch-rbsh-rth0-zb-eu.json b/tests/data/devices/bosch-rbsh-rth0-zb-eu.json index 23688b450..0e9f05416 100644 --- a/tests/data/devices/bosch-rbsh-rth0-zb-eu.json +++ b/tests/data/devices/bosch-rbsh-rth0-zb-eu.json @@ -8,7 +8,7 @@ "friendly_model": "RBSH-RTH0-ZB-EU", "name": "Bosch RBSH-RTH0-ZB-EU", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.bosch.rbsh_rth0_zb_eu:(Bosch / RBSH-RTH0-ZB-EU)", "exposes_features": [], "manufacturer_code": 4617, "power_source": "Mains", diff --git a/tests/data/devices/bosch-rbsh-trv0-zb-eu.json b/tests/data/devices/bosch-rbsh-trv0-zb-eu.json index 055ec1c08..1fadc3bb9 100644 --- a/tests/data/devices/bosch-rbsh-trv0-zb-eu.json +++ b/tests/data/devices/bosch-rbsh-trv0-zb-eu.json @@ -8,7 +8,7 @@ "friendly_model": "RBSH-TRV0-ZB-EU", "name": "BOSCH RBSH-TRV0-ZB-EU", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.bosch.rbsh_trv0_zb_eu:(BOSCH / RBSH-TRV0-ZB-EU)", "exposes_features": [], "manufacturer_code": 4617, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/candeo-c-zb-lc20-dim.json b/tests/data/devices/candeo-c-zb-lc20-dim.json index f329defb4..e7a65b84a 100644 --- a/tests/data/devices/candeo-c-zb-lc20-dim.json +++ b/tests/data/devices/candeo-c-zb-lc20-dim.json @@ -8,7 +8,7 @@ "friendly_model": "C-ZB-LC20-Dim", "name": "Candeo C-ZB-LC20-Dim", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.candeo.smart_led_controllers:(Candeo / C-ZB-LC20-Dim)", "exposes_features": [], "manufacturer_code": 4687, "power_source": "Mains", diff --git a/tests/data/devices/candeo-c-zb-lc20-rgb.json b/tests/data/devices/candeo-c-zb-lc20-rgb.json index cf601316c..972562297 100644 --- a/tests/data/devices/candeo-c-zb-lc20-rgb.json +++ b/tests/data/devices/candeo-c-zb-lc20-rgb.json @@ -8,7 +8,7 @@ "friendly_model": "C-ZB-LC20-RGB", "name": "Candeo C-ZB-LC20-RGB", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.candeo.smart_led_controllers:(Candeo / C-ZB-LC20-RGB)", "exposes_features": [], "manufacturer_code": 4687, "power_source": "Mains", diff --git a/tests/data/devices/centralite-3310-g.json b/tests/data/devices/centralite-3310-g.json index 9fb6b9434..b8e7aa4a2 100644 --- a/tests/data/devices/centralite-3310-g.json +++ b/tests/data/devices/centralite-3310-g.json @@ -8,7 +8,7 @@ "friendly_model": "3310-G", "name": "CentraLite 3310-G", "quirk_applied": true, - "quirk_class": "zhaquirks.centralite.cl_3310S.CentraLite3310S", + "quirk_class": "zhaquirks.centralite.cl_3310S:CentraLite3310S", "exposes_features": [], "manufacturer_code": 4174, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/centralite-3315-seu.json b/tests/data/devices/centralite-3315-seu.json index 610f3b640..ea8cc5475 100644 --- a/tests/data/devices/centralite-3315-seu.json +++ b/tests/data/devices/centralite-3315-seu.json @@ -8,7 +8,7 @@ "friendly_model": "3315-Seu", "name": "CentraLite 3315-Seu", "quirk_applied": true, - "quirk_class": "zhaquirks.centralite.ias.CentraLiteIASSensorV3", + "quirk_class": "zhaquirks.centralite.ias:CentraLiteIASSensorV3", "exposes_features": [], "manufacturer_code": 4174, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/centralite-3320-l.json b/tests/data/devices/centralite-3320-l.json index 726c96c03..8f303076b 100644 --- a/tests/data/devices/centralite-3320-l.json +++ b/tests/data/devices/centralite-3320-l.json @@ -8,7 +8,7 @@ "friendly_model": "3320-L", "name": "CentraLite 3320-L", "quirk_applied": true, - "quirk_class": "zhaquirks.centralite.ias.CentraLiteIASSensor", + "quirk_class": "zhaquirks.centralite.ias:CentraLiteIASSensor", "exposes_features": [], "manufacturer_code": 49887, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/centralite-3326-l.json b/tests/data/devices/centralite-3326-l.json index 8f1fb1e81..4cd89d36f 100644 --- a/tests/data/devices/centralite-3326-l.json +++ b/tests/data/devices/centralite-3326-l.json @@ -8,7 +8,7 @@ "friendly_model": "3326-L", "name": "CentraLite 3326-L", "quirk_applied": true, - "quirk_class": "zhaquirks.centralite.motion.CentraLiteMotionSensor", + "quirk_class": "zhaquirks.centralite.motion:CentraLiteMotionSensor", "exposes_features": [], "manufacturer_code": 49887, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/d5x84yu-et093wrg.json b/tests/data/devices/d5x84yu-et093wrg.json index fad8ce4b7..f85abf36e 100644 --- a/tests/data/devices/d5x84yu-et093wrg.json +++ b/tests/data/devices/d5x84yu-et093wrg.json @@ -8,7 +8,7 @@ "friendly_model": "eT093WRG", "name": "D5X84YU eT093WRG", "quirk_applied": true, - "quirk_class": "zhaquirks.danfoss.thermostat.DanfossThermostat", + "quirk_class": "zhaquirks.danfoss.thermostat:DanfossThermostat", "exposes_features": [ "danfoss.ally_thermostat" ], diff --git a/tests/data/devices/danfoss-etrv0103.json b/tests/data/devices/danfoss-etrv0103.json index f4e787b54..e18a71021 100644 --- a/tests/data/devices/danfoss-etrv0103.json +++ b/tests/data/devices/danfoss-etrv0103.json @@ -8,7 +8,7 @@ "friendly_model": "eTRV0103", "name": "Danfoss eTRV0103", "quirk_applied": true, - "quirk_class": "zhaquirks.danfoss.thermostat.DanfossThermostat", + "quirk_class": "zhaquirks.danfoss.thermostat:DanfossThermostat", "exposes_features": [ "danfoss.ally_thermostat" ], diff --git a/tests/data/devices/digi-xbee3.json b/tests/data/devices/digi-xbee3.json index 99c762c92..c8d85941c 100644 --- a/tests/data/devices/digi-xbee3.json +++ b/tests/data/devices/digi-xbee3.json @@ -8,7 +8,7 @@ "friendly_model": "XBee3", "name": "Digi XBee3", "quirk_applied": true, - "quirk_class": "zhaquirks.xbee.xbee3_io.XBee3Sensor", + "quirk_class": "zhaquirks.xbee.xbee3_io:XBee3Sensor", "exposes_features": [], "manufacturer_code": 4126, "power_source": "Mains", diff --git a/tests/data/devices/ecodim-bv-eco-dim-05-zigbee.json b/tests/data/devices/ecodim-bv-eco-dim-05-zigbee.json index 23dfb9f72..2c9e40ce8 100644 --- a/tests/data/devices/ecodim-bv-eco-dim-05-zigbee.json +++ b/tests/data/devices/ecodim-bv-eco-dim-05-zigbee.json @@ -8,7 +8,7 @@ "friendly_model": "Eco-Dim.05 Zigbee", "name": "EcoDim BV Eco-Dim.05 Zigbee", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.hzc.doubledimmerswitch:(EcoDim BV / EcoDim-Zigbee 3.0)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/ecodim-bv-ecodim-zigbee-3-0.json b/tests/data/devices/ecodim-bv-ecodim-zigbee-3-0.json index 5079030fb..2c178f528 100644 --- a/tests/data/devices/ecodim-bv-ecodim-zigbee-3-0.json +++ b/tests/data/devices/ecodim-bv-ecodim-zigbee-3-0.json @@ -8,7 +8,7 @@ "friendly_model": "EcoDim-Zigbee 3.0", "name": "EcoDim BV EcoDim-Zigbee 3.0", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.hzc.doubledimmerswitch:(EcoDim BV / EcoDim-Zigbee 3.0)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/ecolink-4655bc0-r.json b/tests/data/devices/ecolink-4655bc0-r.json index 75b58aa62..de9e420d6 100644 --- a/tests/data/devices/ecolink-4655bc0-r.json +++ b/tests/data/devices/ecolink-4655bc0-r.json @@ -8,7 +8,7 @@ "friendly_model": "4655BC0-R", "name": "Ecolink 4655BC0-R", "quirk_applied": true, - "quirk_class": "zhaquirks.ecolink.contact.Ecolink4655BC0R", + "quirk_class": "zhaquirks.ecolink.contact:Ecolink4655BC0R", "exposes_features": [], "manufacturer_code": 4335, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ewelink-ck-tlsr8656-ss5-01-7000.json b/tests/data/devices/ewelink-ck-tlsr8656-ss5-01-7000.json index c5f45fdc5..e28a64c2c 100644 --- a/tests/data/devices/ewelink-ck-tlsr8656-ss5-01-7000.json +++ b/tests/data/devices/ewelink-ck-tlsr8656-ss5-01-7000.json @@ -8,7 +8,7 @@ "friendly_model": "CK-TLSR8656-SS5-01(7000)", "name": "eWeLink CK-TLSR8656-SS5-01(7000)", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.button:(eWeLink / WB01)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ewelink-snzb-01p.json b/tests/data/devices/ewelink-snzb-01p.json index 29a8d0e3f..59128b1b4 100644 --- a/tests/data/devices/ewelink-snzb-01p.json +++ b/tests/data/devices/ewelink-snzb-01p.json @@ -8,7 +8,7 @@ "friendly_model": "SNZB-01P", "name": "eWeLink SNZB-01P", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.button:(eWeLink / WB01)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ewelink-snzb-04p.json b/tests/data/devices/ewelink-snzb-04p.json index 9553fe292..33b34ca0d 100644 --- a/tests/data/devices/ewelink-snzb-04p.json +++ b/tests/data/devices/ewelink-snzb-04p.json @@ -8,7 +8,7 @@ "friendly_model": "SNZB-04P", "name": "eWeLink SNZB-04P", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.snzb04p:(eWeLink / SNZB-04P)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ewelink-wb01.json b/tests/data/devices/ewelink-wb01.json index be8aa03df..b405c6961 100644 --- a/tests/data/devices/ewelink-wb01.json +++ b/tests/data/devices/ewelink-wb01.json @@ -8,7 +8,7 @@ "friendly_model": "WB01", "name": "eWeLink WB01", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.button:(eWeLink / WB01)", "exposes_features": [], "manufacturer_code": 0, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-aqszb-110.json b/tests/data/devices/frient-a-s-aqszb-110.json index 0d50bc2d6..1e48f4138 100644 --- a/tests/data/devices/frient-a-s-aqszb-110.json +++ b/tests/data/devices/frient-a-s-aqszb-110.json @@ -8,7 +8,7 @@ "friendly_model": "AQSZB-110", "name": "frient A/S AQSZB-110", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.air_quality:(frient A/S / AQSZB-110)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-emizb-141.json b/tests/data/devices/frient-a-s-emizb-141.json index 02be95a06..f2b296e8f 100644 --- a/tests/data/devices/frient-a-s-emizb-141.json +++ b/tests/data/devices/frient-a-s-emizb-141.json @@ -8,7 +8,7 @@ "friendly_model": "EMIZB-141", "name": "frient A/S EMIZB-141", "quirk_applied": true, - "quirk_class": "zhaquirks.develco.ManufacturerDeviceV2", + "quirk_class": "zhaquirks.develco.emi_led:(frient A/S / EMIZB-141)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-emizb-151.json b/tests/data/devices/frient-a-s-emizb-151.json index a2d152951..15cd8f1c4 100644 --- a/tests/data/devices/frient-a-s-emizb-151.json +++ b/tests/data/devices/frient-a-s-emizb-151.json @@ -8,7 +8,7 @@ "friendly_model": "EMIZB-151", "name": "frient A/S EMIZB-151", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.emi_p1:(frient A/S / EMIZB-151)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", @@ -1259,39 +1259,6 @@ "state": null } }, - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:74:0f:89:c3-2-1", - "migrate_unique_ids": [], - "platform": "sensor", - "class_name": "Battery", - "translation_key": null, - "translation_placeholders": null, - "device_class": "battery", - "state_class": "measurement", - "entity_category": "diagnostic", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:74:0f:89:c3", - "endpoint_id": 2, - "available": true, - "group_id": null, - "suggested_display_precision": 0, - "unit": "%" - }, - "state": { - "class_name": "Battery", - "available": true, - "state": null - }, - "extra_state_attributes": [ - "battery_quantity", - "battery_size", - "battery_voltage" - ] - }, { "info_object": { "fallback_name": null, diff --git a/tests/data/devices/frient-a-s-flszb-110.json b/tests/data/devices/frient-a-s-flszb-110.json index 58afdea0f..7941fac59 100644 --- a/tests/data/devices/frient-a-s-flszb-110.json +++ b/tests/data/devices/frient-a-s-flszb-110.json @@ -8,7 +8,7 @@ "friendly_model": "FLSZB-110", "name": "frient A/S FLSZB-110", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.water_leak:(frient A/S / FLSZB-110)", "exposes_features": [ "siren_basic" ], diff --git a/tests/data/devices/frient-a-s-hmszb-120.json b/tests/data/devices/frient-a-s-hmszb-120.json index 7807cc353..ad7ab334b 100644 --- a/tests/data/devices/frient-a-s-hmszb-120.json +++ b/tests/data/devices/frient-a-s-hmszb-120.json @@ -8,7 +8,7 @@ "friendly_model": "HMSZB-120", "name": "frient A/S HMSZB-120", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.humidity:(frient A/S / HMSZB-120)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-iomzb-110.json b/tests/data/devices/frient-a-s-iomzb-110.json index b1330d95b..a1883c25c 100644 --- a/tests/data/devices/frient-a-s-iomzb-110.json +++ b/tests/data/devices/frient-a-s-iomzb-110.json @@ -8,7 +8,7 @@ "friendly_model": "IOMZB-110", "name": "frient A/S IOMZB-110", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.io_module:(frient A/S / IOMZB-110)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Mains", diff --git a/tests/data/devices/frient-a-s-kepzb-110.json b/tests/data/devices/frient-a-s-kepzb-110.json index ec95fe604..eeb16c654 100644 --- a/tests/data/devices/frient-a-s-kepzb-110.json +++ b/tests/data/devices/frient-a-s-kepzb-110.json @@ -8,7 +8,7 @@ "friendly_model": "KEPZB-110", "name": "frient A/S KEPZB-110", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.intelligent_keypad:(frient A/S / KEPZB-110)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-moszb-140.json b/tests/data/devices/frient-a-s-moszb-140.json index 15bfe8ee5..91b84af22 100644 --- a/tests/data/devices/frient-a-s-moszb-140.json +++ b/tests/data/devices/frient-a-s-moszb-140.json @@ -8,7 +8,7 @@ "friendly_model": "MOSZB-140", "name": "frient A/S MOSZB-140", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.motion:(frient A/S / MOSZB-140)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-moszb-153.json b/tests/data/devices/frient-a-s-moszb-153.json index d3364789e..3834d4c85 100644 --- a/tests/data/devices/frient-a-s-moszb-153.json +++ b/tests/data/devices/frient-a-s-moszb-153.json @@ -8,7 +8,7 @@ "friendly_model": "MOSZB-153", "name": "frient A/S MOSZB-153", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.motion:(frient A/S / MOSZB-153)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-sbtzb-110.json b/tests/data/devices/frient-a-s-sbtzb-110.json index d5e724cb4..dea4b7a53 100644 --- a/tests/data/devices/frient-a-s-sbtzb-110.json +++ b/tests/data/devices/frient-a-s-sbtzb-110.json @@ -8,7 +8,7 @@ "friendly_model": "SBTZB-110", "name": "frient A/S SBTZB-110", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.smart_button:(frient A/S / SBTZB-110)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/frient-a-s-scazb-141.json b/tests/data/devices/frient-a-s-scazb-141.json index 0554309b1..21b9b9fc0 100644 --- a/tests/data/devices/frient-a-s-scazb-141.json +++ b/tests/data/devices/frient-a-s-scazb-141.json @@ -8,7 +8,7 @@ "friendly_model": "SCAZB-141", "name": "frient A/S SCAZB-141", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.smoke_alarm_us:(frient A/S / SCAZB-141)", "exposes_features": [ "siren_basic" ], diff --git a/tests/data/devices/frient-a-s-sirzb-111.json b/tests/data/devices/frient-a-s-sirzb-111.json index 397b941be..1bbf7d312 100644 --- a/tests/data/devices/frient-a-s-sirzb-111.json +++ b/tests/data/devices/frient-a-s-sirzb-111.json @@ -8,7 +8,7 @@ "friendly_model": "SIRZB-111", "name": "frient A/S SIRZB-111", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.smart_siren:(frient A/S / SIRZB-111)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Mains", diff --git a/tests/data/devices/frient-a-s-smszb-120.json b/tests/data/devices/frient-a-s-smszb-120.json index e1890ccdf..3f407c905 100644 --- a/tests/data/devices/frient-a-s-smszb-120.json +++ b/tests/data/devices/frient-a-s-smszb-120.json @@ -8,7 +8,7 @@ "friendly_model": "SMSZB-120", "name": "frient A/S SMSZB-120", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.smoke_alarm:(frient A/S / SMSZB-120)", "exposes_features": [ "siren_basic" ], diff --git a/tests/data/devices/frient-a-s-splzb-141.json b/tests/data/devices/frient-a-s-splzb-141.json index e3067b1b4..d04f1ddf2 100644 --- a/tests/data/devices/frient-a-s-splzb-141.json +++ b/tests/data/devices/frient-a-s-splzb-141.json @@ -8,7 +8,7 @@ "friendly_model": "SPLZB-141", "name": "frient A/S SPLZB-141", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.power_plug:(frient A/S / SPLZB-141)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Mains", diff --git a/tests/data/devices/frient-a-s-wiszb-131.json b/tests/data/devices/frient-a-s-wiszb-131.json index b1bf60d3d..51cac87ae 100644 --- a/tests/data/devices/frient-a-s-wiszb-131.json +++ b/tests/data/devices/frient-a-s-wiszb-131.json @@ -8,7 +8,7 @@ "friendly_model": "WISZB-131", "name": "frient A/S WISZB-131", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.develco.open_close:(frient A/S / WISZB-131)", "exposes_features": [], "manufacturer_code": 4117, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/gledopto-gl-c-009p.json b/tests/data/devices/gledopto-gl-c-009p.json index 85f9c57af..7879d3cc2 100644 --- a/tests/data/devices/gledopto-gl-c-009p.json +++ b/tests/data/devices/gledopto-gl-c-009p.json @@ -8,7 +8,7 @@ "friendly_model": "GL-C-009P", "name": "GLEDOPTO GL-C-009P", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.gledopto.glc009p:(GLEDOPTO / GL-C-009P)", "exposes_features": [], "manufacturer_code": 4687, "power_source": "Mains", diff --git a/tests/data/devices/heiman-smokesensor-em.json b/tests/data/devices/heiman-smokesensor-em.json index c8a15fea3..7f1fda32c 100644 --- a/tests/data/devices/heiman-smokesensor-em.json +++ b/tests/data/devices/heiman-smokesensor-em.json @@ -8,7 +8,7 @@ "friendly_model": "SmokeSensor-EM", "name": "HEIMAN SmokeSensor-EM", "quirk_applied": true, - "quirk_class": "zhaquirks.heiman.smoke.HeimanSmokeEM", + "quirk_class": "zhaquirks.heiman.smoke:HeimanSmokeEM", "exposes_features": [], "manufacturer_code": 4619, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-fyrtur-block-out-roller-blind.json b/tests/data/devices/ikea-of-sweden-fyrtur-block-out-roller-blind.json index ce8dd274f..ba8b1ee91 100644 --- a/tests/data/devices/ikea-of-sweden-fyrtur-block-out-roller-blind.json +++ b/tests/data/devices/ikea-of-sweden-fyrtur-block-out-roller-blind.json @@ -8,7 +8,7 @@ "friendly_model": "FYRTUR block-out roller blind", "name": "IKEA of Sweden FYRTUR block-out roller blind", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.blinds.IkeaTradfriRollerBlinds", + "quirk_class": "zhaquirks.ikea.blinds:IkeaTradfriRollerBlinds", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-inspelning-smart-plug.json b/tests/data/devices/ikea-of-sweden-inspelning-smart-plug.json index 8a5ecd108..2fd7c7830 100644 --- a/tests/data/devices/ikea-of-sweden-inspelning-smart-plug.json +++ b/tests/data/devices/ikea-of-sweden-inspelning-smart-plug.json @@ -8,7 +8,7 @@ "friendly_model": "INSPELNING Smart plug", "name": "IKEA of Sweden INSPELNING Smart plug", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.ikea.plug_g2:(IKEA of Sweden / INSPELNING Smart plug)", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Mains", diff --git a/tests/data/devices/ikea-of-sweden-praktlysing-cellular-blind.json b/tests/data/devices/ikea-of-sweden-praktlysing-cellular-blind.json index f5367d5c1..0526f74d8 100644 --- a/tests/data/devices/ikea-of-sweden-praktlysing-cellular-blind.json +++ b/tests/data/devices/ikea-of-sweden-praktlysing-cellular-blind.json @@ -8,7 +8,7 @@ "friendly_model": "PRAKTLYSING cellular blind", "name": "IKEA of Sweden PRAKTLYSING cellular blind", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.blinds.IkeaTradfriRollerBlinds", + "quirk_class": "zhaquirks.ikea.blinds:IkeaTradfriRollerBlinds", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-remote-control-n2.json b/tests/data/devices/ikea-of-sweden-remote-control-n2.json index 4d7d2de27..29f69c73a 100644 --- a/tests/data/devices/ikea-of-sweden-remote-control-n2.json +++ b/tests/data/devices/ikea-of-sweden-remote-control-n2.json @@ -8,7 +8,7 @@ "friendly_model": "Remote Control N2", "name": "IKEA of Sweden Remote Control N2", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.ikea.fourbtnremote:(IKEA of Sweden / Remote Control N2)", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-rodret-dimmer.json b/tests/data/devices/ikea-of-sweden-rodret-dimmer.json index ec65ee5cb..cff3d417b 100644 --- a/tests/data/devices/ikea-of-sweden-rodret-dimmer.json +++ b/tests/data/devices/ikea-of-sweden-rodret-dimmer.json @@ -8,7 +8,7 @@ "friendly_model": "RODRET Dimmer", "name": "IKEA of Sweden RODRET Dimmer", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.twobtnremote.IkeaRodretRemote2Btn", + "quirk_class": "zhaquirks.ikea.twobtnremote:IkeaRodretRemote2Btn", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-rodret-wireless-dimmer.json b/tests/data/devices/ikea-of-sweden-rodret-wireless-dimmer.json index 1cce57e6b..ea6ebd96f 100644 --- a/tests/data/devices/ikea-of-sweden-rodret-wireless-dimmer.json +++ b/tests/data/devices/ikea-of-sweden-rodret-wireless-dimmer.json @@ -8,7 +8,7 @@ "friendly_model": "RODRET wireless dimmer", "name": "IKEA of Sweden RODRET wireless dimmer", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.twobtnremote.IkeaRodretRemote2BtnNew", + "quirk_class": "zhaquirks.ikea.twobtnremote:IkeaRodretRemote2BtnNew", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-somrig-shortcut-button.json b/tests/data/devices/ikea-of-sweden-somrig-shortcut-button.json index 0fc29e69e..b66e3c972 100644 --- a/tests/data/devices/ikea-of-sweden-somrig-shortcut-button.json +++ b/tests/data/devices/ikea-of-sweden-somrig-shortcut-button.json @@ -8,7 +8,7 @@ "friendly_model": "SOMRIG shortcut button", "name": "IKEA of Sweden SOMRIG shortcut button", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.somrigsmartbtn.IkeaSomrigSmartButton", + "quirk_class": "zhaquirks.ikea.somrigsmartbtn:IkeaSomrigSmartButton", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json index f50cf30ef..671380219 100644 --- a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json +++ b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier-table.json @@ -8,7 +8,7 @@ "friendly_model": "STARKVIND Air purifier table", "name": "IKEA of Sweden STARKVIND Air purifier table", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.starkvind.IkeaSTARKVIND_v2", + "quirk_class": "zhaquirks.ikea.starkvind:IkeaSTARKVIND_v2", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Mains", diff --git a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json index 252bcff00..d23fdb5be 100644 --- a/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json +++ b/tests/data/devices/ikea-of-sweden-starkvind-air-purifier.json @@ -8,7 +8,7 @@ "friendly_model": "STARKVIND Air purifier", "name": "IKEA of Sweden STARKVIND Air purifier", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.starkvind.IkeaSTARKVIND", + "quirk_class": "zhaquirks.ikea.starkvind:IkeaSTARKVIND", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Mains", diff --git a/tests/data/devices/ikea-of-sweden-symfonisk-sound-remote-gen2.json b/tests/data/devices/ikea-of-sweden-symfonisk-sound-remote-gen2.json index 3e1cab9c3..234f94326 100644 --- a/tests/data/devices/ikea-of-sweden-symfonisk-sound-remote-gen2.json +++ b/tests/data/devices/ikea-of-sweden-symfonisk-sound-remote-gen2.json @@ -8,7 +8,7 @@ "friendly_model": "SYMFONISK sound remote gen2", "name": "IKEA of Sweden SYMFONISK sound remote gen2", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.symfonisk2.IkeaSymfoniskGen2v1", + "quirk_class": "zhaquirks.ikea.symfonisk2:IkeaSymfoniskGen2v1", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-control-outlet.json b/tests/data/devices/ikea-of-sweden-tradfri-control-outlet.json index 116b9266c..55d3b4ab9 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-control-outlet.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-control-outlet.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI control outlet", "name": "IKEA of Sweden TRADFRI control outlet", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.ikea.plug:(IKEA of Sweden / TRADFRI control outlet)", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Mains", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-motion-sensor.json b/tests/data/devices/ikea-of-sweden-tradfri-motion-sensor.json index 4a47830ad..1c5b09502 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-motion-sensor.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-motion-sensor.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI motion sensor", "name": "IKEA of Sweden TRADFRI motion sensor", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.motionzha.IkeaTradfriMotionE1745_Var02", + "quirk_class": "zhaquirks.ikea.motionzha:IkeaTradfriMotionE1745_Var02", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-on-off-switch.json b/tests/data/devices/ikea-of-sweden-tradfri-on-off-switch.json index 11d3bbacb..971243923 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-on-off-switch.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-on-off-switch.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI on/off switch", "name": "IKEA of Sweden TRADFRI on/off switch", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.twobtnremote.IkeaTradfriRemote2Btn", + "quirk_class": "zhaquirks.ikea.twobtnremote:IkeaTradfriRemote2Btn", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-open-close-remote.json b/tests/data/devices/ikea-of-sweden-tradfri-open-close-remote.json index d87b2b45c..6f30d834f 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-open-close-remote.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-open-close-remote.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI open/close remote", "name": "IKEA of Sweden TRADFRI open/close remote", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.opencloseremote.IkeaTradfriOpenCloseRemote", + "quirk_class": "zhaquirks.ikea.opencloseremote:IkeaTradfriOpenCloseRemote", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-remote-control.json b/tests/data/devices/ikea-of-sweden-tradfri-remote-control.json index 6018af8e2..992497982 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-remote-control.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-remote-control.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI remote control", "name": "IKEA of Sweden TRADFRI remote control", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.fivebtnremote.IkeaTradfriRemote1", + "quirk_class": "zhaquirks.ikea.fivebtnremote:IkeaTradfriRemote1", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-tradfri-wireless-dimmer.json b/tests/data/devices/ikea-of-sweden-tradfri-wireless-dimmer.json index d6a905ba7..766ce3262 100644 --- a/tests/data/devices/ikea-of-sweden-tradfri-wireless-dimmer.json +++ b/tests/data/devices/ikea-of-sweden-tradfri-wireless-dimmer.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI wireless dimmer", "name": "IKEA of Sweden TRADFRI wireless dimmer", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.dimmer.IkeaDimmer", + "quirk_class": "zhaquirks.ikea.dimmer:IkeaDimmer", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json b/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json index 906233fd7..5580189c8 100644 --- a/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json +++ b/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json @@ -8,7 +8,7 @@ "friendly_model": "VALLHORN Wireless Motion Sensor", "name": "IKEA of Sweden VALLHORN Wireless Motion Sensor", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.ikea.vallhorn:(IKEA of Sweden / VALLHORN Wireless Motion Sensor)", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/innr-sp-120.json b/tests/data/devices/innr-sp-120.json index 889795773..e4ef8d331 100644 --- a/tests/data/devices/innr-sp-120.json +++ b/tests/data/devices/innr-sp-120.json @@ -8,7 +8,7 @@ "friendly_model": "SP 120", "name": "innr SP 120", "quirk_applied": true, - "quirk_class": "zhaquirks.innr.innr_sp120_plug.SP120", + "quirk_class": "zhaquirks.innr.innr_sp120_plug:SP120", "exposes_features": [], "manufacturer_code": 4454, "power_source": "Mains", diff --git a/tests/data/devices/innr-sp-234.json b/tests/data/devices/innr-sp-234.json index f2d0da3e9..ed4958f25 100755 --- a/tests/data/devices/innr-sp-234.json +++ b/tests/data/devices/innr-sp-234.json @@ -8,7 +8,7 @@ "friendly_model": "SP 234", "name": "innr SP 234", "quirk_applied": true, - "quirk_class": "zhaquirks.innr.innr_sp234_plug.SP234", + "quirk_class": "zhaquirks.innr.innr_sp234_plug:SP234", "exposes_features": [], "manufacturer_code": 4454, "power_source": "Mains", diff --git a/tests/data/devices/innr-sp-240.json b/tests/data/devices/innr-sp-240.json index 499692086..47989dd36 100644 --- a/tests/data/devices/innr-sp-240.json +++ b/tests/data/devices/innr-sp-240.json @@ -8,7 +8,7 @@ "friendly_model": "SP 240", "name": "innr SP 240", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.innr.innr_sp240_plug:(innr / SP 240)", "exposes_features": [], "manufacturer_code": 4454, "power_source": "Mains", diff --git a/tests/data/devices/inovelli-vzm30-sn.json b/tests/data/devices/inovelli-vzm30-sn.json index a6ab18b82..caa7ceec6 100644 --- a/tests/data/devices/inovelli-vzm30-sn.json +++ b/tests/data/devices/inovelli-vzm30-sn.json @@ -8,7 +8,7 @@ "friendly_model": "VZM30-SN", "name": "Inovelli VZM30-SN", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.inovelli.VZM30SN:(Inovelli / VZM30-SN)", "exposes_features": [], "manufacturer_code": 4655, "power_source": "Mains", diff --git a/tests/data/devices/inovelli-vzm31-sn.json b/tests/data/devices/inovelli-vzm31-sn.json index 4478d9fb5..b4966d36c 100644 --- a/tests/data/devices/inovelli-vzm31-sn.json +++ b/tests/data/devices/inovelli-vzm31-sn.json @@ -8,7 +8,7 @@ "friendly_model": "VZM31-SN", "name": "Inovelli VZM31-SN", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.inovelli.VZM31SN:(Inovelli / VZM31-SN)", "exposes_features": [], "manufacturer_code": 4655, "power_source": "Mains", diff --git a/tests/data/devices/inovelli-vzm35-sn.json b/tests/data/devices/inovelli-vzm35-sn.json index b1335f3ab..701127f07 100644 --- a/tests/data/devices/inovelli-vzm35-sn.json +++ b/tests/data/devices/inovelli-vzm35-sn.json @@ -8,7 +8,7 @@ "friendly_model": "VZM35-SN", "name": "Inovelli VZM35-SN", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.inovelli.VZM35SN:(Inovelli / VZM35-SN)", "exposes_features": [], "manufacturer_code": 4655, "power_source": "Mains", diff --git a/tests/data/devices/ke-tradfri-open-close-remote.json b/tests/data/devices/ke-tradfri-open-close-remote.json index ee12a939f..4ddcea7b6 100644 --- a/tests/data/devices/ke-tradfri-open-close-remote.json +++ b/tests/data/devices/ke-tradfri-open-close-remote.json @@ -8,7 +8,7 @@ "friendly_model": "TRADFRI open/close remote", "name": "\u0002KE TRADFRI open/close remote", "quirk_applied": true, - "quirk_class": "zhaquirks.ikea.opencloseremote.IkeaTradfriOpenCloseRemote", + "quirk_class": "zhaquirks.ikea.opencloseremote:IkeaTradfriOpenCloseRemote", "exposes_features": [], "manufacturer_code": 4476, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/keen-home-inc-sv01-410-mp-1-1.json b/tests/data/devices/keen-home-inc-sv01-410-mp-1-1.json index f48a8e5a6..49768c08b 100644 --- a/tests/data/devices/keen-home-inc-sv01-410-mp-1-1.json +++ b/tests/data/devices/keen-home-inc-sv01-410-mp-1-1.json @@ -8,7 +8,7 @@ "friendly_model": "SV01-410-MP-1.1", "name": "Keen Home Inc SV01-410-MP-1.1", "quirk_applied": true, - "quirk_class": "zhaquirks.keenhome.sv02612mp13.KeenHomeSmartVent", + "quirk_class": "zhaquirks.keenhome.sv02612mp13:KeenHomeSmartVent", "exposes_features": [], "manufacturer_code": 4443, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/keen-home-inc-sv02-610-mp-1-3.json b/tests/data/devices/keen-home-inc-sv02-610-mp-1-3.json index 60645e422..469d131a7 100644 --- a/tests/data/devices/keen-home-inc-sv02-610-mp-1-3.json +++ b/tests/data/devices/keen-home-inc-sv02-610-mp-1-3.json @@ -8,7 +8,7 @@ "friendly_model": "SV02-610-MP-1.3", "name": "Keen Home Inc SV02-610-MP-1.3", "quirk_applied": true, - "quirk_class": "zhaquirks.keenhome.sv02612mp13.KeenHomeSmartVent", + "quirk_class": "zhaquirks.keenhome.sv02612mp13:KeenHomeSmartVent", "exposes_features": [], "manufacturer_code": 4443, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/keen-home-inc-sv02-612-mp-1-3.json b/tests/data/devices/keen-home-inc-sv02-612-mp-1-3.json index 113c646a4..e34891672 100644 --- a/tests/data/devices/keen-home-inc-sv02-612-mp-1-3.json +++ b/tests/data/devices/keen-home-inc-sv02-612-mp-1-3.json @@ -8,7 +8,7 @@ "friendly_model": "SV02-612-MP-1.3", "name": "Keen Home Inc SV02-612-MP-1.3", "quirk_applied": true, - "quirk_class": "zhaquirks.keenhome.sv02612mp13.KeenHomeSmartVent", + "quirk_class": "zhaquirks.keenhome.sv02612mp13:KeenHomeSmartVent", "exposes_features": [], "manufacturer_code": 4443, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/king-of-fans-inc-hbuniversalcfremote.json b/tests/data/devices/king-of-fans-inc-hbuniversalcfremote.json index 89f420ea0..43ba655bb 100644 --- a/tests/data/devices/king-of-fans-inc-hbuniversalcfremote.json +++ b/tests/data/devices/king-of-fans-inc-hbuniversalcfremote.json @@ -8,7 +8,7 @@ "friendly_model": "HBUniversalCFRemote", "name": "King Of Fans, Inc. HBUniversalCFRemote", "quirk_applied": true, - "quirk_class": "zhaquirks.kof.kof_mr101z.CeilingFan", + "quirk_class": "zhaquirks.kof.kof_mr101z:CeilingFan", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/king-of-fans-inc-hdc52eastwindfan.json b/tests/data/devices/king-of-fans-inc-hdc52eastwindfan.json index 9b192d211..a5ab82bde 100644 --- a/tests/data/devices/king-of-fans-inc-hdc52eastwindfan.json +++ b/tests/data/devices/king-of-fans-inc-hdc52eastwindfan.json @@ -8,7 +8,7 @@ "friendly_model": "HDC52EastwindFan", "name": "King Of Fans, Inc. HDC52EastwindFan", "quirk_applied": true, - "quirk_class": "zhaquirks.kof.kof_mr101z.CeilingFan", + "quirk_class": "zhaquirks.kof.kof_mr101z:CeilingFan", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/konke-3afe140103020000.json b/tests/data/devices/konke-3afe140103020000.json index b3d6bac3a..f64e8c1fc 100644 --- a/tests/data/devices/konke-3afe140103020000.json +++ b/tests/data/devices/konke-3afe140103020000.json @@ -8,7 +8,7 @@ "friendly_model": "3AFE140103020000", "name": "Konke 3AFE140103020000", "quirk_applied": true, - "quirk_class": "zhaquirks.konke.temp.KonkeTempHumidity", + "quirk_class": "zhaquirks.konke.temp:KonkeTempHumidity", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/konke-3afe220103020000.json b/tests/data/devices/konke-3afe220103020000.json index 182bb5fab..b0e4ad0cd 100644 --- a/tests/data/devices/konke-3afe220103020000.json +++ b/tests/data/devices/konke-3afe220103020000.json @@ -8,7 +8,7 @@ "friendly_model": "3AFE220103020000", "name": "Konke 3AFE220103020000", "quirk_applied": true, - "quirk_class": "zhaquirks.konke.temp.KonkeTempHumidity", + "quirk_class": "zhaquirks.konke.temp:KonkeTempHumidity", "exposes_features": [], "manufacturer_code": 4712, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/konke-3afe270104020015.json b/tests/data/devices/konke-3afe270104020015.json index 3e12de8fd..ae99b83b3 100644 --- a/tests/data/devices/konke-3afe270104020015.json +++ b/tests/data/devices/konke-3afe270104020015.json @@ -8,7 +8,7 @@ "friendly_model": "3AFE270104020015", "name": "Konke 3AFE270104020015", "quirk_applied": true, - "quirk_class": "zhaquirks.konke.magnet.KonkeMagnet", + "quirk_class": "zhaquirks.konke.magnet:KonkeMagnet", "exposes_features": [], "manufacturer_code": 4712, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/konke-3afe280100510001.json b/tests/data/devices/konke-3afe280100510001.json index 87b5169ac..5a9c783d6 100644 --- a/tests/data/devices/konke-3afe280100510001.json +++ b/tests/data/devices/konke-3afe280100510001.json @@ -8,7 +8,7 @@ "friendly_model": "3AFE280100510001", "name": "Konke 3AFE280100510001", "quirk_applied": true, - "quirk_class": "zhaquirks.konke.button.KonkeButtonRemote1", + "quirk_class": "zhaquirks.konke.button:KonkeButtonRemote1", "exposes_features": [ "konke.button_remote" ], diff --git a/tests/data/devices/konke-3afe28010402000d.json b/tests/data/devices/konke-3afe28010402000d.json index ad6f1ced9..067b5cb37 100755 --- a/tests/data/devices/konke-3afe28010402000d.json +++ b/tests/data/devices/konke-3afe28010402000d.json @@ -8,7 +8,7 @@ "friendly_model": "3AFE28010402000D", "name": "Konke 3AFE28010402000D", "quirk_applied": true, - "quirk_class": "zhaquirks.konke.motion.KonkeMotion", + "quirk_class": "zhaquirks.konke.motion:KonkeMotion", "exposes_features": [], "manufacturer_code": 4712, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lds-zbt-cctswitch-d0001.json b/tests/data/devices/lds-zbt-cctswitch-d0001.json index dd2fd903f..37e71fdcf 100644 --- a/tests/data/devices/lds-zbt-cctswitch-d0001.json +++ b/tests/data/devices/lds-zbt-cctswitch-d0001.json @@ -8,7 +8,7 @@ "friendly_model": "ZBT-CCTSwitch-D0001", "name": "LDS ZBT-CCTSwitch-D0001", "quirk_applied": true, - "quirk_class": "zhaquirks.lds.cctswitch.CCTSwitch", + "quirk_class": "zhaquirks.lds.cctswitch:CCTSwitch", "exposes_features": [], "manufacturer_code": 4456, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ledvance-flex-rgbw.json b/tests/data/devices/ledvance-flex-rgbw.json index a501e1b86..a6784318a 100755 --- a/tests/data/devices/ledvance-flex-rgbw.json +++ b/tests/data/devices/ledvance-flex-rgbw.json @@ -8,7 +8,7 @@ "friendly_model": "FLEX RGBW", "name": "LEDVANCE FLEX RGBW", "quirk_applied": true, - "quirk_class": "zhaquirks.ledvance.flexrgbw.FlexRGBW", + "quirk_class": "zhaquirks.ledvance.flexrgbw:FlexRGBW", "exposes_features": [], "manufacturer_code": 4489, "power_source": "Mains", diff --git a/tests/data/devices/legrand-dimmer-switch-w-o-neutral.json b/tests/data/devices/legrand-dimmer-switch-w-o-neutral.json index 2968999d6..bbc29ba55 100644 --- a/tests/data/devices/legrand-dimmer-switch-w-o-neutral.json +++ b/tests/data/devices/legrand-dimmer-switch-w-o-neutral.json @@ -8,7 +8,7 @@ "friendly_model": " Dimmer switch w/o neutral", "name": " Legrand Dimmer switch w/o neutral", "quirk_applied": true, - "quirk_class": "zhaquirks.legrand.dimmer.DimmerWithoutNeutralAndBallast", + "quirk_class": "zhaquirks.legrand.dimmer:DimmerWithoutNeutralAndBallast", "exposes_features": [], "manufacturer_code": 4129, "power_source": "Mains", diff --git a/tests/data/devices/legrand-double-gangs-remote-switch.json b/tests/data/devices/legrand-double-gangs-remote-switch.json index 992b04ebc..8688f9caa 100644 --- a/tests/data/devices/legrand-double-gangs-remote-switch.json +++ b/tests/data/devices/legrand-double-gangs-remote-switch.json @@ -8,7 +8,7 @@ "friendly_model": " Double gangs remote switch", "name": " Legrand Double gangs remote switch", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.legrand.wirelessswitch:( Legrand / Double gangs remote switch)", "exposes_features": [], "manufacturer_code": 4129, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/legrand-light-switch-with-neutral.json b/tests/data/devices/legrand-light-switch-with-neutral.json index 060ce5d46..4a90c7089 100644 --- a/tests/data/devices/legrand-light-switch-with-neutral.json +++ b/tests/data/devices/legrand-light-switch-with-neutral.json @@ -8,7 +8,7 @@ "friendly_model": " Light switch with neutral", "name": " Legrand Light switch with neutral", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.legrand.switch:( Legrand / Light switch with neutral)", "exposes_features": [], "manufacturer_code": 4129, "power_source": "Mains", diff --git a/tests/data/devices/lixee-zlinky-tic.json b/tests/data/devices/lixee-zlinky-tic.json index b76eac92a..89bc97771 100644 --- a/tests/data/devices/lixee-zlinky-tic.json +++ b/tests/data/devices/lixee-zlinky-tic.json @@ -8,7 +8,7 @@ "friendly_model": "ZLinky_TIC", "name": "LiXee ZLinky_TIC", "quirk_applied": true, - "quirk_class": "zhaquirks.lixee.zlinky.ZLinkyTICFWV12", + "quirk_class": "zhaquirks.lixee.zlinky:ZLinkyTICFWV12", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Mains", diff --git a/tests/data/devices/lumi-lumi-airmonitor-acn01.json b/tests/data/devices/lumi-lumi-airmonitor-acn01.json index 54465fc1a..d1cb0caef 100644 --- a/tests/data/devices/lumi-lumi-airmonitor-acn01.json +++ b/tests/data/devices/lumi-lumi-airmonitor-acn01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.airmonitor.acn01", "name": "LUMI lumi.airmonitor.acn01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.tvoc.TVOCMonitor", + "quirk_class": "zhaquirks.xiaomi.aqara.tvoc:TVOCMonitor", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-airrtc-agl001.json b/tests/data/devices/lumi-lumi-airrtc-agl001.json index 7dcef4d2b..b56583389 100644 --- a/tests/data/devices/lumi-lumi-airrtc-agl001.json +++ b/tests/data/devices/lumi-lumi-airrtc-agl001.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.airrtc.agl001", "name": "LUMI lumi.airrtc.agl001", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.thermostat_agl001.AGL001", + "quirk_class": "zhaquirks.xiaomi.aqara.thermostat_agl001:AGL001", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-curtain-acn002.json b/tests/data/devices/lumi-lumi-curtain-acn002.json index 5d6daf6af..7f995e71d 100644 --- a/tests/data/devices/lumi-lumi-curtain-acn002.json +++ b/tests/data/devices/lumi-lumi-curtain-acn002.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.curtain.acn002", "name": "LUMI lumi.curtain.acn002", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.xiaomi.aqara.roller_curtain_e1:(LUMI / lumi.curtain.acn002)", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-curtain-agl001.json b/tests/data/devices/lumi-lumi-curtain-agl001.json index c101bb9fc..5af8f6ff3 100644 --- a/tests/data/devices/lumi-lumi-curtain-agl001.json +++ b/tests/data/devices/lumi-lumi-curtain-agl001.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.curtain.agl001", "name": "LUMI lumi.curtain.agl001", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.driver_curtain_e1.DriverE1", + "quirk_class": "zhaquirks.xiaomi.aqara.driver_curtain_e1:DriverE1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-flood-acn001.json b/tests/data/devices/lumi-lumi-flood-acn001.json index 5f27a2bc5..9f0710cf2 100644 --- a/tests/data/devices/lumi-lumi-flood-acn001.json +++ b/tests/data/devices/lumi-lumi-flood-acn001.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.flood.acn001", "name": "LUMI lumi.flood.acn001", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.water_acn001.WaterE1", + "quirk_class": "zhaquirks.xiaomi.aqara.water_acn001:WaterE1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-flood-agl02.json b/tests/data/devices/lumi-lumi-flood-agl02.json index 581fbda9d..e6480ed92 100644 --- a/tests/data/devices/lumi-lumi-flood-agl02.json +++ b/tests/data/devices/lumi-lumi-flood-agl02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.flood.agl02", "name": "LUMI lumi.flood.agl02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.water_agl02.WaterT1", + "quirk_class": "zhaquirks.xiaomi.aqara.water_agl02:WaterT1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-magnet-ac01.json b/tests/data/devices/lumi-lumi-magnet-ac01.json index 313732fce..568afa736 100644 --- a/tests/data/devices/lumi-lumi-magnet-ac01.json +++ b/tests/data/devices/lumi-lumi-magnet-ac01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.magnet.ac01", "name": "LUMI lumi.magnet.ac01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.magnet_ac01.LumiMagnetAC01", + "quirk_class": "zhaquirks.xiaomi.aqara.magnet_ac01:LumiMagnetAC01", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-magnet-acn001.json b/tests/data/devices/lumi-lumi-magnet-acn001.json index fc4385b8f..2994845c8 100644 --- a/tests/data/devices/lumi-lumi-magnet-acn001.json +++ b/tests/data/devices/lumi-lumi-magnet-acn001.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.magnet.acn001", "name": "LUMI lumi.magnet.acn001", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.magnet_acn001.MagnetE1", + "quirk_class": "zhaquirks.xiaomi.aqara.magnet_acn001:MagnetE1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-magnet-agl02.json b/tests/data/devices/lumi-lumi-magnet-agl02.json index bd6819747..f93e735fc 100644 --- a/tests/data/devices/lumi-lumi-magnet-agl02.json +++ b/tests/data/devices/lumi-lumi-magnet-agl02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.magnet.agl02", "name": "LUMI lumi.magnet.agl02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.magnet_agl02.MagnetT1", + "quirk_class": "zhaquirks.xiaomi.aqara.magnet_agl02:MagnetT1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-motion-ac02.json b/tests/data/devices/lumi-lumi-motion-ac02.json index 99880a576..5f065252c 100755 --- a/tests/data/devices/lumi-lumi-motion-ac02.json +++ b/tests/data/devices/lumi-lumi-motion-ac02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.motion.ac02", "name": "LUMI lumi.motion.ac02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.motion_ac02.LumiMotionAC02", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_ac02:LumiMotionAC02", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-motion-agl02.json b/tests/data/devices/lumi-lumi-motion-agl02.json index 718e56cd5..dee9d9740 100644 --- a/tests/data/devices/lumi-lumi-motion-agl02.json +++ b/tests/data/devices/lumi-lumi-motion-agl02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.motion.agl02", "name": "LUMI lumi.motion.agl02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.motion_agl02.MotionT1", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_agl02:MotionT1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-motion-agl04.json b/tests/data/devices/lumi-lumi-motion-agl04.json index 1e9be9df1..331a95bcf 100644 --- a/tests/data/devices/lumi-lumi-motion-agl04.json +++ b/tests/data/devices/lumi-lumi-motion-agl04.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.motion.agl04", "name": "LUMI lumi.motion.agl04", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.motion_agl04.LumiLumiMotionAgl04", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_agl04:LumiLumiMotionAgl04", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-plug-maus01.json b/tests/data/devices/lumi-lumi-plug-maus01.json index 6c7cfac73..93ee8cdf8 100644 --- a/tests/data/devices/lumi-lumi-plug-maus01.json +++ b/tests/data/devices/lumi-lumi-plug-maus01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.plug.maus01", "name": "LUMI lumi.plug.maus01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.plug_maus01.Plug", + "quirk_class": "zhaquirks.xiaomi.aqara.plug_maus01:Plug", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Mains", diff --git a/tests/data/devices/lumi-lumi-relay-c2acn01.json b/tests/data/devices/lumi-lumi-relay-c2acn01.json index 9ee666ac4..57b4c1848 100644 --- a/tests/data/devices/lumi-lumi-relay-c2acn01.json +++ b/tests/data/devices/lumi-lumi-relay-c2acn01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.relay.c2acn01", "name": "LUMI lumi.relay.c2acn01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.relay_c2acn01.Relay", + "quirk_class": "zhaquirks.xiaomi.aqara.relay_c2acn01:Relay", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Mains", diff --git a/tests/data/devices/lumi-lumi-remote-b286opcn01.json b/tests/data/devices/lumi-lumi-remote-b286opcn01.json index 6949d8da3..7bf98d9d6 100644 --- a/tests/data/devices/lumi-lumi-remote-b286opcn01.json +++ b/tests/data/devices/lumi-lumi-remote-b286opcn01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.remote.b286opcn01", "name": "LUMI lumi.remote.b286opcn01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote.RemoteB286OPCN01V4", + "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote:RemoteB286OPCN01V4", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-remote-b28ac1.json b/tests/data/devices/lumi-lumi-remote-b28ac1.json index 657a185c3..045c47ff5 100644 --- a/tests/data/devices/lumi-lumi-remote-b28ac1.json +++ b/tests/data/devices/lumi-lumi-remote-b28ac1.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.remote.b28ac1", "name": "LUMI lumi.remote.b28ac1", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.xiaomi.aqara.remote_h1:(LUMI / lumi.remote.b28ac1)", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-remote-b486opcn01.json b/tests/data/devices/lumi-lumi-remote-b486opcn01.json index 3fb521fba..d60a99559 100755 --- a/tests/data/devices/lumi-lumi-remote-b486opcn01.json +++ b/tests/data/devices/lumi-lumi-remote-b486opcn01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.remote.b486opcn01", "name": "LUMI lumi.remote.b486opcn01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote.RemoteB486OPCN01V2", + "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote:RemoteB486OPCN01V2", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-remote-b686opcn01.json b/tests/data/devices/lumi-lumi-remote-b686opcn01.json index 2ec715c0d..ebbd25661 100644 --- a/tests/data/devices/lumi-lumi-remote-b686opcn01.json +++ b/tests/data/devices/lumi-lumi-remote-b686opcn01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.remote.b686opcn01", "name": "LUMI lumi.remote.b686opcn01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote.RemoteB686OPCN01V3", + "quirk_class": "zhaquirks.xiaomi.aqara.opple_remote:RemoteB686OPCN01V3", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-remote-cagl02.json b/tests/data/devices/lumi-lumi-remote-cagl02.json index 8ea6f90f2..1362872eb 100644 --- a/tests/data/devices/lumi-lumi-remote-cagl02.json +++ b/tests/data/devices/lumi-lumi-remote-cagl02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.remote.cagl02", "name": "LUMI lumi.remote.cagl02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.cube_aqgl01.CubeCAGL02", + "quirk_class": "zhaquirks.xiaomi.aqara.cube_aqgl01:CubeCAGL02", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sen-ill-agl01.json b/tests/data/devices/lumi-lumi-sen-ill-agl01.json index 97acd3a79..99074e03d 100644 --- a/tests/data/devices/lumi-lumi-sen-ill-agl01.json +++ b/tests/data/devices/lumi-lumi-sen-ill-agl01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sen_ill.agl01", "name": "LUMI lumi.sen_ill.agl01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.illumination.IlluminationT1", + "quirk_class": "zhaquirks.xiaomi.aqara.illumination:IlluminationT1", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sen-ill-mgl01.json b/tests/data/devices/lumi-lumi-sen-ill-mgl01.json index 09253b590..8080c49f3 100644 --- a/tests/data/devices/lumi-lumi-sen-ill-mgl01.json +++ b/tests/data/devices/lumi-lumi-sen-ill-mgl01.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sen_ill.mgl01", "name": "LUMI lumi.sen_ill.mgl01", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.illumination.Illumination", + "quirk_class": "zhaquirks.xiaomi.aqara.illumination:Illumination", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-86sw2.json b/tests/data/devices/lumi-lumi-sensor-86sw2.json index 84e91e4b1..d7ae0538b 100644 --- a/tests/data/devices/lumi-lumi-sensor-86sw2.json +++ b/tests/data/devices/lumi-lumi-sensor-86sw2.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_86sw2", "name": "LUMI lumi.sensor_86sw2", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.remote_b286acn01.RemoteB286ACN01", + "quirk_class": "zhaquirks.xiaomi.aqara.remote_b286acn01:RemoteB286ACN01", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-ht-agl02.json b/tests/data/devices/lumi-lumi-sensor-ht-agl02.json index 27f2913f1..c17093e0b 100644 --- a/tests/data/devices/lumi-lumi-sensor-ht-agl02.json +++ b/tests/data/devices/lumi-lumi-sensor-ht-agl02.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_ht.agl02", "name": "LUMI lumi.sensor_ht.agl02", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.sensor_ht_agl02.LumiSensorHtAgl02", + "quirk_class": "zhaquirks.xiaomi.aqara.sensor_ht_agl02:LumiSensorHtAgl02", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-magnet-aq2.json b/tests/data/devices/lumi-lumi-sensor-magnet-aq2.json index f6648d463..5fc0cd1b4 100644 --- a/tests/data/devices/lumi-lumi-sensor-magnet-aq2.json +++ b/tests/data/devices/lumi-lumi-sensor-magnet-aq2.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_magnet.aq2", "name": "LUMI lumi.sensor_magnet.aq2", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.magnet_aq2.MagnetAQ2", + "quirk_class": "zhaquirks.xiaomi.aqara.magnet_aq2:MagnetAQ2", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-motion-aq2.json b/tests/data/devices/lumi-lumi-sensor-motion-aq2.json index 5dd4ab9a3..35c81cedc 100644 --- a/tests/data/devices/lumi-lumi-sensor-motion-aq2.json +++ b/tests/data/devices/lumi-lumi-sensor-motion-aq2.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_motion.aq2", "name": "LUMI lumi.sensor_motion.aq2", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.motion_aq2.MotionAQ2", + "quirk_class": "zhaquirks.xiaomi.aqara.motion_aq2:MotionAQ2", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-smoke-acn03.json b/tests/data/devices/lumi-lumi-sensor-smoke-acn03.json index c0f954f40..79bbf93e2 100644 --- a/tests/data/devices/lumi-lumi-sensor-smoke-acn03.json +++ b/tests/data/devices/lumi-lumi-sensor-smoke-acn03.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_smoke.acn03", "name": "LUMI lumi.sensor_smoke.acn03", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.smoke.LumiSensorSmokeAcn03", + "quirk_class": "zhaquirks.xiaomi.aqara.smoke:LumiSensorSmokeAcn03", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-smoke.json b/tests/data/devices/lumi-lumi-sensor-smoke.json index f147d73fc..66a1fbf56 100644 --- a/tests/data/devices/lumi-lumi-sensor-smoke.json +++ b/tests/data/devices/lumi-lumi-sensor-smoke.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_smoke", "name": "LUMI lumi.sensor_smoke", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.mija.smoke.MijiaHoneywellSmokeDetectorSensor", + "quirk_class": "zhaquirks.xiaomi.mija.smoke:MijiaHoneywellSmokeDetectorSensor", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-switch-aq2.json b/tests/data/devices/lumi-lumi-sensor-switch-aq2.json index 77523b418..704165b82 100644 --- a/tests/data/devices/lumi-lumi-sensor-switch-aq2.json +++ b/tests/data/devices/lumi-lumi-sensor-switch-aq2.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_switch.aq2", "name": "LUMI lumi.sensor_switch.aq2", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.switch_aq2.SwitchAQ2", + "quirk_class": "zhaquirks.xiaomi.aqara.switch_aq2:SwitchAQ2", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-switch-aq3.json b/tests/data/devices/lumi-lumi-sensor-switch-aq3.json index 72cd9a29e..3f5fa08ad 100644 --- a/tests/data/devices/lumi-lumi-sensor-switch-aq3.json +++ b/tests/data/devices/lumi-lumi-sensor-switch-aq3.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_switch.aq3", "name": "LUMI lumi.sensor_switch.aq3", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.sensor_switch_aq3.SwitchAQ3", + "quirk_class": "zhaquirks.xiaomi.aqara.sensor_switch_aq3:SwitchAQ3", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-sensor-switch.json b/tests/data/devices/lumi-lumi-sensor-switch.json index 3a389939b..ceda96e25 100644 --- a/tests/data/devices/lumi-lumi-sensor-switch.json +++ b/tests/data/devices/lumi-lumi-sensor-switch.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.sensor_switch", "name": "LUMI lumi.sensor_switch", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.mija.sensor_switch.MijaButton", + "quirk_class": "zhaquirks.xiaomi.mija.sensor_switch:MijaButton", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lumi-lumi-switch-n0agl1.json b/tests/data/devices/lumi-lumi-switch-n0agl1.json index 8778f930f..dc13b9a23 100644 --- a/tests/data/devices/lumi-lumi-switch-n0agl1.json +++ b/tests/data/devices/lumi-lumi-switch-n0agl1.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.switch.n0agl1", "name": "LUMI lumi.switch.n0agl1", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.switch_t1.SwitchT1Alt3", + "quirk_class": "zhaquirks.xiaomi.aqara.switch_t1:SwitchT1Alt3", "exposes_features": [], "manufacturer_code": 4447, "power_source": "Mains", diff --git a/tests/data/devices/lumi-lumi-vibration-aq1.json b/tests/data/devices/lumi-lumi-vibration-aq1.json index 56a90ba52..80a5d9321 100644 --- a/tests/data/devices/lumi-lumi-vibration-aq1.json +++ b/tests/data/devices/lumi-lumi-vibration-aq1.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.vibration.aq1", "name": "LUMI lumi.vibration.aq1", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.vibration_aq1.VibrationAQ1", + "quirk_class": "zhaquirks.xiaomi.aqara.vibration_aq1:VibrationAQ1", "exposes_features": [ "xiaomi.aqara_vibration_aq1" ], diff --git a/tests/data/devices/lumi-lumi-weather.json b/tests/data/devices/lumi-lumi-weather.json index bd4347f77..d27e16621 100755 --- a/tests/data/devices/lumi-lumi-weather.json +++ b/tests/data/devices/lumi-lumi-weather.json @@ -8,7 +8,7 @@ "friendly_model": "lumi.weather", "name": "LUMI lumi.weather", "quirk_applied": true, - "quirk_class": "zhaquirks.xiaomi.aqara.weather.Weather", + "quirk_class": "zhaquirks.xiaomi.aqara.weather:Weather", "exposes_features": [], "manufacturer_code": 4151, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/lutron-lzl4bwhl01-remote.json b/tests/data/devices/lutron-lzl4bwhl01-remote.json index 31730042a..d8f88c23a 100644 --- a/tests/data/devices/lutron-lzl4bwhl01-remote.json +++ b/tests/data/devices/lutron-lzl4bwhl01-remote.json @@ -8,7 +8,7 @@ "friendly_model": "LZL4BWHL01 Remote", "name": " Lutron LZL4BWHL01 Remote", "quirk_applied": true, - "quirk_class": "zhaquirks.lutron.lzl4bwhl01remote.LutronLZL4BWHL01Remote", + "quirk_class": "zhaquirks.lutron.lzl4bwhl01remote:LutronLZL4BWHL01Remote", "exposes_features": [], "manufacturer_code": 4420, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/mli-tint-extendedcolor.json b/tests/data/devices/mli-tint-extendedcolor.json index d79d8fee2..ed23f007d 100644 --- a/tests/data/devices/mli-tint-extendedcolor.json +++ b/tests/data/devices/mli-tint-extendedcolor.json @@ -8,7 +8,7 @@ "friendly_model": "tint-ExtendedColor", "name": "MLI tint-ExtendedColor", "quirk_applied": true, - "quirk_class": "zhaquirks.mli.tintE14rgbcct.TintRGBCCTLight", + "quirk_class": "zhaquirks.mli.tintE14rgbcct:TintRGBCCTLight", "exposes_features": [], "manufacturer_code": 4635, "power_source": "Mains", diff --git a/tests/data/devices/mli-zbt-remote-all-rgbw.json b/tests/data/devices/mli-zbt-remote-all-rgbw.json index b6b4cf314..54a36a0ce 100644 --- a/tests/data/devices/mli-zbt-remote-all-rgbw.json +++ b/tests/data/devices/mli-zbt-remote-all-rgbw.json @@ -8,7 +8,7 @@ "friendly_model": "ZBT-Remote-ALL-RGBW", "name": "MLI ZBT-Remote-ALL-RGBW", "quirk_applied": true, - "quirk_class": "zhaquirks.mli.tint.TintRemote", + "quirk_class": "zhaquirks.mli.tint:TintRemote", "exposes_features": [], "manufacturer_code": 4635, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/nodon-sin-4-fp-21.json b/tests/data/devices/nodon-sin-4-fp-21.json index e4a845682..cfe3814e8 100644 --- a/tests/data/devices/nodon-sin-4-fp-21.json +++ b/tests/data/devices/nodon-sin-4-fp-21.json @@ -8,7 +8,7 @@ "friendly_model": "SIN-4-FP-21", "name": "NodOn SIN-4-FP-21", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.nodon.pilot_wire:(NodOn / SIN-4-FP-21)", "exposes_features": [], "manufacturer_code": 4747, "power_source": "Mains", diff --git a/tests/data/devices/nodon-sin-4-rs-20.json b/tests/data/devices/nodon-sin-4-rs-20.json index ba88a1c79..09cf74bfa 100644 --- a/tests/data/devices/nodon-sin-4-rs-20.json +++ b/tests/data/devices/nodon-sin-4-rs-20.json @@ -8,7 +8,7 @@ "friendly_model": "SIN-4-RS-20", "name": "NodOn SIN-4-RS-20", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.nodon.roller_shutter:(NodOn / SIN-4-RS-20)", "exposes_features": [], "manufacturer_code": 4747, "power_source": "Mains", diff --git a/tests/data/devices/osram-switch-4x-lightify.json b/tests/data/devices/osram-switch-4x-lightify.json index b15eb664b..73f2bfbca 100644 --- a/tests/data/devices/osram-switch-4x-lightify.json +++ b/tests/data/devices/osram-switch-4x-lightify.json @@ -8,7 +8,7 @@ "friendly_model": "Switch 4x-LIGHTIFY", "name": "OSRAM Switch 4x-LIGHTIFY", "quirk_applied": true, - "quirk_class": "zhaquirks.osram.lightifyx4.LightifyX4", + "quirk_class": "zhaquirks.osram.lightifyx4:LightifyX4", "exposes_features": [], "manufacturer_code": 4364, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/paulmann-licht-gmbh-501-34.json b/tests/data/devices/paulmann-licht-gmbh-501-34.json index 7fccaeefd..2e78378aa 100644 --- a/tests/data/devices/paulmann-licht-gmbh-501-34.json +++ b/tests/data/devices/paulmann-licht-gmbh-501-34.json @@ -8,7 +8,7 @@ "friendly_model": "501.34", "name": "Paulmann Licht GmbH 501.34", "quirk_applied": true, - "quirk_class": "zhaquirks.paulmann.fourbtnremote.PaulmannRemote4Btn", + "quirk_class": "zhaquirks.paulmann.fourbtnremote:PaulmannRemote4Btn", "exposes_features": [], "manufacturer_code": 4644, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/philips-7602031u7.json b/tests/data/devices/philips-7602031u7.json index 20d31867c..00d24c613 100644 --- a/tests/data/devices/philips-7602031u7.json +++ b/tests/data/devices/philips-7602031u7.json @@ -8,7 +8,7 @@ "friendly_model": "Hue Go", "name": "Philips Hue Go", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.philips.hue_light:(Philips / 7602031P7)", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Mains", diff --git a/tests/data/devices/philips-rom001.json b/tests/data/devices/philips-rom001.json index c7066ebc8..4433e8bda 100644 --- a/tests/data/devices/philips-rom001.json +++ b/tests/data/devices/philips-rom001.json @@ -8,7 +8,7 @@ "friendly_model": "ROM001", "name": "Philips ROM001", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rom001.PhilipsROM001", + "quirk_class": "zhaquirks.philips.rom001:PhilipsROM001", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/philips-rwl020.json b/tests/data/devices/philips-rwl020.json index 1e5f84aa2..0f530b272 100644 --- a/tests/data/devices/philips-rwl020.json +++ b/tests/data/devices/philips-rwl020.json @@ -8,7 +8,7 @@ "friendly_model": "RWL020", "name": "Philips RWL020", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rwlfirstgen.PhilipsRWLFirstGen", + "quirk_class": "zhaquirks.philips.rwlfirstgen:PhilipsRWLFirstGen", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/philips-rwl021.json b/tests/data/devices/philips-rwl021.json index 0bddddd2e..388453828 100644 --- a/tests/data/devices/philips-rwl021.json +++ b/tests/data/devices/philips-rwl021.json @@ -8,7 +8,7 @@ "friendly_model": "RWL021", "name": "Philips RWL021", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rwlfirstgen.PhilipsRWLFirstGen", + "quirk_class": "zhaquirks.philips.rwlfirstgen:PhilipsRWLFirstGen", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/philips-sml001.json b/tests/data/devices/philips-sml001.json index 5b9bcf714..0151853ce 100644 --- a/tests/data/devices/philips-sml001.json +++ b/tests/data/devices/philips-sml001.json @@ -8,7 +8,7 @@ "friendly_model": "SML001", "name": "Philips SML001", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.motion.PhilipsMotion", + "quirk_class": "zhaquirks.philips.motion:PhilipsMotion", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/plaid-systems-ps-sprzms-slp3.json b/tests/data/devices/plaid-systems-ps-sprzms-slp3.json index 01e65bbfd..d7c0237aa 100644 --- a/tests/data/devices/plaid-systems-ps-sprzms-slp3.json +++ b/tests/data/devices/plaid-systems-ps-sprzms-slp3.json @@ -8,7 +8,7 @@ "friendly_model": "PS-SPRZMS-SLP3", "name": "PLAID SYSTEMS PS-SPRZMS-SLP3", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.plaid.soil:(PLAID SYSTEMS / PS-SPRZMS-SLP3)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/samjin-button.json b/tests/data/devices/samjin-button.json index 654fde90e..c0fd5b3de 100644 --- a/tests/data/devices/samjin-button.json +++ b/tests/data/devices/samjin-button.json @@ -8,7 +8,7 @@ "friendly_model": "button", "name": "Samjin button", "quirk_applied": true, - "quirk_class": "zhaquirks.samjin.button.SamjinButton", + "quirk_class": "zhaquirks.samjin.button:SamjinButton", "exposes_features": [], "manufacturer_code": 4673, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/samjin-multi.json b/tests/data/devices/samjin-multi.json index dde7d2d93..c227dcd8b 100644 --- a/tests/data/devices/samjin-multi.json +++ b/tests/data/devices/samjin-multi.json @@ -8,7 +8,7 @@ "friendly_model": "multi", "name": "Samjin multi", "quirk_applied": true, - "quirk_class": "zhaquirks.smartthings.multi.SmartthingsMultiPurposeSensor", + "quirk_class": "zhaquirks.smartthings.multi:SmartthingsMultiPurposeSensor", "exposes_features": [], "manufacturer_code": 4673, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/schneider-electric-eko07259.json b/tests/data/devices/schneider-electric-eko07259.json index 6b24cb00a..f01046dee 100644 --- a/tests/data/devices/schneider-electric-eko07259.json +++ b/tests/data/devices/schneider-electric-eko07259.json @@ -8,7 +8,7 @@ "friendly_model": "EKO07259", "name": "Schneider Electric EKO07259", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.schneiderelectric.thermostat:(Schneider Electric / EKO07259)", "exposes_features": [], "manufacturer_code": 4190, "power_source": "Mains", diff --git a/tests/data/devices/schneider-electric-puck-shutter-1.json b/tests/data/devices/schneider-electric-puck-shutter-1.json index 349b32636..bb252b2a7 100644 --- a/tests/data/devices/schneider-electric-puck-shutter-1.json +++ b/tests/data/devices/schneider-electric-puck-shutter-1.json @@ -8,7 +8,7 @@ "friendly_model": "PUCK/SHUTTER/1", "name": "Schneider Electric PUCK/SHUTTER/1", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.schneiderelectric.shutters:(Schneider Electric / PUCK/SHUTTER/1)", "exposes_features": [], "manufacturer_code": 4190, "power_source": "Mains", diff --git a/tests/data/devices/schneider-electric-socket-outlet-1.json b/tests/data/devices/schneider-electric-socket-outlet-1.json index bd3392a1d..a9bec7cad 100644 --- a/tests/data/devices/schneider-electric-socket-outlet-1.json +++ b/tests/data/devices/schneider-electric-socket-outlet-1.json @@ -8,7 +8,7 @@ "friendly_model": "SOCKET/OUTLET/1", "name": "Schneider Electric SOCKET/OUTLET/1", "quirk_applied": true, - "quirk_class": "zhaquirks.schneiderelectric.outlet.SocketOutlet", + "quirk_class": "zhaquirks.schneiderelectric.outlet:SocketOutlet", "exposes_features": [], "manufacturer_code": 4190, "power_source": "Mains", diff --git a/tests/data/devices/schneider-electric-socket-outlet-2.json b/tests/data/devices/schneider-electric-socket-outlet-2.json index 86db1990a..aef258d54 100644 --- a/tests/data/devices/schneider-electric-socket-outlet-2.json +++ b/tests/data/devices/schneider-electric-socket-outlet-2.json @@ -8,7 +8,7 @@ "friendly_model": "SOCKET/OUTLET/2", "name": "Schneider Electric SOCKET/OUTLET/2", "quirk_applied": true, - "quirk_class": "zhaquirks.schneiderelectric.outlet.SocketOutlet", + "quirk_class": "zhaquirks.schneiderelectric.outlet:SocketOutlet", "exposes_features": [], "manufacturer_code": 4190, "power_source": "Mains", diff --git a/tests/data/devices/shelly-1pm.json b/tests/data/devices/shelly-1pm.json index 80efa9df7..1daca3a25 100644 --- a/tests/data/devices/shelly-1pm.json +++ b/tests/data/devices/shelly-1pm.json @@ -7,8 +7,8 @@ "friendly_manufacturer": "Shelly", "friendly_model": "1PM", "name": "Shelly 1PM", - "quirk_applied": false, - "quirk_class": "zigpy.device.Device", + "quirk_applied": true, + "quirk_class": "zhaquirks.shelly.wifi:(Shelly / 1PM)", "exposes_features": [], "manufacturer_code": 5264, "power_source": "Mains", @@ -518,7 +518,7 @@ }, { "cluster_id": "0xfc02", - "endpoint_attribute": "manufacturer_specific", + "endpoint_attribute": "shelly_wifi_setup", "attributes": [] } ], diff --git a/tests/data/devices/shelly-2pm.json b/tests/data/devices/shelly-2pm.json index 8e7de0c8d..0262d4204 100644 --- a/tests/data/devices/shelly-2pm.json +++ b/tests/data/devices/shelly-2pm.json @@ -7,8 +7,8 @@ "friendly_manufacturer": "Shelly", "friendly_model": "2PM", "name": "Shelly 2PM", - "quirk_applied": false, - "quirk_class": "zigpy.device.Device", + "quirk_applied": true, + "quirk_class": "zhaquirks.shelly.wifi:(Shelly / 1PM)", "exposes_features": [], "manufacturer_code": 5264, "power_source": "Mains", @@ -169,7 +169,7 @@ }, { "cluster_id": "0xfc02", - "endpoint_attribute": "manufacturer_specific", + "endpoint_attribute": "shelly_wifi_setup", "attributes": [] } ], diff --git a/tests/data/devices/shelly-ecowitt-ws90.json b/tests/data/devices/shelly-ecowitt-ws90.json index 0938cb8a0..c165908a6 100644 --- a/tests/data/devices/shelly-ecowitt-ws90.json +++ b/tests/data/devices/shelly-ecowitt-ws90.json @@ -8,7 +8,7 @@ "friendly_model": "Ecowitt WS90", "name": "Shelly Ecowitt WS90", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.shelly.ecowitt_ws90:(Shelly / Ecowitt WS90)", "exposes_features": [], "manufacturer_code": 5264, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/shelly-mini1pm.json b/tests/data/devices/shelly-mini1pm.json index d198d726a..f63d376af 100644 --- a/tests/data/devices/shelly-mini1pm.json +++ b/tests/data/devices/shelly-mini1pm.json @@ -7,8 +7,8 @@ "friendly_manufacturer": "Shelly", "friendly_model": "Mini1PM", "name": "Shelly Mini1PM", - "quirk_applied": false, - "quirk_class": "zigpy.device.Device", + "quirk_applied": true, + "quirk_class": "zhaquirks.shelly.wifi:(Shelly / 1PM)", "exposes_features": [], "manufacturer_code": 5264, "power_source": "Mains", @@ -427,7 +427,7 @@ }, { "cluster_id": "0xfc02", - "endpoint_attribute": "manufacturer_specific", + "endpoint_attribute": "shelly_wifi_setup", "attributes": [] } ], diff --git a/tests/data/devices/shyugj-dimmer-switch-zb3-0.json b/tests/data/devices/shyugj-dimmer-switch-zb3-0.json index 701f368b0..d010fbd53 100644 --- a/tests/data/devices/shyugj-dimmer-switch-zb3-0.json +++ b/tests/data/devices/shyugj-dimmer-switch-zb3-0.json @@ -8,7 +8,7 @@ "friendly_model": "Dimmer-Switch-ZB3.0", "name": "Shyugj Dimmer-Switch-ZB3.0", "quirk_applied": true, - "quirk_class": "zhaquirks.hzc.dimmerswitch.DimmerSwitch", + "quirk_class": "zhaquirks.hzc.dimmerswitch:DimmerSwitch", "exposes_features": [], "manufacturer_code": 4714, "power_source": "Mains", diff --git a/tests/data/devices/signify-netherlands-b-v-lcx004.json b/tests/data/devices/signify-netherlands-b-v-lcx004.json index 05cd48a3c..8ef82800e 100644 --- a/tests/data/devices/signify-netherlands-b-v-lcx004.json +++ b/tests/data/devices/signify-netherlands-b-v-lcx004.json @@ -8,7 +8,7 @@ "friendly_model": "Hue Gradient lightstrip", "name": "Philips Hue Gradient lightstrip", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.philips.hue_light:(Signify Netherlands B.V. / LCX004)", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Mains", diff --git a/tests/data/devices/signify-netherlands-b-v-ltb003.json b/tests/data/devices/signify-netherlands-b-v-ltb003.json index 421e9bd27..19776c812 100755 --- a/tests/data/devices/signify-netherlands-b-v-ltb003.json +++ b/tests/data/devices/signify-netherlands-b-v-ltb003.json @@ -8,7 +8,7 @@ "friendly_model": "Hue White Ambiance BR30 E26", "name": "Philips Hue White Ambiance BR30 E26", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.philips.hue_light:(Signify Netherlands B.V. / LTB003)", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Mains", diff --git a/tests/data/devices/signify-netherlands-b-v-rdm001.json b/tests/data/devices/signify-netherlands-b-v-rdm001.json index 56e08a7d3..8a383dd0f 100644 --- a/tests/data/devices/signify-netherlands-b-v-rdm001.json +++ b/tests/data/devices/signify-netherlands-b-v-rdm001.json @@ -8,7 +8,7 @@ "friendly_model": "RDM001", "name": "Signify Netherlands B.V. RDM001", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.wall_switch.PhilipsWallSwitch", + "quirk_class": "zhaquirks.philips.wall_switch:PhilipsWallSwitch", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-rdm002.json b/tests/data/devices/signify-netherlands-b-v-rdm002.json index 8ee140705..1c23d5cfa 100644 --- a/tests/data/devices/signify-netherlands-b-v-rdm002.json +++ b/tests/data/devices/signify-netherlands-b-v-rdm002.json @@ -8,7 +8,7 @@ "friendly_model": "RDM002", "name": "Signify Netherlands B.V. RDM002", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rdm002.PhilipsRDM002", + "quirk_class": "zhaquirks.philips.rdm002:PhilipsRDM002", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-rdm004.json b/tests/data/devices/signify-netherlands-b-v-rdm004.json index 60a44682d..1d65ebede 100644 --- a/tests/data/devices/signify-netherlands-b-v-rdm004.json +++ b/tests/data/devices/signify-netherlands-b-v-rdm004.json @@ -8,7 +8,7 @@ "friendly_model": "RDM004", "name": "Signify Netherlands B.V. RDM004", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.wall_switch.PhilipsWallSwitchNewFirmware", + "quirk_class": "zhaquirks.philips.wall_switch:PhilipsWallSwitchNewFirmware", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-rdm005.json b/tests/data/devices/signify-netherlands-b-v-rdm005.json index cde5c4be9..a6bc1ad80 100644 --- a/tests/data/devices/signify-netherlands-b-v-rdm005.json +++ b/tests/data/devices/signify-netherlands-b-v-rdm005.json @@ -8,7 +8,7 @@ "friendly_model": "RDM005", "name": "Signify Netherlands B.V. RDM005", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rom001.PhilipsROM001", + "quirk_class": "zhaquirks.philips.rom001:PhilipsROM001", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-rwl022.json b/tests/data/devices/signify-netherlands-b-v-rwl022.json index 26a15e2a2..e9859fa39 100644 --- a/tests/data/devices/signify-netherlands-b-v-rwl022.json +++ b/tests/data/devices/signify-netherlands-b-v-rwl022.json @@ -8,7 +8,7 @@ "friendly_model": "RWL022", "name": "Signify Netherlands B.V. RWL022", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.rwl022.PhilipsRWL022", + "quirk_class": "zhaquirks.philips.rwl022:PhilipsRWL022", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-sml003.json b/tests/data/devices/signify-netherlands-b-v-sml003.json index 71dbf94c7..6ac8cd6d0 100644 --- a/tests/data/devices/signify-netherlands-b-v-sml003.json +++ b/tests/data/devices/signify-netherlands-b-v-sml003.json @@ -8,7 +8,7 @@ "friendly_model": "SML003", "name": "Signify Netherlands B.V. SML003", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.motion.SignifyMotion", + "quirk_class": "zhaquirks.philips.motion:SignifyMotion", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/signify-netherlands-b-v-sml004.json b/tests/data/devices/signify-netherlands-b-v-sml004.json index cc8cda62d..d39162798 100644 --- a/tests/data/devices/signify-netherlands-b-v-sml004.json +++ b/tests/data/devices/signify-netherlands-b-v-sml004.json @@ -8,7 +8,7 @@ "friendly_model": "SML004", "name": "Signify Netherlands B.V. SML004", "quirk_applied": true, - "quirk_class": "zhaquirks.philips.motion.SignifyMotion", + "quirk_class": "zhaquirks.philips.motion:SignifyMotion", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/silicon-labs-ezsp.json b/tests/data/devices/silicon-labs-ezsp.json new file mode 100644 index 000000000..46ce7b5dc --- /dev/null +++ b/tests/data/devices/silicon-labs-ezsp.json @@ -0,0 +1,171 @@ +{ + "version": 2, + "ieee": "ab:cd:ef:12:68:c2:d6:5d", + "nwk": "0x0000", + "manufacturer": "Silicon Labs", + "model": "EZSP", + "friendly_manufacturer": "Silicon Labs", + "friendly_model": "EZSP", + "name": "Silicon Labs EZSP", + "quirk_applied": false, + "quirk_class": "zigpy.device.Device", + "exposes_features": [], + "manufacturer_code": 43981, + "power_source": "Mains", + "lqi": 255, + "rssi": 0, + "last_seen": "2026-03-06T00:07:25.006965+00:00", + "available": true, + "device_type": "Coordinator", + "active_coordinator": false, + "node_descriptor": { + "logical_type": "Coordinator", + "complex_descriptor_available": false, + "user_descriptor_available": false, + "reserved": 0, + "aps_flags": 0, + "frequency_band": 8, + "mac_capability_flags": 143, + "manufacturer_code": 43981, + "maximum_buffer_size": 82, + "maximum_incoming_transfer_size": 128, + "server_mask": 11329, + "maximum_outgoing_transfer_size": 128, + "descriptor_capability_field": 0 + }, + "endpoints": { + "1": { + "profile_id": 260, + "device_type": { + "name": "IAS_CONTROL", + "id": 1024 + }, + "in_clusters": [ + { + "cluster_id": "0x0000", + "endpoint_attribute": "basic", + "attributes": [ + { + "id": "0x0004", + "name": "manufacturer", + "zcl_type": "string", + "value": "Silicon Labs" + }, + { + "id": "0x0005", + "name": "model", + "zcl_type": "string", + "value": "EZSP" + } + ] + }, + { + "cluster_id": "0x0006", + "endpoint_attribute": "on_off", + "attributes": [] + }, + { + "cluster_id": "0x000a", + "endpoint_attribute": "time", + "attributes": [] + }, + { + "cluster_id": "0x0019", + "endpoint_attribute": "ota", + "attributes": [] + }, + { + "cluster_id": "0x0501", + "endpoint_attribute": "ias_ace", + "attributes": [] + } + ], + "out_clusters": [ + { + "cluster_id": "0x0001", + "endpoint_attribute": "power", + "attributes": [] + }, + { + "cluster_id": "0x0020", + "endpoint_attribute": "poll_control", + "attributes": [] + }, + { + "cluster_id": "0x0500", + "endpoint_attribute": "ias_zone", + "attributes": [] + }, + { + "cluster_id": "0x0502", + "endpoint_attribute": "ias_wd", + "attributes": [] + } + ] + }, + "2": { + "profile_id": 49246, + "device_type": { + "name": "CONTROLLER", + "id": 2080 + }, + "in_clusters": [ + { + "cluster_id": "0x0000", + "endpoint_attribute": "basic", + "attributes": [] + } + ], + "out_clusters": [] + } + }, + "original_signature": { + "manufacturer": "Silicon Labs", + "model": "EZSP", + "node_desc": { + "logical_type": 0, + "complex_descriptor_available": 0, + "user_descriptor_available": 0, + "reserved": 0, + "aps_flags": 0, + "frequency_band": 8, + "mac_capability_flags": 143, + "manufacturer_code": 43981, + "maximum_buffer_size": 82, + "maximum_incoming_transfer_size": 128, + "server_mask": 11329, + "maximum_outgoing_transfer_size": 128, + "descriptor_capability_field": 0 + }, + "endpoints": { + "1": { + "profile_id": "0x0104", + "device_type": "0x0400", + "input_clusters": [ + "0x0000", + "0x0006", + "0x000a", + "0x0019", + "0x0501" + ], + "output_clusters": [ + "0x0001", + "0x0020", + "0x0500", + "0x0502" + ] + }, + "2": { + "profile_id": "0xc05e", + "device_type": "0x0820", + "input_clusters": [ + "0x0000" + ], + "output_clusters": [] + } + } + }, + "zha_lib_entities": {}, + "neighbors": [], + "routes": [] +} \ No newline at end of file diff --git a/tests/data/devices/smarthjemmet-dk-quad-zig-sw.json b/tests/data/devices/smarthjemmet-dk-quad-zig-sw.json index 668882d51..b2b6f7692 100644 --- a/tests/data/devices/smarthjemmet-dk-quad-zig-sw.json +++ b/tests/data/devices/smarthjemmet-dk-quad-zig-sw.json @@ -8,7 +8,7 @@ "friendly_model": "QUAD-ZIG-SW", "name": "smarthjemmet.dk QUAD-ZIG-SW", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.smarthjemmet.quadzigsw:(smarthjemmet.dk / QUAD-ZIG-SW)", "exposes_features": [], "manufacturer_code": 64447, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/smartthings-multiv4.json b/tests/data/devices/smartthings-multiv4.json index bea5a16ab..e11c66394 100644 --- a/tests/data/devices/smartthings-multiv4.json +++ b/tests/data/devices/smartthings-multiv4.json @@ -8,7 +8,7 @@ "friendly_model": "multiv4", "name": "SmartThings multiv4", "quirk_applied": true, - "quirk_class": "zhaquirks.smartthings.multiv4.SmartThingsMultiV4", + "quirk_class": "zhaquirks.smartthings.multiv4:SmartThingsMultiV4", "exposes_features": [], "manufacturer_code": 4362, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/smartthings-tagv4.json b/tests/data/devices/smartthings-tagv4.json index 4f5162d45..bc4529e23 100644 --- a/tests/data/devices/smartthings-tagv4.json +++ b/tests/data/devices/smartthings-tagv4.json @@ -8,7 +8,7 @@ "friendly_model": "tagv4", "name": "SmartThings tagv4", "quirk_applied": true, - "quirk_class": "zhaquirks.smartthings.tag_v4.SmartThingsTagV4", + "quirk_class": "zhaquirks.smartthings.tag_v4:SmartThingsTagV4", "exposes_features": [], "manufacturer_code": 4362, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/smartwings-wm25-l-z.json b/tests/data/devices/smartwings-wm25-l-z.json index 889a6ce8d..178729f0e 100644 --- a/tests/data/devices/smartwings-wm25-l-z.json +++ b/tests/data/devices/smartwings-wm25-l-z.json @@ -8,7 +8,7 @@ "friendly_model": "WM25/L-Z", "name": "Smartwings WM25/L-Z", "quirk_applied": true, - "quirk_class": "zhaquirks.smartwings.wm25lz.WM25LBlinds", + "quirk_class": "zhaquirks.smartwings.wm25lz:WM25LBlinds", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/sonoff-mini-zbrbs.json b/tests/data/devices/sonoff-mini-zbrbs.json index 8b6b29f5a..5e3b3e107 100644 --- a/tests/data/devices/sonoff-mini-zbrbs.json +++ b/tests/data/devices/sonoff-mini-zbrbs.json @@ -8,7 +8,7 @@ "friendly_model": "MINI-ZBRBS", "name": "SONOFF MINI-ZBRBS", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.mini_zbrbs:(SONOFF / MINI-ZBRBS)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Mains", diff --git a/tests/data/devices/sonoff-s60zbtpg.json b/tests/data/devices/sonoff-s60zbtpg.json index bd5e21d88..eb76bc0e5 100644 --- a/tests/data/devices/sonoff-s60zbtpg.json +++ b/tests/data/devices/sonoff-s60zbtpg.json @@ -8,7 +8,7 @@ "friendly_model": "S60ZBTPG", "name": "SONOFF S60ZBTPG", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.s60zbtpf:(SONOFF / S60ZBTPF)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Mains", diff --git a/tests/data/devices/sonoff-snzb-01m.json b/tests/data/devices/sonoff-snzb-01m.json index d1ce35a0b..0163a8fa7 100644 --- a/tests/data/devices/sonoff-snzb-01m.json +++ b/tests/data/devices/sonoff-snzb-01m.json @@ -8,7 +8,7 @@ "friendly_model": "SNZB-01M", "name": "SONOFF SNZB-01M", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.snzb01m:(SONOFF / SNZB-01M)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/sonoff-snzb-02d.json b/tests/data/devices/sonoff-snzb-02d.json index 65d3349f4..d5b4a219a 100644 --- a/tests/data/devices/sonoff-snzb-02d.json +++ b/tests/data/devices/sonoff-snzb-02d.json @@ -8,7 +8,7 @@ "friendly_model": "SNZB-02D", "name": "SONOFF SNZB-02D", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.snzb02d:(SONOFF / SNZB-02D)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/sonoff-snzb-06p.json b/tests/data/devices/sonoff-snzb-06p.json index 95cc11d95..e676d5c5d 100644 --- a/tests/data/devices/sonoff-snzb-06p.json +++ b/tests/data/devices/sonoff-snzb-06p.json @@ -8,7 +8,7 @@ "friendly_model": "SNZB-06P", "name": "SONOFF SNZB-06P", "quirk_applied": true, - "quirk_class": "zhaquirks.sonoff.snzb06p.SonoffPresenceSenorSNZB06P", + "quirk_class": "zhaquirks.sonoff.snzb06p:SonoffPresenceSenorSNZB06P", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Mains", diff --git a/tests/data/devices/sonoff-swv.json b/tests/data/devices/sonoff-swv.json index 4d798b766..511e1b945 100644 --- a/tests/data/devices/sonoff-swv.json +++ b/tests/data/devices/sonoff-swv.json @@ -8,7 +8,7 @@ "friendly_model": "SWV", "name": "SONOFF SWV", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.swv:(SONOFF / SWV)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/sonoff-trvzb.json b/tests/data/devices/sonoff-trvzb.json index c69128663..d008d507e 100644 --- a/tests/data/devices/sonoff-trvzb.json +++ b/tests/data/devices/sonoff-trvzb.json @@ -8,7 +8,7 @@ "friendly_model": "TRVZB", "name": "SONOFF TRVZB", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.trvzb:(SONOFF / TRVZB)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/sonoff-zbminir2.json b/tests/data/devices/sonoff-zbminir2.json index cebd6856f..b5c4b9dc9 100644 --- a/tests/data/devices/sonoff-zbminir2.json +++ b/tests/data/devices/sonoff-zbminir2.json @@ -8,7 +8,7 @@ "friendly_model": "ZBMINIR2", "name": "SONOFF ZBMINIR2", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.sonoff.zbminir2:(SONOFF / ZBMINIR2)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Mains", diff --git a/tests/data/devices/texasinstruments-ti-router.json b/tests/data/devices/texasinstruments-ti-router.json index c79a343c1..0990672c7 100644 --- a/tests/data/devices/texasinstruments-ti-router.json +++ b/tests/data/devices/texasinstruments-ti-router.json @@ -8,7 +8,7 @@ "friendly_model": "ti.router", "name": "TexasInstruments ti.router", "quirk_applied": true, - "quirk_class": "zhaquirks.texasinstruments.router.TiRouter", + "quirk_class": "zhaquirks.texasinstruments.router:TiRouter", "exposes_features": [], "manufacturer_code": 0, "power_source": "Mains", diff --git a/tests/data/devices/third-reality-inc-3rds17bz.json b/tests/data/devices/third-reality-inc-3rds17bz.json index ef0f24081..df5ddb733 100644 --- a/tests/data/devices/third-reality-inc-3rds17bz.json +++ b/tests/data/devices/third-reality-inc-3rds17bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RDS17BZ", "name": "Third Reality, Inc 3RDS17BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.door_sensor_v2:(Third Reality, Inc / 3RDS17BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rms16bz.json b/tests/data/devices/third-reality-inc-3rms16bz.json index 93cc0a3cd..145902625 100644 --- a/tests/data/devices/third-reality-inc-3rms16bz.json +++ b/tests/data/devices/third-reality-inc-3rms16bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RMS16BZ", "name": "Third Reality, Inc 3RMS16BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.motion_sensor:(Third Reality, Inc / 3RMS16BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rsb015bz.json b/tests/data/devices/third-reality-inc-3rsb015bz.json index 0ec380b5b..37f1b88fc 100644 --- a/tests/data/devices/third-reality-inc-3rsb015bz.json +++ b/tests/data/devices/third-reality-inc-3rsb015bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RSB015BZ", "name": "Third Reality, Inc 3RSB015BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.curtain_gen1:(Third Reality, Inc / 3RSB015BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rsb22bz.json b/tests/data/devices/third-reality-inc-3rsb22bz.json index ea0ae57c2..736c34489 100644 --- a/tests/data/devices/third-reality-inc-3rsb22bz.json +++ b/tests/data/devices/third-reality-inc-3rsb22bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RSB22BZ", "name": "Third Reality, Inc 3RSB22BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.button:(Third Reality, Inc / 3RSB22BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rsm0147z.json b/tests/data/devices/third-reality-inc-3rsm0147z.json index 743a64b29..d19bddc09 100644 --- a/tests/data/devices/third-reality-inc-3rsm0147z.json +++ b/tests/data/devices/third-reality-inc-3rsm0147z.json @@ -8,7 +8,7 @@ "friendly_model": "3RSM0147Z", "name": "Third Reality, Inc 3RSM0147Z", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.soil_moisture_sensor:(Third Reality, Inc / 3RSM0147Z)", "exposes_features": [], "manufacturer_code": 5127, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rsnl02043z.json b/tests/data/devices/third-reality-inc-3rsnl02043z.json index f9e8dc4b9..5ad098a02 100644 --- a/tests/data/devices/third-reality-inc-3rsnl02043z.json +++ b/tests/data/devices/third-reality-inc-3rsnl02043z.json @@ -8,7 +8,7 @@ "friendly_model": "3RSNL02043Z", "name": "Third Reality, Inc 3RSNL02043Z", "quirk_applied": true, - "quirk_class": "zhaquirks.thirdreality.night_light.Nightlight", + "quirk_class": "zhaquirks.thirdreality.night_light:Nightlight", "exposes_features": [], "manufacturer_code": 4877, "power_source": "Mains", diff --git a/tests/data/devices/third-reality-inc-3rsp02028bz.json b/tests/data/devices/third-reality-inc-3rsp02028bz.json index d2a6145a6..781d9bce5 100644 --- a/tests/data/devices/third-reality-inc-3rsp02028bz.json +++ b/tests/data/devices/third-reality-inc-3rsp02028bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RSP02028BZ", "name": "Third Reality, Inc 3RSP02028BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.plug:(Third Reality, Inc / 3RSP02028BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Mains", diff --git a/tests/data/devices/third-reality-inc-3rsp02064z.json b/tests/data/devices/third-reality-inc-3rsp02064z.json index 6baf7383e..53d751658 100644 --- a/tests/data/devices/third-reality-inc-3rsp02064z.json +++ b/tests/data/devices/third-reality-inc-3rsp02064z.json @@ -8,7 +8,7 @@ "friendly_model": "3RSP02064Z", "name": "Third Reality, Inc 3RSP02064Z", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.plug:(Third Reality, Inc / 3RSP02028BZ)", "exposes_features": [], "manufacturer_code": 5127, "power_source": "Mains", diff --git a/tests/data/devices/third-reality-inc-3rss009z.json b/tests/data/devices/third-reality-inc-3rss009z.json index f1fe4a212..2bc721050 100644 --- a/tests/data/devices/third-reality-inc-3rss009z.json +++ b/tests/data/devices/third-reality-inc-3rss009z.json @@ -8,7 +8,7 @@ "friendly_model": "3RSS009Z", "name": "Third Reality, Inc 3RSS009Z", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.switch_v2:(Third Reality, Inc / 3RSS009Z)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rths24bz.json b/tests/data/devices/third-reality-inc-3rths24bz.json index fbadb8c43..9763fddb5 100644 --- a/tests/data/devices/third-reality-inc-3rths24bz.json +++ b/tests/data/devices/third-reality-inc-3rths24bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RTHS24BZ", "name": "Third Reality, Inc 3RTHS24BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.temperature_sensor:(Third Reality, Inc / 3RTHS24BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rvs01031z.json b/tests/data/devices/third-reality-inc-3rvs01031z.json index 1eb1105b0..16297c8ca 100644 --- a/tests/data/devices/third-reality-inc-3rvs01031z.json +++ b/tests/data/devices/third-reality-inc-3rvs01031z.json @@ -8,7 +8,7 @@ "friendly_model": "3RVS01031Z", "name": "Third Reality, Inc 3RVS01031Z", "quirk_applied": true, - "quirk_class": "zhaquirks.thirdreality.vibrate.Vibrate", + "quirk_class": "zhaquirks.thirdreality.vibrate:Vibrate", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/third-reality-inc-3rws18bz.json b/tests/data/devices/third-reality-inc-3rws18bz.json index a04c64adc..aece4a5ac 100644 --- a/tests/data/devices/third-reality-inc-3rws18bz.json +++ b/tests/data/devices/third-reality-inc-3rws18bz.json @@ -8,7 +8,7 @@ "friendly_model": "3RWS18BZ", "name": "Third Reality, Inc 3RWS18BZ", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.thirdreality.water_leak_sensor:(Third Reality, Inc / 3RWS18BZ)", "exposes_features": [], "manufacturer_code": 4659, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tyst11-i5j6ifxj-5j6ifxj.json b/tests/data/devices/tyst11-i5j6ifxj-5j6ifxj.json index 845c60473..efc3bb185 100644 --- a/tests/data/devices/tyst11-i5j6ifxj-5j6ifxj.json +++ b/tests/data/devices/tyst11-i5j6ifxj-5j6ifxj.json @@ -8,7 +8,7 @@ "friendly_model": "5j6ifxj", "name": "_TYST11_i5j6ifxj 5j6ifxj", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TYST11_i5j6ifxj / 5j6ifxj)", "exposes_features": [], "manufacturer_code": 0, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tyzb01-1xktopx6-ts0041a.json b/tests/data/devices/tyzb01-1xktopx6-ts0041a.json index d9c04fdde..7089efd40 100644 --- a/tests/data/devices/tyzb01-1xktopx6-ts0041a.json +++ b/tests/data/devices/tyzb01-1xktopx6-ts0041a.json @@ -8,7 +8,7 @@ "friendly_model": "TS0041A", "name": "_TYZB01_1xktopx6 TS0041A", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0041.TuyaSmartRemote0041_var04", + "quirk_class": "zhaquirks.tuya.ts0041:TuyaSmartRemote0041_var04", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-1dd0d5yi-ts130f.json b/tests/data/devices/tz3000-1dd0d5yi-ts130f.json index 3fc115b8c..76ebb6aff 100644 --- a/tests/data/devices/tz3000-1dd0d5yi-ts130f.json +++ b/tests/data/devices/tz3000-1dd0d5yi-ts130f.json @@ -8,7 +8,7 @@ "friendly_model": "TS130F", "name": "_TZ3000_1dd0d5yi TS130F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts130f.TuyaTS130FTI2", + "quirk_class": "zhaquirks.tuya.ts130f:TuyaTS130FTI2", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-303avxxt-ts011f.json b/tests/data/devices/tz3000-303avxxt-ts011f.json index 4205c6854..21b784ec1 100644 --- a/tests/data/devices/tz3000-303avxxt-ts011f.json +++ b/tests/data/devices/tz3000-303avxxt-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_303avxxt TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-6l1pjfqe-ts011f.json b/tests/data/devices/tz3000-6l1pjfqe-ts011f.json index ce24e413a..5341131c2 100644 --- a/tests/data/devices/tz3000-6l1pjfqe-ts011f.json +++ b/tests/data/devices/tz3000-6l1pjfqe-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_6l1pjfqe TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_CB_Metering", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_CB_Metering", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-8nkb7mof-ts0121.json b/tests/data/devices/tz3000-8nkb7mof-ts0121.json index 014690358..0baa0c214 100644 --- a/tests/data/devices/tz3000-8nkb7mof-ts0121.json +++ b/tests/data/devices/tz3000-8nkb7mof-ts0121.json @@ -8,7 +8,7 @@ "friendly_model": "TS0121", "name": "_TZ3000_8nkb7mof TS0121", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0121_plug.TS0121B", + "quirk_class": "zhaquirks.tuya.ts0121_plug:TS0121B", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-8nyaanzb-ts011f.json b/tests/data/devices/tz3000-8nyaanzb-ts011f.json index c410000eb..3097ec245 100644 --- a/tests/data/devices/tz3000-8nyaanzb-ts011f.json +++ b/tests/data/devices/tz3000-8nyaanzb-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_8nyaanzb TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_TZ3000_2AC_var02", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_TZ3000_2AC_var02", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-a37eix1s-ts0004.json b/tests/data/devices/tz3000-a37eix1s-ts0004.json index 18ce0ac73..a594ec16a 100644 --- a/tests/data/devices/tz3000-a37eix1s-ts0004.json +++ b/tests/data/devices/tz3000-a37eix1s-ts0004.json @@ -8,7 +8,7 @@ "friendly_model": "TS0004", "name": "_TZ3000_a37eix1s TS0004", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_4G_GPP_Var2", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_4G_GPP_Var2", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-bjawzodf-ty0201.json b/tests/data/devices/tz3000-bjawzodf-ty0201.json index 37b341f08..2a0db38a8 100644 --- a/tests/data/devices/tz3000-bjawzodf-ty0201.json +++ b/tests/data/devices/tz3000-bjawzodf-ty0201.json @@ -8,7 +8,7 @@ "friendly_model": "TY0201", "name": "_TZ3000_bjawzodf TY0201", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3000_bjawzodf / TY0201)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-dbou1ap4-ts0505a.json b/tests/data/devices/tz3000-dbou1ap4-ts0505a.json index 53cb7c0c9..3ae742e95 100644 --- a/tests/data/devices/tz3000-dbou1ap4-ts0505a.json +++ b/tests/data/devices/tz3000-dbou1ap4-ts0505a.json @@ -8,7 +8,7 @@ "friendly_model": "TS0505A", "name": "_TZ3000_dbou1ap4 TS0505A", "quirk_applied": true, - "quirk_class": "zhaquirks.lidl.rgbcct.RGBCCTLight", + "quirk_class": "zhaquirks.lidl.rgbcct:RGBCCTLight", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-drc9tuqb-ts0001.json b/tests/data/devices/tz3000-drc9tuqb-ts0001.json index bd49dbeec..e4bc8e797 100644 --- a/tests/data/devices/tz3000-drc9tuqb-ts0001.json +++ b/tests/data/devices/tz3000-drc9tuqb-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_drc9tuqb TS0001", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_1G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_1G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-empogkya-ts0003.json b/tests/data/devices/tz3000-empogkya-ts0003.json index dc6ad4f16..15804d5be 100644 --- a/tests/data/devices/tz3000-empogkya-ts0003.json +++ b/tests/data/devices/tz3000-empogkya-ts0003.json @@ -8,7 +8,7 @@ "friendly_model": "TS0003", "name": "_TZ3000_empogkya TS0003", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_3G_GPP_Var2", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_3G_GPP_Var2", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-famkxci2-ts0043.json b/tests/data/devices/tz3000-famkxci2-ts0043.json index 982ff1941..832be0c99 100644 --- a/tests/data/devices/tz3000-famkxci2-ts0043.json +++ b/tests/data/devices/tz3000-famkxci2-ts0043.json @@ -8,7 +8,7 @@ "friendly_model": "TS0043", "name": "_TZ3000_famkxci2 TS0043", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0043.TuyaSmartRemote0043TOPlusA", + "quirk_class": "zhaquirks.tuya.ts0043:TuyaSmartRemote0043TOPlusA", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-gwkzibhs-ts004f.json b/tests/data/devices/tz3000-gwkzibhs-ts004f.json index 2da4664ae..7462b396b 100644 --- a/tests/data/devices/tz3000-gwkzibhs-ts004f.json +++ b/tests/data/devices/tz3000-gwkzibhs-ts004f.json @@ -8,7 +8,7 @@ "friendly_model": "TS004F", "name": "_TZ3000_gwkzibhs TS004F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts004f.TuyaSmartRemote004FROK", + "quirk_class": "zhaquirks.tuya.ts004f:TuyaSmartRemote004FROK", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-idhkkbqj-ts0012.json b/tests/data/devices/tz3000-idhkkbqj-ts0012.json index 33bea50c3..2abfd62f4 100644 --- a/tests/data/devices/tz3000-idhkkbqj-ts0012.json +++ b/tests/data/devices/tz3000-idhkkbqj-ts0012.json @@ -8,7 +8,7 @@ "friendly_model": "TS0012", "name": "_TZ3000_idhkkbqj TS0012", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts001x.TuyaDoubleNoNeutralSwitch", + "quirk_class": "zhaquirks.tuya.ts001x:TuyaDoubleNoNeutralSwitch", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-itnrsufe-ts0201.json b/tests/data/devices/tz3000-itnrsufe-ts0201.json index 7f132e340..ebc6b285c 100644 --- a/tests/data/devices/tz3000-itnrsufe-ts0201.json +++ b/tests/data/devices/tz3000-itnrsufe-ts0201.json @@ -8,7 +8,7 @@ "friendly_model": "TS0201", "name": "_TZ3000_itnrsufe TS0201", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0201.MoesTemperatureHumidtySensorWithScreen", + "quirk_class": "zhaquirks.tuya.ts0201:MoesTemperatureHumidtySensorWithScreen", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-j1xl73iw-ts130f.json b/tests/data/devices/tz3000-j1xl73iw-ts130f.json index 527d9c2f0..49d3603fc 100644 --- a/tests/data/devices/tz3000-j1xl73iw-ts130f.json +++ b/tests/data/devices/tz3000-j1xl73iw-ts130f.json @@ -8,7 +8,7 @@ "friendly_model": "TS130F", "name": "_TZ3000_j1xl73iw TS130F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts130f.TuyaTS130Double_GP_ESTC", + "quirk_class": "zhaquirks.tuya.ts130f:TuyaTS130Double_GP_ESTC", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-ja5osu5g-ts004f.json b/tests/data/devices/tz3000-ja5osu5g-ts004f.json index b605cc3fb..24a3be566 100644 --- a/tests/data/devices/tz3000-ja5osu5g-ts004f.json +++ b/tests/data/devices/tz3000-ja5osu5g-ts004f.json @@ -8,7 +8,7 @@ "friendly_model": "TS004F", "name": "_TZ3000_ja5osu5g TS004F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts004f.TuyaSmartRemote004FSK", + "quirk_class": "zhaquirks.tuya.ts004f:TuyaSmartRemote004FSK", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-kjfzuycl-ts004f.json b/tests/data/devices/tz3000-kjfzuycl-ts004f.json index fd62b6e33..b782ce4a8 100644 --- a/tests/data/devices/tz3000-kjfzuycl-ts004f.json +++ b/tests/data/devices/tz3000-kjfzuycl-ts004f.json @@ -8,7 +8,7 @@ "friendly_model": "TS004F", "name": "_TZ3000_kjfzuycl TS004F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts004f.TuyaSmartRemote004FSK", + "quirk_class": "zhaquirks.tuya.ts004f:TuyaSmartRemote004FSK", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-kmsbwdol-ts130f.json b/tests/data/devices/tz3000-kmsbwdol-ts130f.json index 01f7de633..4790aca93 100644 --- a/tests/data/devices/tz3000-kmsbwdol-ts130f.json +++ b/tests/data/devices/tz3000-kmsbwdol-ts130f.json @@ -8,7 +8,7 @@ "friendly_model": "TS130F", "name": "_TZ3000_kmsbwdol TS130F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts130f.TuyaTS130Double_GP_ESTC", + "quirk_class": "zhaquirks.tuya.ts130f:TuyaTS130Double_GP_ESTC", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-kqvb5akv-ts0001.json b/tests/data/devices/tz3000-kqvb5akv-ts0001.json index b1da12238..7c4393b2c 100644 --- a/tests/data/devices/tz3000-kqvb5akv-ts0001.json +++ b/tests/data/devices/tz3000-kqvb5akv-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_kqvb5akv TS0001", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.ts0001_switch:(_TZ3000_xkap8wtb / TS0001)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-lepzuhto-ts011f.json b/tests/data/devices/tz3000-lepzuhto-ts011f.json index 4a5e3554f..ed5d9f9db 100644 --- a/tests/data/devices/tz3000-lepzuhto-ts011f.json +++ b/tests/data/devices/tz3000-lepzuhto-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_lepzuhto TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_CB_Metering", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_CB_Metering", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-llfaquvp-ts0012.json b/tests/data/devices/tz3000-llfaquvp-ts0012.json index 8c7360154..9c13b96ae 100644 --- a/tests/data/devices/tz3000-llfaquvp-ts0012.json +++ b/tests/data/devices/tz3000-llfaquvp-ts0012.json @@ -8,7 +8,7 @@ "friendly_model": "TS0012", "name": "_TZ3000_llfaquvp TS0012", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts001x.Tuya_Double_No_N_Plus", + "quirk_class": "zhaquirks.tuya.ts001x:Tuya_Double_No_N_Plus", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-ltiqubue-ts130f.json b/tests/data/devices/tz3000-ltiqubue-ts130f.json index e0c55ed30..d29f2de5b 100644 --- a/tests/data/devices/tz3000-ltiqubue-ts130f.json +++ b/tests/data/devices/tz3000-ltiqubue-ts130f.json @@ -8,7 +8,7 @@ "friendly_model": "TS130F", "name": "_TZ3000_ltiqubue TS130F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts130f.TuyaTS130FUL", + "quirk_class": "zhaquirks.tuya.ts130f:TuyaTS130FUL", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-mkhkxx1p-ts0001.json b/tests/data/devices/tz3000-mkhkxx1p-ts0001.json index f6134d4a5..a00b2da33 100644 --- a/tests/data/devices/tz3000-mkhkxx1p-ts0001.json +++ b/tests/data/devices/tz3000-mkhkxx1p-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_mkhkxx1p TS0001", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.ts0001_switch:(_TZ3000_xkap8wtb / TS0001)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-mw1pqqqt-ts0003.json b/tests/data/devices/tz3000-mw1pqqqt-ts0003.json index 2e2e003f2..459212e84 100644 --- a/tests/data/devices/tz3000-mw1pqqqt-ts0003.json +++ b/tests/data/devices/tz3000-mw1pqqqt-ts0003.json @@ -8,7 +8,7 @@ "friendly_model": "TS0003", "name": "_TZ3000_mw1pqqqt TS0003", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_3G_Metering", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_3G_Metering", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-o1jzcxou-ts011f.json b/tests/data/devices/tz3000-o1jzcxou-ts011f.json index ced73be76..c7df3088d 100644 --- a/tests/data/devices/tz3000-o1jzcxou-ts011f.json +++ b/tests/data/devices/tz3000-o1jzcxou-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_o1jzcxou TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_v7", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_v7", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-okaz9tjs-ts011f.json b/tests/data/devices/tz3000-okaz9tjs-ts011f.json index 5de4ec755..3f2e5cb49 100644 --- a/tests/data/devices/tz3000-okaz9tjs-ts011f.json +++ b/tests/data/devices/tz3000-okaz9tjs-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_okaz9tjs TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_v5", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_v5", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-p26flek3-ts0001.json b/tests/data/devices/tz3000-p26flek3-ts0001.json index 6a0bec9c7..1a7a49988 100644 --- a/tests/data/devices/tz3000-p26flek3-ts0001.json +++ b/tests/data/devices/tz3000-p26flek3-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_p26flek3 TS0001", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_1G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_1G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-r6buo8ba-ts011f.json b/tests/data/devices/tz3000-r6buo8ba-ts011f.json index 5931cf1f1..eb0243bee 100644 --- a/tests/data/devices/tz3000-r6buo8ba-ts011f.json +++ b/tests/data/devices/tz3000-r6buo8ba-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_r6buo8ba TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_v6", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_v6", "exposes_features": [], "manufacturer_code": 0, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-rbl8c85w-ts0012.json b/tests/data/devices/tz3000-rbl8c85w-ts0012.json index e97c5e69f..c0488968a 100644 --- a/tests/data/devices/tz3000-rbl8c85w-ts0012.json +++ b/tests/data/devices/tz3000-rbl8c85w-ts0012.json @@ -8,7 +8,7 @@ "friendly_model": "TS0012", "name": "_TZ3000_rbl8c85w TS0012", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts001x.Tuya_Double_Var06", + "quirk_class": "zhaquirks.tuya.ts001x:Tuya_Double_Var06", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-sgb0xhwn-ts011f.json b/tests/data/devices/tz3000-sgb0xhwn-ts011f.json index 312c65454..b2e86d711 100644 --- a/tests/data/devices/tz3000-sgb0xhwn-ts011f.json +++ b/tests/data/devices/tz3000-sgb0xhwn-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_sgb0xhwn TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_2AC_var05", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_2AC_var05", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-tgddllx4-ts0001.json b/tests/data/devices/tz3000-tgddllx4-ts0001.json index 1614ec54c..f8ec4d389 100644 --- a/tests/data/devices/tz3000-tgddllx4-ts0001.json +++ b/tests/data/devices/tz3000-tgddllx4-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_tgddllx4 TS0001", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.ts0001_switch:(_TZ3000_xkap8wtb / TS0001)", "exposes_features": [], "manufacturer_code": 4660, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-typdpbpg-ts011f.json b/tests/data/devices/tz3000-typdpbpg-ts011f.json index 14cf637be..903059c65 100644 --- a/tests/data/devices/tz3000-typdpbpg-ts011f.json +++ b/tests/data/devices/tz3000-typdpbpg-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_typdpbpg TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_v3", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_v3", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-u3oupgdy-ts0004.json b/tests/data/devices/tz3000-u3oupgdy-ts0004.json index ba7666363..57e7a3771 100644 --- a/tests/data/devices/tz3000-u3oupgdy-ts0004.json +++ b/tests/data/devices/tz3000-u3oupgdy-ts0004.json @@ -8,7 +8,7 @@ "friendly_model": "TS0004", "name": "_TZ3000_u3oupgdy TS0004", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_4G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_4G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-w0qqde0g-ts011f.json b/tests/data/devices/tz3000-w0qqde0g-ts011f.json index b6de9c638..b79edbd4a 100644 --- a/tests/data/devices/tz3000-w0qqde0g-ts011f.json +++ b/tests/data/devices/tz3000-w0qqde0g-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_w0qqde0g TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_v2", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_v2", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-wkr3jqmr-ts0004.json b/tests/data/devices/tz3000-wkr3jqmr-ts0004.json index 304be2008..36d6d6b8a 100644 --- a/tests/data/devices/tz3000-wkr3jqmr-ts0004.json +++ b/tests/data/devices/tz3000-wkr3jqmr-ts0004.json @@ -8,7 +8,7 @@ "friendly_model": "TS0004", "name": "_TZ3000_wkr3jqmr TS0004", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_4G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_4G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-wn65ixz9-ts0001.json b/tests/data/devices/tz3000-wn65ixz9-ts0001.json index c3721a5fe..d481422e0 100644 --- a/tests/data/devices/tz3000-wn65ixz9-ts0001.json +++ b/tests/data/devices/tz3000-wn65ixz9-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3000_wn65ixz9 TS0001", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_1G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_1G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-xkap8wtb-ts000f.json b/tests/data/devices/tz3000-xkap8wtb-ts000f.json index 212b3edf5..f8ddb4688 100644 --- a/tests/data/devices/tz3000-xkap8wtb-ts000f.json +++ b/tests/data/devices/tz3000-xkap8wtb-ts000f.json @@ -8,7 +8,7 @@ "friendly_model": "TS000F", "name": "_TZ3000_xkap8wtb TS000F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000f_switch.Tuya_1G_Wall_Switch_Metering", + "quirk_class": "zhaquirks.tuya.ts000f_switch:Tuya_1G_Wall_Switch_Metering", "exposes_features": [], "manufacturer_code": 4107, "power_source": "Mains", diff --git a/tests/data/devices/tz3000-xxacnuab-ts0004.json b/tests/data/devices/tz3000-xxacnuab-ts0004.json index 2f1d02f57..9c4f13f53 100644 --- a/tests/data/devices/tz3000-xxacnuab-ts0004.json +++ b/tests/data/devices/tz3000-xxacnuab-ts0004.json @@ -8,7 +8,7 @@ "friendly_model": "TS0004", "name": "_TZ3000_xxacnuab TS0004", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts000x.Switch_4G_GPP", + "quirk_class": "zhaquirks.tuya.ts000x:Switch_4G_GPP", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3000-yj6k7vfo-ts0041.json b/tests/data/devices/tz3000-yj6k7vfo-ts0041.json index 57502d1fd..afbfeee87 100644 --- a/tests/data/devices/tz3000-yj6k7vfo-ts0041.json +++ b/tests/data/devices/tz3000-yj6k7vfo-ts0041.json @@ -8,7 +8,7 @@ "friendly_model": "TS0041", "name": "_TZ3000_yj6k7vfo TS0041", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0041.TuyaSmartRemote0041TOPlusA", + "quirk_class": "zhaquirks.tuya.ts0041:TuyaSmartRemote0041TOPlusA", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-zl1kmjqx-ty0201.json b/tests/data/devices/tz3000-zl1kmjqx-ty0201.json index a709e4050..f7e566ca7 100644 --- a/tests/data/devices/tz3000-zl1kmjqx-ty0201.json +++ b/tests/data/devices/tz3000-zl1kmjqx-ty0201.json @@ -8,7 +8,7 @@ "friendly_model": "TY0201", "name": "_TZ3000_zl1kmjqx TY0201", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3000_bjawzodf / TY0201)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-zl1kmjqx.json b/tests/data/devices/tz3000-zl1kmjqx.json index 80fffa119..77cd60eac 100644 --- a/tests/data/devices/tz3000-zl1kmjqx.json +++ b/tests/data/devices/tz3000-zl1kmjqx.json @@ -8,7 +8,7 @@ "friendly_model": "", "name": "_TZ3000_zl1kmjqx ", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3000_bjawzodf / TY0201)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3000-zv6x8bt2-ts011f.json b/tests/data/devices/tz3000-zv6x8bt2-ts011f.json index b34931cb0..be115d3b8 100644 --- a/tests/data/devices/tz3000-zv6x8bt2-ts011f.json +++ b/tests/data/devices/tz3000-zv6x8bt2-ts011f.json @@ -8,7 +8,7 @@ "friendly_model": "TS011F", "name": "_TZ3000_zv6x8bt2 TS011F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts011f_plug.Plug_CB_Metering", + "quirk_class": "zhaquirks.tuya.ts011f_plug:Plug_CB_Metering", "exposes_features": [ "tuya.plug_on_off_attributes" ], diff --git a/tests/data/devices/tz3040-bb6xaihh-ts0202.json b/tests/data/devices/tz3040-bb6xaihh-ts0202.json index 7ebc4a8e0..ba489dd10 100644 --- a/tests/data/devices/tz3040-bb6xaihh-ts0202.json +++ b/tests/data/devices/tz3040-bb6xaihh-ts0202.json @@ -8,7 +8,7 @@ "friendly_model": "TS0202", "name": "_TZ3040_bb6xaihh TS0202", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3000_bb6xaihh / SNZB-03)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3210-0jxeoadc-ts0049.json b/tests/data/devices/tz3210-0jxeoadc-ts0049.json index afa694bc5..fb869efb8 100644 --- a/tests/data/devices/tz3210-0jxeoadc-ts0049.json +++ b/tests/data/devices/tz3210-0jxeoadc-ts0049.json @@ -8,7 +8,7 @@ "friendly_model": "TS0049", "name": "_TZ3210_0jxeoadc TS0049", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3210_0jxeoadc / TS0049)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3210-3ulg9kpo-ts0021.json b/tests/data/devices/tz3210-3ulg9kpo-ts0021.json index 0fba1f195..d64436164 100644 --- a/tests/data/devices/tz3210-3ulg9kpo-ts0021.json +++ b/tests/data/devices/tz3210-3ulg9kpo-ts0021.json @@ -8,7 +8,7 @@ "friendly_model": "TS0021", "name": "_TZ3210_3ulg9kpo TS0021", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0021.TS0021", + "quirk_class": "zhaquirks.tuya.ts0021:TS0021", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3210-dwytrmda-ts130f.json b/tests/data/devices/tz3210-dwytrmda-ts130f.json index 56a18e6e8..14ccef364 100644 --- a/tests/data/devices/tz3210-dwytrmda-ts130f.json +++ b/tests/data/devices/tz3210-dwytrmda-ts130f.json @@ -8,7 +8,7 @@ "friendly_model": "TS130F", "name": "_TZ3210_dwytrmda TS130F", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts130f.TuyaTS130FTOGP", + "quirk_class": "zhaquirks.tuya.ts130f:TuyaTS130FTOGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tz3210-j4pdtz9v-ts0001.json b/tests/data/devices/tz3210-j4pdtz9v-ts0001.json index ac4799487..e52168dba 100644 --- a/tests/data/devices/tz3210-j4pdtz9v-ts0001.json +++ b/tests/data/devices/tz3210-j4pdtz9v-ts0001.json @@ -8,7 +8,7 @@ "friendly_model": "TS0001", "name": "_TZ3210_j4pdtz9v TS0001", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3210_dse8ogfy / TS0001)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3210-tgvtvdoc-ts0207.json b/tests/data/devices/tz3210-tgvtvdoc-ts0207.json index b9f19ab86..df8c0a25d 100644 --- a/tests/data/devices/tz3210-tgvtvdoc-ts0207.json +++ b/tests/data/devices/tz3210-tgvtvdoc-ts0207.json @@ -8,7 +8,7 @@ "friendly_model": "TS0207", "name": "_TZ3210_tgvtvdoc TS0207", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ3210_tgvtvdoc / TS0207)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3210-up3pngle-ts0205.json b/tests/data/devices/tz3210-up3pngle-ts0205.json index d6076caaf..e2c4e2c4d 100644 --- a/tests/data/devices/tz3210-up3pngle-ts0205.json +++ b/tests/data/devices/tz3210-up3pngle-ts0205.json @@ -8,7 +8,7 @@ "friendly_model": "TS0205", "name": "_TZ3210_up3pngle TS0205", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.tuya_smoke:(_TZ3210_up3pngle / TS0205)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz3290-7v1k4vufotpowp9z-ts1201.json b/tests/data/devices/tz3290-7v1k4vufotpowp9z-ts1201.json index d85fcf01f..092a74b86 100644 --- a/tests/data/devices/tz3290-7v1k4vufotpowp9z-ts1201.json +++ b/tests/data/devices/tz3290-7v1k4vufotpowp9z-ts1201.json @@ -8,7 +8,7 @@ "friendly_model": "TS1201", "name": "_TZ3290_7v1k4vufotpowp9z TS1201", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts1201.ZosungIRBlaster_ZS06", + "quirk_class": "zhaquirks.tuya.ts1201:ZosungIRBlaster_ZS06", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tz3290-ot6ewjvmejq5ekhl-ts1201.json b/tests/data/devices/tz3290-ot6ewjvmejq5ekhl-ts1201.json index dcc5d5974..250611bb3 100644 --- a/tests/data/devices/tz3290-ot6ewjvmejq5ekhl-ts1201.json +++ b/tests/data/devices/tz3290-ot6ewjvmejq5ekhl-ts1201.json @@ -8,7 +8,7 @@ "friendly_model": "TS1201", "name": "_TZ3290_ot6ewjvmejq5ekhl TS1201", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts1201.ZosungIRBlaster", + "quirk_class": "zhaquirks.tuya.ts1201:ZosungIRBlaster", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tz6210-duv6fhwt-ts0601.json b/tests/data/devices/tz6210-duv6fhwt-ts0601.json index 5c272df36..57cd88c2d 100644 --- a/tests/data/devices/tz6210-duv6fhwt-ts0601.json +++ b/tests/data/devices/tz6210-duv6fhwt-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZ6210_duv6fhwt TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZ6210_duv6fhwt / TS0601)", "exposes_features": [], "manufacturer_code": 4619, "power_source": "Mains", diff --git a/tests/data/devices/tze200-1n2zev06-ts0601.json b/tests/data/devices/tze200-1n2zev06-ts0601.json index 00be7eb9f..69195e7d9 100644 --- a/tests/data/devices/tze200-1n2zev06-ts0601.json +++ b/tests/data/devices/tze200-1n2zev06-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_1n2zev06 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_81isopgh / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze200-2aaelwxk-ts0225.json b/tests/data/devices/tze200-2aaelwxk-ts0225.json index ffc4da039..c19814853 100644 --- a/tests/data/devices/tze200-2aaelwxk-ts0225.json +++ b/tests/data/devices/tze200-2aaelwxk-ts0225.json @@ -8,7 +8,7 @@ "friendly_model": "TS0225", "name": "_TZE200_2aaelwxk TS0225", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_2aaelwxk / TS0225)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-2se8efxh-ts0601.json b/tests/data/devices/tze200-2se8efxh-ts0601.json index a9b6444fb..9070a17d1 100644 --- a/tests/data/devices/tze200-2se8efxh-ts0601.json +++ b/tests/data/devices/tze200-2se8efxh-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_2se8efxh TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_myd45weu / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-3towulqd-ts0601.json b/tests/data/devices/tze200-3towulqd-ts0601.json index bd8dc15df..f0514577f 100644 --- a/tests/data/devices/tze200-3towulqd-ts0601.json +++ b/tests/data/devices/tze200-3towulqd-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_3towulqd TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_3towulqd / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-4utwozi2-ts0601.json b/tests/data/devices/tze200-4utwozi2-ts0601.json index 85500e03a..8d3287739 100644 --- a/tests/data/devices/tze200-4utwozi2-ts0601.json +++ b/tests/data/devices/tze200-4utwozi2-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_4utwozi2 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-6rdj8dzm-ts0601.json b/tests/data/devices/tze200-6rdj8dzm-ts0601.json index 12f61088d..9b1132f1b 100644 --- a/tests/data/devices/tze200-6rdj8dzm-ts0601.json +++ b/tests/data/devices/tze200-6rdj8dzm-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_6rdj8dzm TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-7bztmfm1-ts0601.json b/tests/data/devices/tze200-7bztmfm1-ts0601.json index 4d438cc6b..3d746ae11 100644 --- a/tests/data/devices/tze200-7bztmfm1-ts0601.json +++ b/tests/data/devices/tze200-7bztmfm1-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_7bztmfm1 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_7bztmfm1 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-7ytb3h8u-ts0601.json b/tests/data/devices/tze200-7ytb3h8u-ts0601.json index f847205f5..6bfe21592 100644 --- a/tests/data/devices/tze200-7ytb3h8u-ts0601.json +++ b/tests/data/devices/tze200-7ytb3h8u-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_7ytb3h8u TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_a7sghmms / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-81isopgh-ts0601.json b/tests/data/devices/tze200-81isopgh-ts0601.json index d302db145..8d9275bdc 100644 --- a/tests/data/devices/tze200-81isopgh-ts0601.json +++ b/tests/data/devices/tze200-81isopgh-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_81isopgh TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_81isopgh / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-9xfjixap-ts0601.json b/tests/data/devices/tze200-9xfjixap-ts0601.json index f8bd10618..3aeeedfd6 100644 --- a/tests/data/devices/tze200-9xfjixap-ts0601.json +++ b/tests/data/devices/tze200-9xfjixap-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_9xfjixap TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-a7sghmms-ts0601.json b/tests/data/devices/tze200-a7sghmms-ts0601.json index 0a80b4211..9333d4def 100644 --- a/tests/data/devices/tze200-a7sghmms-ts0601.json +++ b/tests/data/devices/tze200-a7sghmms-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_a7sghmms TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_a7sghmms / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-b6wax7g0-ts0601.json b/tests/data/devices/tze200-b6wax7g0-ts0601.json index 869261719..77ac9a676 100644 --- a/tests/data/devices/tze200-b6wax7g0-ts0601.json +++ b/tests/data/devices/tze200-b6wax7g0-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_b6wax7g0 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_trv.MoesHY368_Type1new", + "quirk_class": "zhaquirks.tuya.ts0601_trv:MoesHY368_Type1new", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-bkkmqmyo-ts0601.json b/tests/data/devices/tze200-bkkmqmyo-ts0601.json index 3bf882456..48fa08168 100644 --- a/tests/data/devices/tze200-bkkmqmyo-ts0601.json +++ b/tests/data/devices/tze200-bkkmqmyo-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_bkkmqmyo TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_din_power.HikingPowerMeter", + "quirk_class": "zhaquirks.tuya.ts0601_din_power:HikingPowerMeter", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze200-c88teujp-ts0601.json b/tests/data/devices/tze200-c88teujp-ts0601.json index 3b628a0b9..4ec67a698 100644 --- a/tests/data/devices/tze200-c88teujp-ts0601.json +++ b/tests/data/devices/tze200-c88teujp-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_c88teujp TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TYST11_KGbxAXL2 / GbxAXL2)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-cirvgep4-ts0601.json b/tests/data/devices/tze200-cirvgep4-ts0601.json index f475a9b87..9f733bf42 100644 --- a/tests/data/devices/tze200-cirvgep4-ts0601.json +++ b/tests/data/devices/tze200-cirvgep4-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_cirvgep4 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-crq3r3la-ck-bl702-mws-01-7016.json b/tests/data/devices/tze200-crq3r3la-ck-bl702-mws-01-7016.json index 055da7fc9..17f739b35 100644 --- a/tests/data/devices/tze200-crq3r3la-ck-bl702-mws-01-7016.json +++ b/tests/data/devices/tze200-crq3r3la-ck-bl702-mws-01-7016.json @@ -8,7 +8,7 @@ "friendly_model": "CK-BL702-MWS-01(7016)", "name": "_TZE200_crq3r3la CK-BL702-MWS-01(7016)", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_2aaelwxk / TS0225)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-dwcarsat-ts0601.json b/tests/data/devices/tze200-dwcarsat-ts0601.json index 7e9db91f2..70250c5ff 100644 --- a/tests/data/devices/tze200-dwcarsat-ts0601.json +++ b/tests/data/devices/tze200-dwcarsat-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_dwcarsat TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_dwcarsat / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-gaj531w3-ts0601.json b/tests/data/devices/tze200-gaj531w3-ts0601.json index f916fb92f..0201be14f 100644 --- a/tests/data/devices/tze200-gaj531w3-ts0601.json +++ b/tests/data/devices/tze200-gaj531w3-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_gaj531w3 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_cover.TuyaMoesCover0601", + "quirk_class": "zhaquirks.tuya.ts0601_cover:TuyaMoesCover0601", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-gjldowol-ts0601.json b/tests/data/devices/tze200-gjldowol-ts0601.json index 2d9e21646..f3bc13125 100644 --- a/tests/data/devices/tze200-gjldowol-ts0601.json +++ b/tests/data/devices/tze200-gjldowol-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_gjldowol TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_gjldowol / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-gkfbdvyx-ts0601.json b/tests/data/devices/tze200-gkfbdvyx-ts0601.json index a15b26686..4c915d174 100644 --- a/tests/data/devices/tze200-gkfbdvyx-ts0601.json +++ b/tests/data/devices/tze200-gkfbdvyx-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_gkfbdvyx TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_gkfbdvyx / TS0601)", "exposes_features": [], "manufacturer_code": 4660, "power_source": "Mains", diff --git a/tests/data/devices/tze200-h4cgnbzg-ts0601.json b/tests/data/devices/tze200-h4cgnbzg-ts0601.json index 53e6b9630..f816d3854 100644 --- a/tests/data/devices/tze200-h4cgnbzg-ts0601.json +++ b/tests/data/devices/tze200-h4cgnbzg-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_h4cgnbzg TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TYST11_KGbxAXL2 / GbxAXL2)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-hr0tdd47-ts0601.json b/tests/data/devices/tze200-hr0tdd47-ts0601.json index 465142048..ab7b7ef67 100644 --- a/tests/data/devices/tze200-hr0tdd47-ts0601.json +++ b/tests/data/devices/tze200-hr0tdd47-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_hr0tdd47 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_hr0tdd47 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-icka1clh-ts0601.json b/tests/data/devices/tze200-icka1clh-ts0601.json index 90ea431b8..1697715f7 100644 --- a/tests/data/devices/tze200-icka1clh-ts0601.json +++ b/tests/data/devices/tze200-icka1clh-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_icka1clh TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_cover.TuyaMoesCover0601", + "quirk_class": "zhaquirks.tuya.ts0601_cover:TuyaMoesCover0601", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-jva8ink8-ts0601.json b/tests/data/devices/tze200-jva8ink8-ts0601.json index f02d69c38..c41b9fe10 100644 --- a/tests/data/devices/tze200-jva8ink8-ts0601.json +++ b/tests/data/devices/tze200-jva8ink8-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_jva8ink8 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_ar0slwnd / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze200-jwsjbxjs-ts0601.json b/tests/data/devices/tze200-jwsjbxjs-ts0601.json index 4e58d4d29..2d79eeaa5 100644 --- a/tests/data/devices/tze200-jwsjbxjs-ts0601.json +++ b/tests/data/devices/tze200-jwsjbxjs-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_jwsjbxjs TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaQuintupleSwitchGP", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaQuintupleSwitchGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-kb5noeto-ts0601.json b/tests/data/devices/tze200-kb5noeto-ts0601.json index d6b520371..5a0afd1b4 100644 --- a/tests/data/devices/tze200-kb5noeto-ts0601.json +++ b/tests/data/devices/tze200-kb5noeto-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_kb5noeto TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_2aaelwxk / TS0601)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-kyfqmmyl-ts0601.json b/tests/data/devices/tze200-kyfqmmyl-ts0601.json index ff3b37b5c..f90b70eca 100644 --- a/tests/data/devices/tze200-kyfqmmyl-ts0601.json +++ b/tests/data/devices/tze200-kyfqmmyl-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_kyfqmmyl TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaTripleSwitchVar03", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaTripleSwitchVar03", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze200-locansqn-ts0601.json b/tests/data/devices/tze200-locansqn-ts0601.json index c179cd1e4..9f99216e2 100644 --- a/tests/data/devices/tze200-locansqn-ts0601.json +++ b/tests/data/devices/tze200-locansqn-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_locansqn TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_lve3dvpy / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-m9skfctm-ts0601.json b/tests/data/devices/tze200-m9skfctm-ts0601.json index 0a52b7497..2bffef694 100644 --- a/tests/data/devices/tze200-m9skfctm-ts0601.json +++ b/tests/data/devices/tze200-m9skfctm-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_m9skfctm TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_aycxwiau / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-mja3fuja-ts0601.json b/tests/data/devices/tze200-mja3fuja-ts0601.json index 370204956..83b4241a8 100644 --- a/tests/data/devices/tze200-mja3fuja-ts0601.json +++ b/tests/data/devices/tze200-mja3fuja-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_mja3fuja TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_mja3fuja / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-mudxchsu-ts0601.json b/tests/data/devices/tze200-mudxchsu-ts0601.json index 79e72f2e7..fd6a9c7a5 100644 --- a/tests/data/devices/tze200-mudxchsu-ts0601.json +++ b/tests/data/devices/tze200-mudxchsu-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_mudxchsu TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_trv.ZonnsmartTV01_ZG", + "quirk_class": "zhaquirks.tuya.ts0601_trv:ZonnsmartTV01_ZG", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-myd45weu-ts0601.json b/tests/data/devices/tze200-myd45weu-ts0601.json index c5e733cac..ff3a06765 100644 --- a/tests/data/devices/tze200-myd45weu-ts0601.json +++ b/tests/data/devices/tze200-myd45weu-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_myd45weu TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_myd45weu / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-ntcy3xu1-ts0601.json b/tests/data/devices/tze200-ntcy3xu1-ts0601.json index d9627b1bd..51bb48869 100644 --- a/tests/data/devices/tze200-ntcy3xu1-ts0601.json +++ b/tests/data/devices/tze200-ntcy3xu1-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_ntcy3xu1 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_ntcy3xu1 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-pay2byax-ts0601.json b/tests/data/devices/tze200-pay2byax-ts0601.json index 4f170e5c6..84c4620cd 100644 --- a/tests/data/devices/tze200-pay2byax-ts0601.json +++ b/tests/data/devices/tze200-pay2byax-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_pay2byax TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_pay2byax / TS0601)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-qrztc3ev-ts0601.json b/tests/data/devices/tze200-qrztc3ev-ts0601.json index 9028cb0cb..481b4469f 100644 --- a/tests/data/devices/tze200-qrztc3ev-ts0601.json +++ b/tests/data/devices/tze200-qrztc3ev-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_qrztc3ev TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_lve3dvpy / TS0601)", "exposes_features": [], "manufacturer_code": 4742, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-qyflbnbj-ts0601.json b/tests/data/devices/tze200-qyflbnbj-ts0601.json index 8ba3be3a0..6580742c6 100644 --- a/tests/data/devices/tze200-qyflbnbj-ts0601.json +++ b/tests/data/devices/tze200-qyflbnbj-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_qyflbnbj TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bq5c8xfe / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-rccxox8p-ts0601.json b/tests/data/devices/tze200-rccxox8p-ts0601.json index 90fa36d57..4bdb5f179 100644 --- a/tests/data/devices/tze200-rccxox8p-ts0601.json +++ b/tests/data/devices/tze200-rccxox8p-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_rccxox8p TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_aycxwiau / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-rxq4iti9-ts0601.json b/tests/data/devices/tze200-rxq4iti9-ts0601.json index a9a468779..a524ef378 100644 --- a/tests/data/devices/tze200-rxq4iti9-ts0601.json +++ b/tests/data/devices/tze200-rxq4iti9-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_rxq4iti9 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-sur6q7ko-ts0601.json b/tests/data/devices/tze200-sur6q7ko-ts0601.json index 3e5009428..41ed04051 100644 --- a/tests/data/devices/tze200-sur6q7ko-ts0601.json +++ b/tests/data/devices/tze200-sur6q7ko-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_sur6q7ko TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_trv.ZonnsmartTV01_ZG", + "quirk_class": "zhaquirks.tuya.ts0601_trv:ZonnsmartTV01_ZG", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-t1blo2bj-ts0601.json b/tests/data/devices/tze200-t1blo2bj-ts0601.json index eda038536..8ce243259 100644 --- a/tests/data/devices/tze200-t1blo2bj-ts0601.json +++ b/tests/data/devices/tze200-t1blo2bj-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_t1blo2bj TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_t1blo2bj / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-vdiuwbkq-ts0601.json b/tests/data/devices/tze200-vdiuwbkq-ts0601.json index 10a36d50d..369510f4d 100644 --- a/tests/data/devices/tze200-vdiuwbkq-ts0601.json +++ b/tests/data/devices/tze200-vdiuwbkq-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_vdiuwbkq TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_cover.TuyaMoesCover0601", + "quirk_class": "zhaquirks.tuya.ts0601_cover:TuyaMoesCover0601", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-viy9ihs7-ts0601.json b/tests/data/devices/tze200-viy9ihs7-ts0601.json index be6c0e541..82a66bf5b 100644 --- a/tests/data/devices/tze200-viy9ihs7-ts0601.json +++ b/tests/data/devices/tze200-viy9ihs7-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_viy9ihs7 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_viy9ihs7 / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-vvmbj46n-ts0601.json b/tests/data/devices/tze200-vvmbj46n-ts0601.json index 122c541c5..335790184 100644 --- a/tests/data/devices/tze200-vvmbj46n-ts0601.json +++ b/tests/data/devices/tze200-vvmbj46n-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_vvmbj46n TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_lve3dvpy / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-ya4ft0w4-ts0601.json b/tests/data/devices/tze200-ya4ft0w4-ts0601.json index 1da92e1c9..2684bc4cb 100644 --- a/tests/data/devices/tze200-ya4ft0w4-ts0601.json +++ b/tests/data/devices/tze200-ya4ft0w4-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_ya4ft0w4 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_gkfbdvyx / TS0601)", "exposes_features": [], "manufacturer_code": 4660, "power_source": "Mains", diff --git a/tests/data/devices/tze200-yjjdcqsq-ts0601.json b/tests/data/devices/tze200-yjjdcqsq-ts0601.json index b838c52cd..c4d6eef42 100644 --- a/tests/data/devices/tze200-yjjdcqsq-ts0601.json +++ b/tests/data/devices/tze200-yjjdcqsq-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_yjjdcqsq TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze200-yojqa8xn-ts0601.json b/tests/data/devices/tze200-yojqa8xn-ts0601.json index a4a71f08b..5b3c3098e 100644 --- a/tests/data/devices/tze200-yojqa8xn-ts0601.json +++ b/tests/data/devices/tze200-yojqa8xn-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_yojqa8xn TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_yojqa8xn / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-yvx5lh6k-ts0601.json b/tests/data/devices/tze200-yvx5lh6k-ts0601.json index baf759420..2cacad1ea 100644 --- a/tests/data/devices/tze200-yvx5lh6k-ts0601.json +++ b/tests/data/devices/tze200-yvx5lh6k-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_yvx5lh6k TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_7bztmfm1 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze200-yw7cahqs-ts0601.json b/tests/data/devices/tze200-yw7cahqs-ts0601.json index c6193aae4..904ce6757 100644 --- a/tests/data/devices/tze200-yw7cahqs-ts0601.json +++ b/tests/data/devices/tze200-yw7cahqs-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE200_yw7cahqs TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TYST11_KGbxAXL2 / GbxAXL2)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-1v1dxkck-ts0601.json b/tests/data/devices/tze204-1v1dxkck-ts0601.json index 3e67fa695..89392ef84 100644 --- a/tests/data/devices/tze204-1v1dxkck-ts0601.json +++ b/tests/data/devices/tze204-1v1dxkck-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_1v1dxkck TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_dimmer.TuyaTripleSwitchDimmerGP", + "quirk_class": "zhaquirks.tuya.ts0601_dimmer:TuyaTripleSwitchDimmerGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-1youk3hj-ts0601.json b/tests/data/devices/tze204-1youk3hj-ts0601.json index eb428a6a4..cc323bcc2 100644 --- a/tests/data/devices/tze204-1youk3hj-ts0601.json +++ b/tests/data/devices/tze204-1youk3hj-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_1youk3hj TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_1youk3hj / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-58of2pfn-ts0601.json b/tests/data/devices/tze204-58of2pfn-ts0601.json index a5dadde5c..79bbef465 100644 --- a/tests/data/devices/tze204-58of2pfn-ts0601.json +++ b/tests/data/devices/tze204-58of2pfn-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_58of2pfn TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaQuadrupleSwitchGP", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaQuadrupleSwitchGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-7ytb3h8u-ts0601.json b/tests/data/devices/tze204-7ytb3h8u-ts0601.json index 2d1353efd..e9d27d552 100644 --- a/tests/data/devices/tze204-7ytb3h8u-ts0601.json +++ b/tests/data/devices/tze204-7ytb3h8u-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_7ytb3h8u TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_a7sghmms / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-9yapgbuv-ts0601.json b/tests/data/devices/tze204-9yapgbuv-ts0601.json index 99df3db4f..b9a1d016f 100644 --- a/tests/data/devices/tze204-9yapgbuv-ts0601.json +++ b/tests/data/devices/tze204-9yapgbuv-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_9yapgbuv TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-bxoo2swd-ts0601.json b/tests/data/devices/tze204-bxoo2swd-ts0601.json index 1a6151845..4f48cfee0 100644 --- a/tests/data/devices/tze204-bxoo2swd-ts0601.json +++ b/tests/data/devices/tze204-bxoo2swd-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_bxoo2swd TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_dimmer.TuyaDoubleSwitchDimmer", + "quirk_class": "zhaquirks.tuya.ts0601_dimmer:TuyaDoubleSwitchDimmer", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze204-c2fmom5z-ts0601.json b/tests/data/devices/tze204-c2fmom5z-ts0601.json index 64c071b91..e8b949e05 100644 --- a/tests/data/devices/tze204-c2fmom5z-ts0601.json +++ b/tests/data/devices/tze204-c2fmom5z-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_c2fmom5z TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_7bztmfm1 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-chbyv06x-ts0601.json b/tests/data/devices/tze204-chbyv06x-ts0601.json index f6517440c..8b95e406e 100644 --- a/tests/data/devices/tze204-chbyv06x-ts0601.json +++ b/tests/data/devices/tze204-chbyv06x-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_chbyv06x TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_yojqa8xn / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-cirvgep4-ts0601.json b/tests/data/devices/tze204-cirvgep4-ts0601.json index 0b0a7da9d..6e96a1c13 100644 --- a/tests/data/devices/tze204-cirvgep4-ts0601.json +++ b/tests/data/devices/tze204-cirvgep4-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_cirvgep4 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-dqolcpcp-ts0601.json b/tests/data/devices/tze204-dqolcpcp-ts0601.json index 682d2d03c..691f98960 100644 --- a/tests/data/devices/tze204-dqolcpcp-ts0601.json +++ b/tests/data/devices/tze204-dqolcpcp-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_dqolcpcp TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaSwitchX12GP", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaSwitchX12GP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-dtzziy1e-ts0601.json b/tests/data/devices/tze204-dtzziy1e-ts0601.json index d56409f92..bb946e398 100644 --- a/tests/data/devices/tze204-dtzziy1e-ts0601.json +++ b/tests/data/devices/tze204-dtzziy1e-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_dtzziy1e TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_sbyx0lm6 / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze204-dwcarsat-ts0601.json b/tests/data/devices/tze204-dwcarsat-ts0601.json index 42a70a43b..e32fb91a7 100644 --- a/tests/data/devices/tze204-dwcarsat-ts0601.json +++ b/tests/data/devices/tze204-dwcarsat-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_dwcarsat TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_dwcarsat / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-ex3rcdha-ts0601.json b/tests/data/devices/tze204-ex3rcdha-ts0601.json index 2900dbfeb..11ee024e0 100644 --- a/tests/data/devices/tze204-ex3rcdha-ts0601.json +++ b/tests/data/devices/tze204-ex3rcdha-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ex3rcdha TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_ex3rcdha / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-gops3slb-ts0601.json b/tests/data/devices/tze204-gops3slb-ts0601.json index fe0c2ff08..42db989ec 100644 --- a/tests/data/devices/tze204-gops3slb-ts0601.json +++ b/tests/data/devices/tze204-gops3slb-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_gops3slb TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_lzriup1j / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-iaeejhvf-ts0601.json b/tests/data/devices/tze204-iaeejhvf-ts0601.json index ced57c4ea..398626af3 100644 --- a/tests/data/devices/tze204-iaeejhvf-ts0601.json +++ b/tests/data/devices/tze204-iaeejhvf-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_iaeejhvf TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_sbyx0lm6 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-k7mfgaen-ts0601.json b/tests/data/devices/tze204-k7mfgaen-ts0601.json index 80a43fc49..9cf2e565c 100644 --- a/tests/data/devices/tze204-k7mfgaen-ts0601.json +++ b/tests/data/devices/tze204-k7mfgaen-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_k7mfgaen TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_k7mfgaen / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-kyhbrfyl-ts0601.json b/tests/data/devices/tze204-kyhbrfyl-ts0601.json index c1a143505..2acc6bef1 100644 --- a/tests/data/devices/tze204-kyhbrfyl-ts0601.json +++ b/tests/data/devices/tze204-kyhbrfyl-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_kyhbrfyl TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_kyhbrfyl / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-ltwbm23f-ts0601.json b/tests/data/devices/tze204-ltwbm23f-ts0601.json index 3bb138d58..0c36cc247 100644 --- a/tests/data/devices/tze204-ltwbm23f-ts0601.json +++ b/tests/data/devices/tze204-ltwbm23f-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ltwbm23f TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_qyr2m29i / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-lzriup1j-ts0601.json b/tests/data/devices/tze204-lzriup1j-ts0601.json index 8ba5a3d01..aa664b110 100644 --- a/tests/data/devices/tze204-lzriup1j-ts0601.json +++ b/tests/data/devices/tze204-lzriup1j-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_lzriup1j TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_lzriup1j / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-mtoaryre-ts0601.json b/tests/data/devices/tze204-mtoaryre-ts0601.json index bdff5292d..9a80ca388 100644 --- a/tests/data/devices/tze204-mtoaryre-ts0601.json +++ b/tests/data/devices/tze204-mtoaryre-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_mtoaryre TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_sbyx0lm6 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-muvkrjr5-ts0601.json b/tests/data/devices/tze204-muvkrjr5-ts0601.json index ddf11abee..9b41ed395 100644 --- a/tests/data/devices/tze204-muvkrjr5-ts0601.json +++ b/tests/data/devices/tze204-muvkrjr5-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_muvkrjr5 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_muvkrjr5 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-nlrfgpny-ts0601.json b/tests/data/devices/tze204-nlrfgpny-ts0601.json index 571988a01..9f9a952c5 100644 --- a/tests/data/devices/tze204-nlrfgpny-ts0601.json +++ b/tests/data/devices/tze204-nlrfgpny-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_nlrfgpny TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_nlrfgpny / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-nqqylykc-ts0601.json b/tests/data/devices/tze204-nqqylykc-ts0601.json index 0ecd41a38..ccd6cb856 100644 --- a/tests/data/devices/tze204-nqqylykc-ts0601.json +++ b/tests/data/devices/tze204-nqqylykc-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_nqqylykc TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_dimmer.TuyaSingleSwitchDimmerGP", + "quirk_class": "zhaquirks.tuya.ts0601_dimmer:TuyaSingleSwitchDimmerGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-ogx8u5z6-ts0601.json b/tests/data/devices/tze204-ogx8u5z6-ts0601.json index bb2d0cc39..8ebb825e3 100644 --- a/tests/data/devices/tze204-ogx8u5z6-ts0601.json +++ b/tests/data/devices/tze204-ogx8u5z6-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ogx8u5z6 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-p3lqqy2r-ts0601.json b/tests/data/devices/tze204-p3lqqy2r-ts0601.json index d694f3d67..9e4fb6fb4 100644 --- a/tests/data/devices/tze204-p3lqqy2r-ts0601.json +++ b/tests/data/devices/tze204-p3lqqy2r-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_p3lqqy2r TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_p3lqqy2r / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze204-ptaqh9tk-ts0601.json b/tests/data/devices/tze204-ptaqh9tk-ts0601.json index 6069bc809..d4d8251b5 100644 --- a/tests/data/devices/tze204-ptaqh9tk-ts0601.json +++ b/tests/data/devices/tze204-ptaqh9tk-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ptaqh9tk TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaSingleSwitchGP", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaSingleSwitchGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-qasjif9e-ts0601.json b/tests/data/devices/tze204-qasjif9e-ts0601.json index dbcf19a4c..213e51bb3 100644 --- a/tests/data/devices/tze204-qasjif9e-ts0601.json +++ b/tests/data/devices/tze204-qasjif9e-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_qasjif9e TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_qasjif9e / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-qvxrkeif-ts0601.json b/tests/data/devices/tze204-qvxrkeif-ts0601.json index 2c5021e79..ae6f8fc58 100644 --- a/tests/data/devices/tze204-qvxrkeif-ts0601.json +++ b/tests/data/devices/tze204-qvxrkeif-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_qvxrkeif TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_qvxrkeif / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-qyr2m29i-ts0601.json b/tests/data/devices/tze204-qyr2m29i-ts0601.json index d2eeb8d86..5a04980ed 100644 --- a/tests/data/devices/tze204-qyr2m29i-ts0601.json +++ b/tests/data/devices/tze204-qyr2m29i-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_qyr2m29i TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_qyr2m29i / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-rtrmfadk-ts0601.json b/tests/data/devices/tze204-rtrmfadk-ts0601.json index 5d3e77419..e092f81ac 100644 --- a/tests/data/devices/tze204-rtrmfadk-ts0601.json +++ b/tests/data/devices/tze204-rtrmfadk-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_rtrmfadk TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_rtrmfadk / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-sooucan5-ts0601.json b/tests/data/devices/tze204-sooucan5-ts0601.json index 6fcf91d52..92353126b 100644 --- a/tests/data/devices/tze204-sooucan5-ts0601.json +++ b/tests/data/devices/tze204-sooucan5-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_sooucan5 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_ar0slwnd / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-sxm7l9xa-ts0601.json b/tests/data/devices/tze204-sxm7l9xa-ts0601.json index c0cc76460..3787a43a6 100644 --- a/tests/data/devices/tze204-sxm7l9xa-ts0601.json +++ b/tests/data/devices/tze204-sxm7l9xa-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_sxm7l9xa TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_sxm7l9xa / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-upagmta9-ts0601.json b/tests/data/devices/tze204-upagmta9-ts0601.json index 25d97e53d..dc6a4b4e4 100644 --- a/tests/data/devices/tze204-upagmta9-ts0601.json +++ b/tests/data/devices/tze204-upagmta9-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_upagmta9 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-uxllnywp-ts0601.json b/tests/data/devices/tze204-uxllnywp-ts0601.json index b741ab08c..25f3d53b7 100644 --- a/tests/data/devices/tze204-uxllnywp-ts0601.json +++ b/tests/data/devices/tze204-uxllnywp-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_uxllnywp TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_uxllnywp / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-vmcgja59-ts0601.json b/tests/data/devices/tze204-vmcgja59-ts0601.json index 8e11a0dc6..48df3027d 100644 --- a/tests/data/devices/tze204-vmcgja59-ts0601.json +++ b/tests/data/devices/tze204-vmcgja59-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_vmcgja59 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_switch.TuyaSwitchX16GP", + "quirk_class": "zhaquirks.tuya.ts0601_switch:TuyaSwitchX16GP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-xnbkhhdr-ts0601.json b/tests/data/devices/tze204-xnbkhhdr-ts0601.json index 147fd7ac7..3603ec9f3 100644 --- a/tests/data/devices/tze204-xnbkhhdr-ts0601.json +++ b/tests/data/devices/tze204-xnbkhhdr-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_xnbkhhdr TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_xnbkhhdr / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-ya4ft0w4-ts0601.json b/tests/data/devices/tze204-ya4ft0w4-ts0601.json index ab2f8522e..1dce6e33f 100644 --- a/tests/data/devices/tze204-ya4ft0w4-ts0601.json +++ b/tests/data/devices/tze204-ya4ft0w4-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ya4ft0w4 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_gkfbdvyx / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-yjjdcqsq-ts0601.json b/tests/data/devices/tze204-yjjdcqsq-ts0601.json index ffc870bc7..dd856f25f 100644 --- a/tests/data/devices/tze204-yjjdcqsq-ts0601.json +++ b/tests/data/devices/tze204-yjjdcqsq-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_yjjdcqsq TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze204-yvx5lh6k-ts0601.json b/tests/data/devices/tze204-yvx5lh6k-ts0601.json index 34f65ad7c..e113c42f4 100644 --- a/tests/data/devices/tze204-yvx5lh6k-ts0601.json +++ b/tests/data/devices/tze204-yvx5lh6k-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_yvx5lh6k TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_7bztmfm1 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-zenj4lxv-ts0601.json b/tests/data/devices/tze204-zenj4lxv-ts0601.json index 9ae1fa22e..86c3587a1 100644 --- a/tests/data/devices/tze204-zenj4lxv-ts0601.json +++ b/tests/data/devices/tze204-zenj4lxv-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_zenj4lxv TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.ts0601_dimmer.TuyaDoubleSwitchDimmerGP", + "quirk_class": "zhaquirks.tuya.ts0601_dimmer:TuyaDoubleSwitchDimmerGP", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze204-ztqnh5cg-ts0601.json b/tests/data/devices/tze204-ztqnh5cg-ts0601.json index dd2e00476..05bf22f5c 100644 --- a/tests/data/devices/tze204-ztqnh5cg-ts0601.json +++ b/tests/data/devices/tze204-ztqnh5cg-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE204_ztqnh5cg TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_qasjif9e / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Mains", diff --git a/tests/data/devices/tze284-0zaf1cr8-ts0601.json b/tests/data/devices/tze284-0zaf1cr8-ts0601.json index 4c70e87e0..b64be678c 100644 --- a/tests/data/devices/tze284-0zaf1cr8-ts0601.json +++ b/tests/data/devices/tze284-0zaf1cr8-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_0zaf1cr8 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(TZE200_0zaf1cr8 / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-3mzb0sdz-ts0601.json b/tests/data/devices/tze284-3mzb0sdz-ts0601.json index 5f4914ec9..31a199c53 100644 --- a/tests/data/devices/tze284-3mzb0sdz-ts0601.json +++ b/tests/data/devices/tze284-3mzb0sdz-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_3mzb0sdz TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_3mzb0sdz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-aao3yzhs-ts0601.json b/tests/data/devices/tze284-aao3yzhs-ts0601.json index 564b41fcf..ec5ffc692 100644 --- a/tests/data/devices/tze284-aao3yzhs-ts0601.json +++ b/tests/data/devices/tze284-aao3yzhs-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_aao3yzhs TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_aao3yzhs / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-c6wv4xyo-ts0601.json b/tests/data/devices/tze284-c6wv4xyo-ts0601.json index 24c143414..5eb07b233 100644 --- a/tests/data/devices/tze284-c6wv4xyo-ts0601.json +++ b/tests/data/devices/tze284-c6wv4xyo-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_c6wv4xyo TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-dikb3dp6-ts0601.json b/tests/data/devices/tze284-dikb3dp6-ts0601.json index 3b038e14f..c174dd50e 100644 --- a/tests/data/devices/tze284-dikb3dp6-ts0601.json +++ b/tests/data/devices/tze284-dikb3dp6-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_dikb3dp6 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_dikb3dp6 / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Mains", diff --git a/tests/data/devices/tze284-fhvpaltk-ts0601.json b/tests/data/devices/tze284-fhvpaltk-ts0601.json index b326665e9..b7fd1dd54 100644 --- a/tests/data/devices/tze284-fhvpaltk-ts0601.json +++ b/tests/data/devices/tze284-fhvpaltk-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_fhvpaltk TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_8zizsafo / TS0601)", "exposes_features": [], "manufacturer_code": 4098, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-kyyu8rbj-ts0601.json b/tests/data/devices/tze284-kyyu8rbj-ts0601.json index 023acd54e..fe5982c50 100644 --- a/tests/data/devices/tze284-kyyu8rbj-ts0601.json +++ b/tests/data/devices/tze284-kyyu8rbj-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_kyyu8rbj TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_kyyu8rbj / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-myd45weu-ts0601.json b/tests/data/devices/tze284-myd45weu-ts0601.json index e2b0e35d9..e8a15e6de 100644 --- a/tests/data/devices/tze284-myd45weu-ts0601.json +++ b/tests/data/devices/tze284-myd45weu-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_myd45weu TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_myd45weu / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-ne4pikwm-ts0601.json b/tests/data/devices/tze284-ne4pikwm-ts0601.json index 3c8de2c88..4b62a26aa 100644 --- a/tests/data/devices/tze284-ne4pikwm-ts0601.json +++ b/tests/data/devices/tze284-ne4pikwm-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_ne4pikwm TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_ne4pikwm / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-o3x45p96-ts0601.json b/tests/data/devices/tze284-o3x45p96-ts0601.json index 173a8eae1..838e0a476 100644 --- a/tests/data/devices/tze284-o3x45p96-ts0601.json +++ b/tests/data/devices/tze284-o3x45p96-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_o3x45p96 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-ogx8u5z6-ts0601.json b/tests/data/devices/tze284-ogx8u5z6-ts0601.json index 3297e750f..8318ec87a 100644 --- a/tests/data/devices/tze284-ogx8u5z6-ts0601.json +++ b/tests/data/devices/tze284-ogx8u5z6-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_ogx8u5z6 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bvu2wnxz / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-oitavov2-ts0601.json b/tests/data/devices/tze284-oitavov2-ts0601.json index 78ff13cff..f011e9f3d 100644 --- a/tests/data/devices/tze284-oitavov2-ts0601.json +++ b/tests/data/devices/tze284-oitavov2-ts0601.json @@ -7,8 +7,8 @@ "friendly_manufacturer": "_TZE284_oitavov2", "friendly_model": "TS0601", "name": "_TZE284_oitavov2 TS0601", - "quirk_applied": false, - "quirk_class": "zigpy.device.Device", + "quirk_applied": true, + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_myd45weu / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", @@ -173,7 +173,7 @@ }, { "cluster_id": "0xef00", - "endpoint_attribute": null, + "endpoint_attribute": "tuya_manufacturer", "attributes": [] } ], diff --git a/tests/data/devices/tze284-qyflbnbj-ts0601.json b/tests/data/devices/tze284-qyflbnbj-ts0601.json index 85177b090..18c9c925c 100644 --- a/tests/data/devices/tze284-qyflbnbj-ts0601.json +++ b/tests/data/devices/tze284-qyflbnbj-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_qyflbnbj TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_bq5c8xfe / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-rjxqso4a-ts0601.json b/tests/data/devices/tze284-rjxqso4a-ts0601.json index 5e6e978d7..914570009 100644 --- a/tests/data/devices/tze284-rjxqso4a-ts0601.json +++ b/tests/data/devices/tze284-rjxqso4a-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_rjxqso4a TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_hr0tdd47 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-rqcuwlsa-ts0601.json b/tests/data/devices/tze284-rqcuwlsa-ts0601.json index af25a8145..f7b040794 100644 --- a/tests/data/devices/tze284-rqcuwlsa-ts0601.json +++ b/tests/data/devices/tze284-rqcuwlsa-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_rqcuwlsa TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_rqcuwlsa / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-sgabhwa6-ts0601.json b/tests/data/devices/tze284-sgabhwa6-ts0601.json index 76e62500e..13db986fc 100644 --- a/tests/data/devices/tze284-sgabhwa6-ts0601.json +++ b/tests/data/devices/tze284-sgabhwa6-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_sgabhwa6 TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE284_aao3yzhs / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-upagmta9-ts0601.json b/tests/data/devices/tze284-upagmta9-ts0601.json index 179166e55..8d570526e 100644 --- a/tests/data/devices/tze284-upagmta9-ts0601.json +++ b/tests/data/devices/tze284-upagmta9-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_upagmta9 TS0601", "quirk_applied": true, - "quirk_class": "zhaquirks.tuya.builder.EnchantedDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE200_upagmta9 / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/tze284-xnbkhhdr-ts0601.json b/tests/data/devices/tze284-xnbkhhdr-ts0601.json index 9a5b05630..cd521c34e 100644 --- a/tests/data/devices/tze284-xnbkhhdr-ts0601.json +++ b/tests/data/devices/tze284-xnbkhhdr-ts0601.json @@ -8,7 +8,7 @@ "friendly_model": "TS0601", "name": "_TZE284_xnbkhhdr TS0601", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.tuya.builder:(_TZE204_xnbkhhdr / TS0601)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/ubisys-j1-5502.json b/tests/data/devices/ubisys-j1-5502.json index d100eaa36..9e51b3e43 100644 --- a/tests/data/devices/ubisys-j1-5502.json +++ b/tests/data/devices/ubisys-j1-5502.json @@ -7,9 +7,11 @@ "friendly_manufacturer": "ubisys", "friendly_model": "J1 (5502)", "name": "ubisys J1 (5502)", - "quirk_applied": false, - "quirk_class": "zigpy.device.Device", - "exposes_features": [], + "quirk_applied": true, + "quirk_class": "zhaquirks.ubisys.cover_j1:(ubisys / J1 (5502))", + "exposes_features": [ + "se_poll_summation" + ], "manufacturer_code": 4660, "power_source": "Mains", "lqi": null, @@ -68,12 +70,6 @@ "cluster_id": "0x0102", "endpoint_attribute": "window_covering", "attributes": [ - { - "id": "0x0007", - "name": "config_status", - "zcl_type": "map8", - "value": 3 - }, { "id": "0x0008", "name": "current_position_lift_percentage", @@ -87,40 +83,64 @@ "unsupported": true }, { - "id": "0x0011", - "name": "installed_closed_limit_lift", - "zcl_type": "uint16", - "unsupported": true + "id": "0x0017", + "name": "window_covering_mode", + "zcl_type": "map8", + "value": 0 + } + ] + }, + { + "cluster_id": "0xfbfe", + "endpoint_attribute": "ubisys_j1_calibration", + "attributes": [ + { + "id": "0x0004", + "name": "calibration_state", + "zcl_type": "enum8", + "value": 0 }, { - "id": "0x0013", - "name": "installed_closed_limit_tilt", - "zcl_type": "uint16", + "id": "0x0002", + "name": "enter_calibration_mode", + "zcl_type": "bool", "unsupported": true }, { - "id": "0x0010", - "name": "installed_open_limit_lift", - "zcl_type": "uint16", + "id": "0x0003", + "name": "exit_calibration_mode", + "zcl_type": "bool", "unsupported": true }, { - "id": "0x0012", - "name": "installed_open_limit_tilt", - "zcl_type": "uint16", + "id": "0x0000", + "name": "prepare_calibration", + "zcl_type": "bool", "unsupported": true }, { - "id": "0x0017", - "name": "window_covering_mode", - "zcl_type": "map8", + "id": "0x0001", + "name": "run_calibration", + "zcl_type": "bool", + "unsupported": true + } + ] + }, + { + "cluster_id": "0xfbff", + "endpoint_attribute": "ubisys_input_config", + "attributes": [ + { + "id": "0x0001", + "name": "detached", + "zcl_type": "bool", "value": 0 }, { "id": "0x0000", - "name": "window_covering_type", + "name": "input_mode", "zcl_type": "enum8", - "value": 5 + "value": 0 } ] } @@ -198,6 +218,114 @@ "class_name": "IdentifyButton", "available": true } + }, + { + "info_object": { + "fallback_name": "Enter calibration mode", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-enter_calibration_mode", + "migrate_unique_ids": [], + "platform": "button", + "class_name": "WriteAttributeButton", + "translation_key": "enter_calibration_mode", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "attribute_name": "enter_calibration_mode", + "attribute_value": true + }, + "state": { + "class_name": "WriteAttributeButton", + "available": true + } + }, + { + "info_object": { + "fallback_name": "Exit calibration mode", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-exit_calibration_mode", + "migrate_unique_ids": [], + "platform": "button", + "class_name": "WriteAttributeButton", + "translation_key": "exit_calibration_mode", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "attribute_name": "exit_calibration_mode", + "attribute_value": true + }, + "state": { + "class_name": "WriteAttributeButton", + "available": true + } + }, + { + "info_object": { + "fallback_name": "Prepare manual calibration", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-prepare_calibration", + "migrate_unique_ids": [], + "platform": "button", + "class_name": "WriteAttributeButton", + "translation_key": "prepare_manual_calibration", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "attribute_name": "prepare_calibration", + "attribute_value": true + }, + "state": { + "class_name": "WriteAttributeButton", + "available": true + } + }, + { + "info_object": { + "fallback_name": "Run auto-calibration", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-run_calibration", + "migrate_unique_ids": [], + "platform": "button", + "class_name": "WriteAttributeButton", + "translation_key": "run_auto_calibration", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "attribute_name": "run_calibration", + "attribute_value": true + }, + "state": { + "class_name": "WriteAttributeButton", + "available": true + } } ], "cover": [ @@ -234,6 +362,421 @@ } } ], + "number": [ + { + "info_object": { + "fallback_name": "Additional steps", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-additional_steps", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "additional_steps", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 100, + "native_min_value": 0, + "native_step": 1, + "native_unit_of_measurement": "%" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Inactive power threshold", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-inactive_power_threshold", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "inactive_power_threshold", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 65.535, + "native_min_value": 0, + "native_step": 0.001, + "native_unit_of_measurement": "W" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Closed limit lift", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_closed_limit_lift_config", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "closed_limit_lift", + "translation_placeholders": null, + "device_class": "distance", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 65535, + "native_min_value": 0, + "native_step": 1, + "native_unit_of_measurement": "cm" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Closed limit tilt", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_closed_limit_tilt_config", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "closed_limit_tilt", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 6553.5, + "native_min_value": 0, + "native_step": 0.1, + "native_unit_of_measurement": "\u00b0" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Open limit lift", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_open_limit_lift_config", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "open_limit_lift", + "translation_placeholders": null, + "device_class": "distance", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 65535, + "native_min_value": 0, + "native_step": 1, + "native_unit_of_measurement": "cm" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Open limit tilt", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_open_limit_tilt_config", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "open_limit_tilt", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 6553.5, + "native_min_value": 0, + "native_step": 0.1, + "native_unit_of_measurement": "\u00b0" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Tilt turn time (open to close)", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-lift_to_tilt_transition_steps", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "tilt_turn_time_open_to_close", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 1310.7, + "native_min_value": 0, + "native_step": 0.02, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Tilt turn time (close to open)", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-lift_to_tilt_transition_steps_2", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "tilt_turn_time_close_to_open", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 1310.7, + "native_min_value": 0, + "native_step": 0.02, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Startup time", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-startup_steps", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "startup_time", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 1310.7, + "native_min_value": 0, + "native_step": 0.02, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Travel time (open to close)", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-total_steps", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "travel_time_open_to_close", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 1310.7, + "native_min_value": 0, + "native_step": 0.02, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Travel time (close to open)", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-total_steps_2", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "travel_time_close_to_open", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 1310.7, + "native_min_value": 0, + "native_step": 0.02, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + }, + { + "info_object": { + "fallback_name": "Turnaround guard time", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-turnaround_guard_time", + "migrate_unique_ids": [], + "platform": "number", + "class_name": "NumberConfigurationEntity", + "translation_key": "turnaround_guard_time", + "translation_placeholders": null, + "device_class": "duration", + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "mode": "box", + "native_max_value": 12.7, + "native_min_value": 0.5, + "native_step": 0.05, + "native_unit_of_measurement": "s" + }, + "state": { + "class_name": "NumberConfigurationEntity", + "available": true, + "state": null + } + } + ], + "select": [ + { + "info_object": { + "fallback_name": "Window covering type", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-window_covering_type_config", + "migrate_unique_ids": [], + "platform": "select", + "class_name": "ZCLEnumSelectEntity", + "translation_key": "window_covering_type", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "enum": "WindowCoveringType", + "options": [ + "Rollershade", + "Rollershade two motors", + "Rollershade exterior", + "Rollershade exterior two motors", + "Drapery", + "Awning", + "Shutter", + "Tilt blind tilt only", + "Tilt blind tilt and lift", + "Projector screen" + ] + }, + "state": { + "class_name": "ZCLEnumSelectEntity", + "available": true, + "state": null + } + } + ], "sensor": [ { "info_object": { @@ -318,6 +861,34 @@ "available": true, "state": "Awning" } + }, + { + "info_object": { + "fallback_name": "Auto-calibration state", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-calibration_state", + "migrate_unique_ids": [], + "platform": "sensor", + "class_name": "EnumSensor", + "translation_key": "auto_calibration_state", + "translation_placeholders": null, + "device_class": "enum", + "state_class": null, + "entity_category": "diagnostic", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "suggested_display_precision": null, + "unit": null + }, + "state": { + "class_name": "EnumSensor", + "available": true, + "state": "Idle" + } } ], "switch": [ @@ -352,6 +923,38 @@ "state": false, "inverted": false } + }, + { + "info_object": { + "fallback_name": "Detached mode", + "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-detached", + "migrate_unique_ids": [], + "platform": "switch", + "class_name": "ConfigurableAttributeSwitch", + "translation_key": "detached", + "translation_placeholders": null, + "device_class": null, + "state_class": null, + "entity_category": "config", + "entity_registry_enabled_default": true, + "enabled": true, + "primary": false, + "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", + "endpoint_id": 1, + "available": true, + "group_id": null, + "attribute_name": "detached", + "invert_attribute_name": null, + "force_inverted": false, + "off_value": 0, + "on_value": 1 + }, + "state": { + "class_name": "ConfigurableAttributeSwitch", + "available": true, + "state": false, + "inverted": false + } } ] }, diff --git a/tests/data/devices/ubisys-s1-5501.json b/tests/data/devices/ubisys-s1-5501.json index d7ec8eb68..a0ceaf0a9 100644 --- a/tests/data/devices/ubisys-s1-5501.json +++ b/tests/data/devices/ubisys-s1-5501.json @@ -8,7 +8,7 @@ "friendly_model": "S1 (5501)", "name": "ubisys S1 (5501)", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.ubisys.switch_s1:(ubisys / S1 (5501))", "exposes_features": [ "se_poll_summation" ], diff --git a/tests/data/devices/universal-electronics-inc-urc4460bc0-x-r.json b/tests/data/devices/universal-electronics-inc-urc4460bc0-x-r.json index 91305d02e..39b8a3cb1 100644 --- a/tests/data/devices/universal-electronics-inc-urc4460bc0-x-r.json +++ b/tests/data/devices/universal-electronics-inc-urc4460bc0-x-r.json @@ -8,7 +8,7 @@ "friendly_model": "URC4460BC0-X-R", "name": "Universal Electronics Inc URC4460BC0-X-R", "quirk_applied": true, - "quirk_class": "zhaquirks.universalelectronics.contact_sensor.ContactSensor", + "quirk_class": "zhaquirks.universalelectronics.contact_sensor:ContactSensor", "exposes_features": [], "manufacturer_code": 4335, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/visonic-mct-340-sma.json b/tests/data/devices/visonic-mct-340-sma.json index 29c07652e..afc838453 100644 --- a/tests/data/devices/visonic-mct-340-sma.json +++ b/tests/data/devices/visonic-mct-340-sma.json @@ -8,7 +8,7 @@ "friendly_model": "MCT-340 SMA", "name": "Visonic MCT-340 SMA", "quirk_applied": true, - "quirk_class": "zhaquirks.visonic.mct340.MCT340", + "quirk_class": "zhaquirks.visonic.mct340:MCT340", "exposes_features": [], "manufacturer_code": 4113, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/xiaomi-lywsd03mmc.json b/tests/data/devices/xiaomi-lywsd03mmc.json index 2f260c899..54e68b602 100644 --- a/tests/data/devices/xiaomi-lywsd03mmc.json +++ b/tests/data/devices/xiaomi-lywsd03mmc.json @@ -8,7 +8,7 @@ "friendly_model": "LYWSD03MMC", "name": "Xiaomi LYWSD03MMC", "quirk_applied": true, - "quirk_class": "zigpy.quirks.v2.CustomDeviceV2", + "quirk_class": "zhaquirks.xiaomi.custom.z03mmc:(Xiaomi / LYWSD03MMC)", "exposes_features": [], "manufacturer_code": 4417, "power_source": "Battery or Unknown", diff --git a/tests/data/devices/xiaoyan-terncy-sd01.json b/tests/data/devices/xiaoyan-terncy-sd01.json index 8af216d9c..3404943a9 100644 --- a/tests/data/devices/xiaoyan-terncy-sd01.json +++ b/tests/data/devices/xiaoyan-terncy-sd01.json @@ -8,7 +8,7 @@ "friendly_model": "TERNCY-SD01", "name": "Xiaoyan TERNCY-SD01", "quirk_applied": true, - "quirk_class": "zhaquirks.terncy.sd01.TerncyKnobSmartDimmer", + "quirk_class": "zhaquirks.terncy.sd01:TerncyKnobSmartDimmer", "exposes_features": [], "manufacturer_code": 4648, "power_source": "Battery or Unknown", diff --git a/tests/test_inovelli.py.bak b/tests/test_inovelli.py.bak new file mode 100644 index 000000000..2e8399ffc --- /dev/null +++ b/tests/test_inovelli.py.bak @@ -0,0 +1,164 @@ +"""Test Inovelli cluster handler.""" + +from unittest.mock import AsyncMock, patch + +import pytest +from zhaquirks.inovelli.types import AllLEDEffectType + +from tests.common import join_zigpy_device, zigpy_device_from_json +from zha.application.gateway import Gateway +from zha.zigbee.cluster_handlers.const import CLUSTER_HANDLER_INOVELLI + + +async def test_inovelli_all_led_effect_with_string_effect_type( + zha_gateway: Gateway, +) -> None: + """Test that issue_all_led_effect accepts string effect types. + + Regression test for https://github.com/home-assistant/core/issues/159053 + """ + zigpy_dev = await zigpy_device_from_json( + zha_gateway.application_controller, + "tests/data/devices/inovelli-vzm31-sn.json", + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_dev) + + # Get the Inovelli cluster handler + inovelli_cluster_handler = None + for endpoint in zha_device.endpoints.values(): + for ch in endpoint.all_cluster_handlers.values(): + if ch.name == CLUSTER_HANDLER_INOVELLI: + inovelli_cluster_handler = ch + break + + assert inovelli_cluster_handler is not None + + # Mock at the endpoint level to capture the serialized frame + with patch.object( + inovelli_cluster_handler.cluster.endpoint, + "request", + new_callable=AsyncMock, + ) as mock_request: + # Pass effect_type as a string (like the UI does) + # This should NOT raise ValueError + await inovelli_cluster_handler.issue_all_led_effect( + effect_type="Fast_Falling", + color=200, + level=100, + duration=3, + ) + + # Verify the request was made + assert len(mock_request.mock_calls) == 1 + + # The serialized frame data should contain the correct byte value + # Frame format: [frame_control, mfr_code_lo, mfr_code_hi, seq, cmd_id, effect, color, level, duration] + frame_data = mock_request.mock_calls[0].kwargs["data"] + assert frame_data[5] == 0x0B # Fast_Falling = 11 + assert frame_data[6] == 200 # color + assert frame_data[7] == 100 # level + assert frame_data[8] == 3 # duration + + +async def test_inovelli_individual_led_effect_with_string_effect_type( + zha_gateway: Gateway, +) -> None: + """Test that issue_individual_led_effect accepts string effect types. + + Regression test for https://github.com/home-assistant/core/issues/159053 + """ + zigpy_dev = await zigpy_device_from_json( + zha_gateway.application_controller, + "tests/data/devices/inovelli-vzm31-sn.json", + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_dev) + + # Get the Inovelli cluster handler + inovelli_cluster_handler = None + for endpoint in zha_device.endpoints.values(): + for ch in endpoint.all_cluster_handlers.values(): + if ch.name == CLUSTER_HANDLER_INOVELLI: + inovelli_cluster_handler = ch + break + + assert inovelli_cluster_handler is not None + + # Mock at the endpoint level to capture the serialized frame + with patch.object( + inovelli_cluster_handler.cluster.endpoint, + "request", + new_callable=AsyncMock, + ) as mock_request: + # Pass effect_type as a string (like the UI does) + # This should NOT raise ValueError + await inovelli_cluster_handler.issue_individual_led_effect( + led_number=1, + effect_type="Solid", + color=200, + level=100, + duration=3, + ) + + # Verify the request was made + assert len(mock_request.mock_calls) == 1 + + # The serialized frame data should contain the correct byte values + # Frame format: [frame_control, mfr_code_lo, mfr_code_hi, seq, cmd_id, led_num, effect, color, level, duration] + frame_data = mock_request.mock_calls[0].kwargs["data"] + assert frame_data[5] == 1 # led_number + assert frame_data[6] == 0x01 # Solid = 1 + assert frame_data[7] == 200 # color + assert frame_data[8] == 100 # level + assert frame_data[9] == 3 # duration + + +@pytest.mark.parametrize( + ("effect_type", "expected_value"), + [ + (AllLEDEffectType.Fast_Falling, 11), # Enum value + (11, 11), # Integer value + ("Fast_Falling", 11), # String value + ], +) +async def test_inovelli_all_led_effect_accepts_various_types( + zha_gateway: Gateway, + effect_type: AllLEDEffectType | int | str, + expected_value: int, +) -> None: + """Test that issue_all_led_effect accepts enum, int, and string effect types.""" + zigpy_dev = await zigpy_device_from_json( + zha_gateway.application_controller, + "tests/data/devices/inovelli-vzm31-sn.json", + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_dev) + + # Get the Inovelli cluster handler + inovelli_cluster_handler = None + for endpoint in zha_device.endpoints.values(): + for ch in endpoint.all_cluster_handlers.values(): + if ch.name == CLUSTER_HANDLER_INOVELLI: + inovelli_cluster_handler = ch + break + + assert inovelli_cluster_handler is not None + + # Mock at the endpoint level to capture the serialized frame + with patch.object( + inovelli_cluster_handler.cluster.endpoint, + "request", + new_callable=AsyncMock, + ) as mock_request: + # Should not raise ValueError for any of these types + await inovelli_cluster_handler.issue_all_led_effect( + effect_type=effect_type, + color=200, + level=100, + duration=3, + ) + + # Verify the request was made + assert len(mock_request.mock_calls) == 1 + + # All types should result in the same byte value in the serialized frame + frame_data = mock_request.mock_calls[0].kwargs["data"] + assert frame_data[5] == expected_value From c2459c42e63a22df92eefad669def03292a69bd4 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:22:42 -0400 Subject: [PATCH 26/41] Ensure quirk registry entries are fully hashable --- zha/quirks.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/zha/quirks.py b/zha/quirks.py index e57ad61a4..deee78cd9 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -5,7 +5,7 @@ from collections import defaultdict from collections.abc import Callable, Iterator import contextlib -from dataclasses import dataclass +from dataclasses import dataclass, field import inspect import logging from pathlib import Path @@ -121,15 +121,15 @@ def from_class(cls, target: type) -> QuirkSource: ) -def make_zigpy_device_replacement( - device_cls: type[zigpy.device.Device], -) -> Callable[[zigpy.device.Device], zigpy.device.Device]: - """Return a transform wrapping a device in `device_cls` (a `BaseCustomDevice`).""" +@dataclass(frozen=True) +class ReplaceZigpyDevice: + """A transform wrapping a device in `device_cls` (a `BaseCustomDevice`).""" - def _replace(device: zigpy.device.Device) -> zigpy.device.Device: - return device_cls(device.application, device.ieee, device.nwk, device) + device_cls: type[zigpy.device.Device] - return _replace + def __call__(self, device: zigpy.device.Device) -> zigpy.device.Device: + """Replace a zigpy device.""" + return self.device_cls(device.application, device.ieee, device.nwk, device) @dataclass(frozen=True) @@ -141,7 +141,9 @@ class QuirkRegistryEntry: Callable[[zigpy.device.Device], zigpy.device.Device], ... ] = () zha_device_factory: Callable[..., Device] | None = None - source: QuirkSource | None = None + # Excluded from equality so identical quirks registered at different sites still + # deduplicate. + source: QuirkSource | None = field(default=None, compare=False) class DeviceRegistry: @@ -159,9 +161,10 @@ def __init__(self) -> None: self._wildcard_registry: list[QuirkRegistryEntry] = [] def register(self, entry: QuirkRegistryEntry) -> QuirkRegistryEntry: - """Add a quirk entry to the registry.""" + """Add a quirk entry to the registry, ignoring exact duplicates.""" if not entry.device_match.applies_to: - self._wildcard_registry.insert(0, entry) + if entry not in self._wildcard_registry: + self._wildcard_registry.insert(0, entry) return entry for manufacturer, model in entry.device_match.applies_to: @@ -170,7 +173,9 @@ def register(self, entry: QuirkRegistryEntry) -> QuirkRegistryEntry: f"{entry!r} must specify a manufacturer and/or model to match" ) - self._registry[ModelInfo(manufacturer, model)].insert(0, entry) + entries = self._registry[ModelInfo(manufacturer, model)] + if entry not in entries: + entries.insert(0, entry) return entry @@ -181,7 +186,7 @@ def register_device(self, cls: type[Device]) -> type[Device]: transforms: list[Callable[[zigpy.device.Device], zigpy.device.Device]] = [] if cls._zigpy_device_cls is not None: - transforms.append(make_zigpy_device_replacement(cls._zigpy_device_cls)) + transforms.append(ReplaceZigpyDevice(cls._zigpy_device_cls)) transforms.extend(cls._zigpy_device_transforms) self.register( From e180fa84d8373282777b9901df73c34a5dedb467 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:53:50 -0400 Subject: [PATCH 27/41] Log when quirks fail to transform a device --- zha/quirks.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zha/quirks.py b/zha/quirks.py index deee78cd9..df8377446 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -238,9 +238,15 @@ def resolve(self, zigpy_device: zigpy.device.Device) -> zigpy.device.Device: entry, ) + # A failing quirk must not prevent the device from loading: log and fall + # back to the bare device rather than letting the exception propagate. resolved_device = zigpy_device - for transform in entry.zigpy_transforms: - resolved_device = transform(resolved_device) + try: + for transform in entry.zigpy_transforms: + resolved_device = transform(resolved_device) + except Exception: + _LOGGER.exception("Failed to load quirk for %s", zigpy_device) + return zigpy_device setattr(resolved_device, QUIRK_REGISTRY_ENTRY_ATTR, entry) From 17a3d63599a39d66d9c4a806bc90a52dfeb59374 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:08:17 -0400 Subject: [PATCH 28/41] Bump zigpy and quirks --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61fe8f876..de4345a63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,8 @@ dependencies = [ "zigpy-deconz==0.25.5", "zigpy-xbee==0.21.1", "zigpy-zigate==0.14.0", - "zha-quirks>=1.2.0", - "zigpy==1.6.0", + "zha-quirks>=2.0.0", + "zigpy==2.0.0", ] [tool.setuptools.packages.find] From 142191181a3309705ec87f09da3b047a108c0557 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:50:30 -0400 Subject: [PATCH 29/41] Move `targets_cluster` out of quirks and into ZHA --- zha/application/platforms/__init__.py | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/zha/application/platforms/__init__.py b/zha/application/platforms/__init__.py index 3c0c210cd..608afe56b 100644 --- a/zha/application/platforms/__init__.py +++ b/zha/application/platforms/__init__.py @@ -617,6 +617,41 @@ def cluster(self) -> zigpy.zcl.Cluster: """Return the ZCL cluster backing this entity.""" return self._cluster + def targets_cluster( + self, + cluster_id: int, + cluster_type: zigpy.zcl.ClusterType | None = None, + ) -> bool: + """Return True if this entity targets the given cluster.""" + match = self._cluster_match + if match is None: + # Generated quirks-v2 entities have no class-level `_cluster_match` + # but do have a concrete backing cluster; match against it directly. + cluster = self.cluster + if cluster.cluster_id != cluster_id: + return False + actual_type = ( + zigpy.zcl.ClusterType.Client + if cluster.is_client + else zigpy.zcl.ClusterType.Server + ) + return cluster_type is None or cluster_type == actual_type + + in_server = ( + cluster_id in match.server_clusters + or cluster_id in match.optional_server_clusters + ) + in_client = ( + cluster_id in match.client_clusters + or cluster_id in match.optional_client_clusters + ) + + if cluster_type == zigpy.zcl.ClusterType.Server: + return in_server + if cluster_type == zigpy.zcl.ClusterType.Client: + return in_client + return in_server or in_client + @property def should_poll(self) -> bool: """Return True if we need to poll for state changes.""" From 342c6406a21559a0a25dc94eb452ed2b6e5921d0 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:08:21 -0400 Subject: [PATCH 30/41] Drop zhaquirks as a runtime dependency --- pyproject.toml | 2 +- .../platforms/binary_sensor/__init__.py | 2 +- zha/application/platforms/button/__init__.py | 6 +- zha/application/platforms/const.py | 1 - zha/application/platforms/legacy_quirks.py | 118 ++++++++++++++++++ zha/application/platforms/number/__init__.py | 2 +- zha/application/platforms/select.py | 18 +-- zha/application/platforms/sensor/__init__.py | 14 ++- zha/application/platforms/switch.py | 2 +- zha/application/platforms/virtual.py | 2 +- 10 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 zha/application/platforms/legacy_quirks.py diff --git a/pyproject.toml b/pyproject.toml index de4345a63..fd9042ee8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ dependencies = [ "zigpy-deconz==0.25.5", "zigpy-xbee==0.21.1", "zigpy-zigate==0.14.0", - "zha-quirks>=2.0.0", "zigpy==2.0.0", ] @@ -54,6 +53,7 @@ testing = [ "python-slugify", "ruff==0.15.16", "uv>=0.11.16", + "zha-quirks>=2.0.0", ] ci = [ {include-group = "testing"}, diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index 01f7e2196..688cb706c 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -39,12 +39,12 @@ BinarySensorDeviceClass, ) from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, IKEA_AIR_PURIFIER_CLUSTER, SMARTTHINGS_ACCELERATION_CLUSTER, TUYA_MANUFACTURER_CLUSTER, ) from zha.application.platforms.helpers import validate_device_class +from zha.application.platforms.legacy_quirks import AQARA_OPPLE_CLUSTER from zha.quirks import DANFOSS_ALLY_THERMOSTAT if TYPE_CHECKING: diff --git a/zha/application/platforms/button/__init__.py b/zha/application/platforms/button/__init__.py index bae359918..ec664037c 100644 --- a/zha/application/platforms/button/__init__.py +++ b/zha/application/platforms/button/__init__.py @@ -21,10 +21,8 @@ register_entity, ) from zha.application.platforms.button.const import DEFAULT_DURATION, ButtonDeviceClass -from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, - TUYA_MANUFACTURER_CLUSTER, -) +from zha.application.platforms.const import TUYA_MANUFACTURER_CLUSTER +from zha.application.platforms.legacy_quirks import AQARA_OPPLE_CLUSTER if TYPE_CHECKING: from zha.zigbee.device import Device diff --git a/zha/application/platforms/const.py b/zha/application/platforms/const.py index 6a60cec05..8af49f67e 100644 --- a/zha/application/platforms/const.py +++ b/zha/application/platforms/const.py @@ -2,7 +2,6 @@ from typing import Final -AQARA_OPPLE_CLUSTER: Final[int] = 0xFCC0 IKEA_AIR_PURIFIER_CLUSTER: Final[int] = 0xFC7D IKEA_REMOTE_CLUSTER: Final[int] = 0xFC80 IKEA_SHORTCUT_V1_CLUSTER: Final[int] = 0xFC7F diff --git a/zha/application/platforms/legacy_quirks.py b/zha/application/platforms/legacy_quirks.py new file mode 100644 index 000000000..508a7bb87 --- /dev/null +++ b/zha/application/platforms/legacy_quirks.py @@ -0,0 +1,118 @@ +"""Constants copied from legacy zha-quirks device handlers. This will be removed.""" + +from __future__ import annotations + +from typing import Final + +from zigpy import types as t + +AQARA_OPPLE_CLUSTER: Final[int] = 0xFCC0 + + +class MagnetAC01OppleCluster: + """Mirrors `zhaquirks.xiaomi.aqara.magnet_ac01.OppleCluster`.""" + + cluster_id = AQARA_OPPLE_CLUSTER + + class DetectionDistance(t.enum8): + """Detection distance.""" + + TenMillimeters = 0x01 + TwentyMillimeters = 0x02 + ThirtyMillimeters = 0x03 + + +class T2RelayOppleCluster: + """Mirrors `zhaquirks.xiaomi.aqara.switch_acn047.OppleCluster`.""" + + cluster_id = AQARA_OPPLE_CLUSTER + + class SwitchType(t.enum8): + """Switch type.""" + + Toggle = 0x01 + Momentary = 0x02 + NoSwitch = 0x03 + + class StartupOnOff(t.enum8): + """Startup mode.""" + + On = 0x00 + Previous = 0x01 + Off = 0x02 + Toggle = 0x03 + + class DecoupledMode(t.enum8): + """Decoupled mode.""" + + Decoupled = 0x00 + ControlRelay = 0x01 + + class SwitchMode(t.enum8): + """Switch Mode.""" + + Power = 0x00 + Pulse = 0x01 + Dry = 0x03 + + +class DanfossViewingDirectionEnum(t.enum8): + """Default or Inverted screen orientation.""" + + Default = 0x00 + Inverted = 0x01 + + +class DanfossAdaptationRunControlEnum(t.enum8): + """Initiate or Cancel adaptation run.""" + + Nothing = 0x00 + Initiate = 0x01 + Cancel = 0x02 + + +class DanfossExerciseDayOfTheWeekEnum(t.enum8): + """Day of the week.""" + + Sunday = 0 + Monday = 1 + Tuesday = 2 + Wednesday = 3 + Thursday = 4 + Friday = 5 + Saturday = 6 + Undefined = 7 + + +class DanfossOpenWindowDetectionEnum(t.enum8): + """Danfoss open window detection judgments.""" + + Quarantine = 0x00 + Closed = 0x01 + Maybe = 0x02 + Open = 0x03 + External = 0x04 + + +class DanfossSoftwareErrorCodeBitmap(t.bitmap16): + """Danfoss software error code bitmap.""" + + Top_pcb_sensor_error = 0x0001 + Side_pcb_sensor_error = 0x0002 + Non_volatile_memory_error = 0x0004 + Unknown_hw_error = 0x0008 + Motor_error = 0x0020 + Invalid_internal_communication = 0x0080 + Invalid_clock_information = 0x0200 + Radio_communication_error = 0x0800 + Encoder_jammed = 0x1000 + Low_battery = 0x2000 + Critical_low_battery = 0x4000 + + +class DanfossAdaptationRunStatusBitmap(t.bitmap8): + """Danfoss Adaptation run status bitmap.""" + + In_progress = 0x0001 + Valve_characteristic_found = 0x0002 + Valve_characteristic_lost = 0x0004 diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 5afe37360..40f4f8e84 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -33,13 +33,13 @@ register_entity, ) from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, IKEA_AIR_PURIFIER_CLUSTER, INOVELLI_CLUSTER, SINOPE_MANUFACTURER_CLUSTER, TUYA_MANUFACTURER_CLUSTER, ) from zha.application.platforms.helpers import validate_device_class +from zha.application.platforms.legacy_quirks import AQARA_OPPLE_CLUSTER from zha.application.platforms.number.bacnet import BACNET_UNITS_TO_HA_UNITS from zha.application.platforms.number.const import ICONS, NumberDeviceClass, NumberMode from zha.quirks import DANFOSS_ALLY_THERMOSTAT diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index 14c271457..5131bf6c6 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -9,9 +9,6 @@ import logging from typing import TYPE_CHECKING, Any, cast -from zhaquirks.danfoss import thermostat as danfoss_thermostat -from zhaquirks.xiaomi.aqara.magnet_ac01 import OppleCluster as MagnetAC01OppleCluster -from zhaquirks.xiaomi.aqara.switch_acn047 import OppleCluster as T2RelayOppleCluster from zigpy import types from zigpy.zcl import ( AttributeReadEvent, @@ -44,11 +41,18 @@ register_entity, ) from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, INOVELLI_CLUSTER, SINOPE_MANUFACTURER_CLUSTER, TUYA_MANUFACTURER_CLUSTER, ) +from zha.application.platforms.legacy_quirks import ( + AQARA_OPPLE_CLUSTER, + DanfossAdaptationRunControlEnum, + DanfossExerciseDayOfTheWeekEnum, + DanfossViewingDirectionEnum, + MagnetAC01OppleCluster, + T2RelayOppleCluster, +) from zha.application.platforms.siren import AdvancedSiren from zha.quirks import ( BEGA_LIGHT_SWITCHABLE_WHITE, @@ -984,7 +988,7 @@ class DanfossExerciseDayOfTheWeek(ZCLEnumSelectEntity): _unique_id_suffix = "exercise_day_of_week" _attribute_name = "exercise_day_of_week" _attr_translation_key: str = "exercise_day_of_week" - _enum = danfoss_thermostat.DanfossExerciseDayOfTheWeekEnum + _enum = DanfossExerciseDayOfTheWeekEnum _cluster_id = Thermostat.cluster_id _cluster_match = ClusterMatch( @@ -1137,7 +1141,7 @@ class DanfossAdaptationRunControl(ZCLEnumSelectEntity): _unique_id_suffix = "adaptation_run_control" _attribute_name = "adaptation_run_control" _attr_translation_key: str = "adaptation_run_command" - _enum = danfoss_thermostat.DanfossAdaptationRunControlEnum + _enum = DanfossAdaptationRunControlEnum _cluster_id = Thermostat.cluster_id _cluster_match = ClusterMatch( @@ -1194,7 +1198,7 @@ class DanfossViewingDirection(ZCLEnumSelectEntity): _unique_id_suffix = "viewing_direction" _attribute_name = "viewing_direction" _attr_translation_key: str = "viewing_direction" - _enum = danfoss_thermostat.DanfossViewingDirectionEnum + _enum = DanfossViewingDirectionEnum _cluster_id = UserInterface.cluster_id _cluster_match = ClusterMatch( diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 74c6e3467..5ac490b1b 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -14,7 +14,6 @@ import typing from typing import TYPE_CHECKING, Any, cast -from zhaquirks.danfoss import thermostat as danfoss_thermostat from zigpy import types from zigpy.state import Counter, State from zigpy.zcl import ( @@ -72,7 +71,6 @@ ) from zha.application.platforms.climate.const import HVACAction from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, IKEA_AIR_PURIFIER_CLUSTER, INOVELLI_CLUSTER, SMARTTHINGS_HUMIDITY_CLUSTER, @@ -81,6 +79,12 @@ VOC_LEVEL_CLUSTER, ) from zha.application.platforms.helpers import validate_device_class +from zha.application.platforms.legacy_quirks import ( + AQARA_OPPLE_CLUSTER, + DanfossAdaptationRunStatusBitmap, + DanfossOpenWindowDetectionEnum, + DanfossSoftwareErrorCodeBitmap, +) from zha.application.platforms.number.bacnet import BACNET_UNITS_TO_HA_UNITS from zha.application.platforms.sensor.const import ( ANALOG_INPUT_APPTYPE_DEV_CLASS, @@ -3676,7 +3680,7 @@ class DanfossOpenWindowDetection(EnumSensor): _unique_id_suffix = "open_window_detection" _attribute_name = "open_window_detection" _attr_translation_key: str = "open_window_detected" - _enum = danfoss_thermostat.DanfossOpenWindowDetectionEnum + _enum = DanfossOpenWindowDetectionEnum _cluster_id = Thermostat.cluster_id _cluster_match = ClusterMatch( @@ -3737,7 +3741,7 @@ class DanfossAdaptationRunStatus(BitMapSensor): _attribute_name = "adaptation_run_status" _attr_translation_key: str = "adaptation_run_status" _attr_entity_category = EntityCategory.DIAGNOSTIC - _bitmap = danfoss_thermostat.DanfossAdaptationRunStatusBitmap + _bitmap = DanfossAdaptationRunStatusBitmap _cluster_id = Thermostat.cluster_id _cluster_match = ClusterMatch( @@ -3799,7 +3803,7 @@ class DanfossSoftwareErrorCode(BitMapSensor): _attribute_name = "sw_error_code" _attr_translation_key: str = "software_error" _attr_entity_category = EntityCategory.DIAGNOSTIC - _bitmap = danfoss_thermostat.DanfossSoftwareErrorCodeBitmap + _bitmap = DanfossSoftwareErrorCodeBitmap _cluster_id = Diagnostic.cluster_id _cluster_match = ClusterMatch( diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index 0ba5ae56f..a6474a2ce 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -39,12 +39,12 @@ register_group_entity, ) from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, IKEA_AIR_PURIFIER_CLUSTER, INOVELLI_CLUSTER, SINOPE_MANUFACTURER_CLUSTER, TUYA_MANUFACTURER_CLUSTER, ) +from zha.application.platforms.legacy_quirks import AQARA_OPPLE_CLUSTER from zha.application.platforms.light.const import LIGHT_PROFILE_DEVICE_TYPES from zha.exceptions import ZHAException from zha.quirks import DANFOSS_ALLY_THERMOSTAT, TUYA_PLUG_ONOFF diff --git a/zha/application/platforms/virtual.py b/zha/application/platforms/virtual.py index 321685dc9..4585ca960 100644 --- a/zha/application/platforms/virtual.py +++ b/zha/application/platforms/virtual.py @@ -39,7 +39,6 @@ register_entity, ) from zha.application.platforms.const import ( - AQARA_OPPLE_CLUSTER, IKEA_REMOTE_CLUSTER, IKEA_SHORTCUT_V1_CLUSTER, INOVELLI_CLUSTER, @@ -50,6 +49,7 @@ SONOFF_CLUSTER, TUYA_MANUFACTURER_CLUSTER, ) +from zha.application.platforms.legacy_quirks import AQARA_OPPLE_CLUSTER from zha.exceptions import ZHAException from zha.quirks import TUYA_PLUG_MANUFACTURER from zha.zigbee.endpoint import cluster_event_unique_id, split_event_arg From 6b5de03c533b25b7916a62bd0cac9632e223b7cf Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:48:27 -0400 Subject: [PATCH 31/41] Add a test for entity reinterviewing events --- tests/test_gateway.py | 123 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 3 deletions(-) diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 423939365..6ec360117 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -10,7 +10,7 @@ from zigpy.config import CONF_NWK, CONF_NWK_COUNTRY_CODE from zigpy.profiles import zha import zigpy.types -from zigpy.zcl.clusters import general, lighting +from zigpy.zcl.clusters import general, lighting, measurement import zigpy.zdo.types import zigpy.zdo.types as zdo_t from zigpy.zdo.types import LogicalType, NodeDescriptor @@ -40,8 +40,8 @@ from zha.application.helpers import ZHAData from zha.application.platforms import GroupEntity from zha.application.platforms.light.const import EFFECT_OFF, LightEntityFeature -from zha.quirks import DeviceRegistry -from zha.zigbee.device import Device +from zha.quirks import DeviceMatch, DeviceRegistry, ModelInfo, QuirkRegistryEntry +from zha.zigbee.device import Device, DeviceEntityAddedEvent, DeviceEntityRemovedEvent from zha.zigbee.group import Group, GroupMemberReference IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8" @@ -723,6 +723,123 @@ async def test_reinterview_rebuilds_in_place_when_quirk_unchanged( assert zha_gateway.get_device(zigpy_device.ieee) is zha_device +async def test_reinterview_in_place_events(zha_gateway: Gateway) -> None: + """An in-place reinterview that changes the entity set emits add/remove events.""" + + # For simplicity, assume the device changes its model during a reinterview + registry = DeviceRegistry() + registry.register( + QuirkRegistryEntry( + device_match=DeviceMatch( + applies_to=(ModelInfo("Fake_Manufacturer", "Model_A"),) + ) + ) + ) + registry.register( + QuirkRegistryEntry( + device_match=DeviceMatch( + applies_to=(ModelInfo("Fake_Manufacturer", "Model_B"),) + ) + ) + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [ + general.Basic.cluster_id, + general.OnOff.cluster_id, + measurement.TemperatureMeasurement.cluster_id, + ], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + SIG_EP_PROFILE: zha.PROFILE_ID, + } + }, + manufacturer="Fake_Manufacturer", + model="Model_A", + registry=registry, + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + assert zha_device._initialized is True + + ieee = zigpy_device.ieee + + before = {e.unique_id for e in zha_device.platform_entities.values()} + assert "00:0d:6f:00:0a:90:69:e7-1-1026" in before + assert "00:0d:6f:00:0a:90:69:e7-1-1029" not in before + + events = [] + zha_device.on_all_events(events.append) + + # Re-interview into a device that drops the temperature sensor and adds a humidity + # sensor (the OnOff switch is unchanged). + reinterviewed = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [ + general.Basic.cluster_id, + general.OnOff.cluster_id, + measurement.RelativeHumidity.cluster_id, + ], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + SIG_EP_PROFILE: zha.PROFILE_ID, + } + }, + manufacturer="Fake_Manufacturer", + model="Model_B", + registry=registry, + ) + zha_gateway.device_reinterviewed(reinterviewed) + await zha_gateway.async_block_till_done() + + # Rebuilt in place: same object, still initialized, new zigpy device bound. + assert zha_gateway.get_device(ieee) is zha_device + assert zha_device.device is reinterviewed + assert zha_device._initialized is True + + after = {e.unique_id for e in zha_device.platform_entities.values()} + assert "00:0d:6f:00:0a:90:69:e7-1-1029" in after + assert "00:0d:6f:00:0a:90:69:e7-1-1026" not in after + + entity_events = [ + e + for e in events + if isinstance(e, (DeviceEntityAddedEvent, DeviceEntityRemovedEvent)) + ] + assert entity_events == [ + # Old ones were removed + DeviceEntityRemovedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-0-rssi" + ), + DeviceEntityRemovedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-0-lqi" + ), + DeviceEntityRemovedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-1026" + ), + DeviceEntityRemovedEvent( + platform=Platform.SWITCH, unique_id="00:0d:6f:00:0a:90:69:e7-1-6" + ), + # And new ones added + DeviceEntityAddedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-0-rssi" + ), + DeviceEntityAddedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-0-lqi" + ), + DeviceEntityAddedEvent( + platform=Platform.SENSOR, unique_id="00:0d:6f:00:0a:90:69:e7-1-1029" + ), + DeviceEntityAddedEvent( + platform=Platform.SWITCH, unique_id="00:0d:6f:00:0a:90:69:e7-1-6" + ), + ] + + def test_gateway_raw_device_initialized( zha_gateway: Gateway, ) -> None: From f17cb124f4081e072e34838837c9c0bd270b047b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:56:51 -0400 Subject: [PATCH 32/41] Fix reinterviewing logic per review --- zha/application/gateway.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zha/application/gateway.py b/zha/application/gateway.py index 0a557fa8c..ecff916d1 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -489,9 +489,21 @@ async def _async_device_reinterviewed( return old_entry = getattr(zha_device.device, QUIRK_REGISTRY_ENTRY_ATTR, None) + old_factory = ( + old_entry.zha_device_factory + if old_entry is not None and old_entry.zha_device_factory + else Device + ) + new_entry = getattr(new_zigpy_device, QUIRK_REGISTRY_ENTRY_ATTR, None) + new_factory = ( + new_entry.zha_device_factory + if new_entry is not None and new_entry.zha_device_factory + else Device + ) - if old_entry is new_entry: + # Only a `Device` swap requires a new object + if new_factory is old_factory: _LOGGER.debug( "Rebuilding device %s:%s after reinterview", new_zigpy_device.nwk, From a5ab718e95e8325a9bff934c367fba39c8a4e98c Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:02:54 -0400 Subject: [PATCH 33/41] Add an explicit test case for `DeviceFullInitEvent` on quirk swap out --- tests/test_gateway.py | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 6ec360117..6b9964bdc 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -30,6 +30,7 @@ from zha.application.const import ZHA_GW_MSG, ZHA_GW_MSG_CONNECTION_LOST, RadioType from zha.application.gateway import ( ConnectionLostEvent, + DeviceFullInitEvent, DeviceJoinedDeviceInfo, DeviceJoinedEvent, DevicePairingStatus, @@ -690,6 +691,56 @@ async def test_reinterview_replaces_device_when_quirk_changes( assert new_zha_device.model == "Quirk B" +async def test_reinterview_swap_emits_full_init_configured( + zha_gateway: Gateway, +) -> None: + """A re-interview that replaces the device emits a CONFIGURED full-init event. + + HA repoints its (IEEE-keyed) device proxy off this event, so the swap must + still surface it with `CONFIGURED` status and `new_join=False`. + """ + registry = DeviceRegistry() + ( + QuirkBuilder("Fake_Manufacturer", "Model_A") + .friendly_name(model="Quirk A", manufacturer="Fake_Manufacturer") + .add_to_registry(registry) + ) + ( + QuirkBuilder("Fake_Manufacturer", "Model_B") + .friendly_name(model="Quirk B", manufacturer="Fake_Manufacturer") + .add_to_registry(registry) + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_A", + registry=registry, + ) + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + + events = [] + zha_gateway.on_all_events(events.append) + + reinterviewed = create_mock_zigpy_device( + zha_gateway, + ZIGPY_DEVICE_BASIC, + manufacturer="Fake_Manufacturer", + model="Model_B", + registry=registry, + ) + zha_gateway.device_reinterviewed(reinterviewed) + await zha_gateway.async_block_till_done() + + assert zha_gateway.get_device(zigpy_device.ieee) is not zha_device + + full_inits = [e for e in events if isinstance(e, DeviceFullInitEvent)] + assert len(full_inits) == 1 + assert full_inits[0].device_info.pairing_status is DevicePairingStatus.CONFIGURED + assert full_inits[0].new_join is False + + async def test_reinterview_rebuilds_in_place_when_quirk_unchanged( zha_gateway: Gateway, ) -> None: From 38ec9aee5889b92ac77736bc41b7b5b3d7820278 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:17:24 -0400 Subject: [PATCH 34/41] Drop local zigpy override --- pyproject.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fd9042ee8..5e3242f9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,10 +64,6 @@ ci = [ [tool.uv] default-groups = ["testing"] -# Temporary, until the `device_resolver` hook is released in zigpy -[tool.uv.sources] -zigpy = { path = "../zigpy", editable = true } - [tool.setuptools-git-versioning] enabled = true From af78810a6967104bc2f4579e26b1d791a4b12e90 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:39:32 -0400 Subject: [PATCH 35/41] Temporarily fix `uv.lock` for CI --- pyproject.toml | 10 ++ uv.lock | 395 +++++++++++++++++++------------------------------ 2 files changed, 160 insertions(+), 245 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5e3242f9f..b3bbceca2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,16 @@ ci = [ [tool.uv] default-groups = ["testing"] +# `zha` and `zha-quirks` mutually pin `>=2.0.0`, but neither is tagged 2.0.0 yet. Drop +# those bounds for the dev lock so resolution succeeds. Will be fixed in 2.0.1. +[[tool.uv.dependency-metadata]] +name = "zha-quirks" +version = "2.0.0" +requires-dist = ["zigpy>=2.0.0", "zha"] + +[tool.uv.sources] +zha-quirks = { git = "https://github.com/puddly/zha-device-handlers", rev = "7ac5b603cec5ae12fd24a7f40522aeb607aa4b1a" } + [tool.setuptools-git-versioning] enabled = true diff --git a/uv.lock b/uv.lock index 5d94ae9b1..c511e8a24 100644 --- a/uv.lock +++ b/uv.lock @@ -9,6 +9,13 @@ resolution-markers = [ "python_full_version < '3.13.2'", ] +[manifest] + +[[manifest.dependency-metadata]] +name = "zha-quirks" +version = "2.0.0" +requires-dist = ["zigpy>=2.0.0", "zha"] + [[package]] name = "acme" version = "2.10.0" @@ -924,21 +931,21 @@ wheels = [ [[package]] name = "boto3" -version = "1.43.35" +version = "1.43.36" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/f7/9ba34c655158a9827d0fdd8c6211a3836d9fbf29d48b82783995927b3907/boto3-1.43.35.tar.gz", hash = "sha256:392ba41e82629a77ad70de38c43f8fc0f5f453c965e381e4e87d6fcedc80d6c4", size = 112638, upload-time = "2026-06-22T20:26:39.039Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/9f/897287e955db0f50b12fd69ef45956e4fd2c7ddb48c736872f7ea2314443/boto3-1.43.36.tar.gz", hash = "sha256:587d7ee92a12e440ad12b0e7f11f3358f0c4d65b19f64726efc94aaf194aff28", size = 112690, upload-time = "2026-06-23T02:47:14.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/72/a92face6b5284d1283f2cccae224d6a10ebc5735c5d6fa93837109ebb6a1/boto3-1.43.35-py3-none-any.whl", hash = "sha256:461d5fb1ee1422d4a0a439666cebb29e0d5b4bf4308d3c2564449c77a1f4ae52", size = 140032, upload-time = "2026-06-22T20:26:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f1/274303f52483ecf199eae6f8d9b6f5951670397ee4d72c06cfd4eb644612/boto3-1.43.36-py3-none-any.whl", hash = "sha256:42942dde254673abcbc9e6e60017c88341a4f49d99d24e1f2e290fb38138c26f", size = 140031, upload-time = "2026-06-23T02:47:13.178Z" }, ] [[package]] name = "botocore" -version = "1.43.35" +version = "1.43.36" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, @@ -946,9 +953,9 @@ dependencies = [ { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/d8/28177fef65f6ef8c7fdd5b44024254816961578ce8925bc336229e236cdc/botocore-1.43.35.tar.gz", hash = "sha256:ea16aaad7db67b5af67719e9da3302474af97a7fdf27161c2ecb30adb3570d50", size = 15625607, upload-time = "2026-06-22T20:26:26.637Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/37/da9e7f6ca73ac73afd7f0bb7f238aa5daba35c081e98d7f48a7c399599c0/botocore-1.43.36.tar.gz", hash = "sha256:4cae47d1b2d426316b85a0087d9e69e048f13bc003b5177d74639fe9dfd28205", size = 15625488, upload-time = "2026-06-23T02:47:03.192Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/6c/5807aca5a873714144beb69ed5adc04bcdb8e9496be6bf7b72a76a6c9773/botocore-1.43.35-py3-none-any.whl", hash = "sha256:51c5893b5404b74012edde03bbd9c199c4bd7ffa35ddad173d0835efa7de281b", size = 15313659, upload-time = "2026-06-22T20:26:22.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/19/934f81592527a3f7f9b943c893e334c721a4644948642bc33885d584e9ec/botocore-1.43.36-py3-none-any.whl", hash = "sha256:3c65fdc39ed01d8dfde1e961b34038aed03c459f8ddf80717a12ac006475e49d", size = 15313630, upload-time = "2026-06-23T02:46:59.327Z" }, ] [[package]] @@ -1244,86 +1251,71 @@ wheels = [ [[package]] name = "coverage" -version = "7.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, - { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, - { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, - { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, - { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, - { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, - { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, - { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, - { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, - { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, - { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, - { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, - { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, - { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, - { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, - { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, - { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, - { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, - { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, - { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, - { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, - { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, - { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, - { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, - { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, - { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, - { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, - { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, - { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, - { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, - { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, - { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, - { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +version = "7.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/91/0a7c28934e50d8ac9a7b117712d176f2953c3170bccced5eaacfa3e96175/coverage-7.14.3.tar.gz", hash = "sha256:1a7563a443f3d53fdeb040ec8c9f7466aed7ca3dc5891aa09d3ca3625fa4387f", size = 924398, upload-time = "2026-06-22T23:10:25.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/b0/8a911f6ffe6974dac4df95b468ab9a2899d0e59f0f99a489afeec39f00bc/coverage-7.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d74ff26299c4879ce3a4d826f9d3d4d556fd285fde7bbce3c0ef5a8ab1cec24", size = 220672, upload-time = "2026-06-22T23:08:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/0fc0cb52538783dbbae0934b834f5a58fd5354380ee6cad4a07b15dc845d/coverage-7.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:96150a9cf3468ea20f0bc5d0e21b3df8972c31480ef90fa7614b773cc6429665", size = 221035, upload-time = "2026-06-22T23:08:28.372Z" }, + { url = "https://files.pythonhosted.org/packages/77/e2/421ccfbb48335ac49e93301478cf5d623b0c2bf1c0cadd8e2b2fc6c0c710/coverage-7.14.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:27d07a46500ba23515b838dbcf52512026af04090755cf6cc64166d88c9b9a1a", size = 252540, upload-time = "2026-06-22T23:08:30.226Z" }, + { url = "https://files.pythonhosted.org/packages/06/c2/05b8c890097c61a7f4406b35396b997a635200ded0339eda83dfbe526c5f/coverage-7.14.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:621e13c6108234d7960aaf5762ab5c3c00f33c30c15af06dcbff0c73bf112727", size = 255274, upload-time = "2026-06-22T23:08:31.876Z" }, + { url = "https://files.pythonhosted.org/packages/dc/be/b6d9efe447f8ba3c3c854195f326bd64c54b907d936cd2fdebf8767ec72e/coverage-7.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b60ca6d8af70473491a15a343cbabab2e8f9ea66a4376e81c7aa24876a6f977", size = 256389, upload-time = "2026-06-22T23:08:33.843Z" }, + { url = "https://files.pythonhosted.org/packages/d4/3c/f26e50acc429e608bc534ac06f0a3c169019c798178ec5e9de3dbc0df9c9/coverage-7.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c90a7cdd5e380e1ce02f19792e2ac2fbfbf177e35a27e69fd3e873b30d895c0c", size = 258648, upload-time = "2026-06-22T23:08:35.481Z" }, + { url = "https://files.pythonhosted.org/packages/9e/a2/01c1fabf816c8e1dae197e258edf878a3d3ddc86fbda34b76e5794277d8f/coverage-7.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5d788e5fd55347eef06ca0732c77d04a264de67e8ff24631270cdff3767a60cf", size = 252949, upload-time = "2026-06-22T23:08:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/89/c6/941166dd79c31fd44a13063780ae8d552eee0089a0a0930b9bdb7df554ed/coverage-7.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62c7f79db2851c95ef020e5d28b97afde3daf9f7febcd35b53e05638f729063f", size = 254310, upload-time = "2026-06-22T23:08:39.174Z" }, + { url = "https://files.pythonhosted.org/packages/10/31/80b1fd028201a961033ce95be3cd1e39e521b3762e6b4a1ac1616cb291e7/coverage-7.14.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:90f7608aeb5d9b60b523b9fb2a4ee1973867cc4865a3f26fe6c7577073b70205", size = 252453, upload-time = "2026-06-22T23:08:40.84Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/c3d9addd94c4b524f3f4af0232075f5fe7170ce99a1386edff803e5934db/coverage-7.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1e3b91f9c4740aeb571ecf82e5e8d8e4ab62d34fcb5a5d4e5baa38c6f7d2857c", size = 256522, upload-time = "2026-06-22T23:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/14/e5a0575f73795af3a7a9ae13dadf812e17d32422896839987dc3f86947e1/coverage-7.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c946099774a7699de03cbd0ff0a64e21aed4525eed9d959adde4afe6d15758ef", size = 252023, upload-time = "2026-06-22T23:08:44.243Z" }, + { url = "https://files.pythonhosted.org/packages/38/9b/9652ee531937ce3b8a63a8896885b2b4a2d56adc30e53c9540c666286d88/coverage-7.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16b206e521feb8b7133a45754643dead0538489cf8b783b90cf5f4e3299625fd", size = 253893, upload-time = "2026-06-22T23:08:46.113Z" }, + { url = "https://files.pythonhosted.org/packages/b1/05/42678841c8c38e4b08bdfc48269f5a16dfbf5806000fe6a89b4cece3c691/coverage-7.14.3-cp312-cp312-win32.whl", hash = "sha256:ea3169c7116eb6cdf7608c6c7da9ecfcb3da40688e3a510fac2d1d2bafd6dc35", size = 222734, upload-time = "2026-06-22T23:08:47.858Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/07a4fcee55177a25f1b52331a8e92cf4f2c53b1a9c75ce2981fd59c684ad/coverage-7.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:7ea52fc08f007bcc494d4bb3df3851e95843d881860ba38fe2c64dc100db5e7d", size = 223266, upload-time = "2026-06-22T23:08:49.494Z" }, + { url = "https://files.pythonhosted.org/packages/aa/34/2b8b66a989282ea7b370beb49f50bab29470dc30bb0b03935b6b802782f7/coverage-7.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:8cec0ad652ec57790970d817490105bd917d783c2f7b38d6b58a0ca312e1a336", size = 222655, upload-time = "2026-06-22T23:08:51.766Z" }, + { url = "https://files.pythonhosted.org/packages/a9/83/7fefbf5df23ed2b7f489907564a7b34b9b07098128e12e0fdfa92626e456/coverage-7.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47968988b367990ae4ab17523790c38cd125e02c6bfd379b6022be2d40bdc38c", size = 220699, upload-time = "2026-06-22T23:08:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/31/e6/38c3653ff6d56d704b29241362387ca824e38e15b76fdcb7096538195790/coverage-7.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0ee68f5c34812780f3a7063382c0a9fcbb99985b7ddcdcaa626e4f3fb2e0783a", size = 221068, upload-time = "2026-06-22T23:08:55.571Z" }, + { url = "https://files.pythonhosted.org/packages/20/86/4f5c45d51c5cd10a128933f0fd235393c9146abbfd2ce2dfa68b3267ead3/coverage-7.14.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fa9e5c6857a7e80fa22ace5cf3550ae392bbfc322f1d8dd2d2d5a8be38cec027", size = 252060, upload-time = "2026-06-22T23:08:57.464Z" }, + { url = "https://files.pythonhosted.org/packages/82/50/dfce42eff2cecabcd5a9bbad5489449c87db3415f408d23ffee417ce01f6/coverage-7.14.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:98a0859b0e98e43e1178a9402e19c8127766b14f7109a374d976e5a62c0e5c73", size = 254657, upload-time = "2026-06-22T23:08:59.453Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d2/639ceb1bc8038fd0d66768278d5dc22df3391918b8278c2a21aa2602a531/coverage-7.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69918344541ed9c8368566c2adc03c0e33d4550d7faa87d1b35e49b6a3286ea9", size = 255892, upload-time = "2026-06-22T23:09:01.291Z" }, + { url = "https://files.pythonhosted.org/packages/8b/96/002094a10e113512500dc1e10430a449417e17b0f90f7d496bcb820208b7/coverage-7.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f300ac92cd4b570724c8ffbbd0c130fee298d2447f41d5a3abf58976fae1de", size = 258026, upload-time = "2026-06-22T23:09:03.017Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ec/286a5d2fad9c4bee59bd724feeb7d5bf8303c6c9200b51d1dd945a9c72b0/coverage-7.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11a7ec9f97ab950f4c5af62229befc7faf208fdbc0116d3902d7e306cf2c5abd", size = 252285, upload-time = "2026-06-22T23:09:04.773Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7d/a17753a0b12dd48d0d50f5fab079ad99d3be1eac790494d89f3a417ca0b9/coverage-7.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a571bd889cd36c5922ce8e42e059f9d37d02301531d11374afa4c87a578625d5", size = 254023, upload-time = "2026-06-22T23:09:06.513Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/a76c6ceba6a2c313f905310abf2701d534cada22d372db11731831e9e209/coverage-7.14.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de76caefc8deabb0dd1678b6a980be97d14c8d87e213ac194dbf8b09e96d63fb", size = 251989, upload-time = "2026-06-22T23:09:08.382Z" }, + { url = "https://files.pythonhosted.org/packages/d9/39/353013a75fec0fb49f7553519f9d52b4441e902e5178c93f38eb6c07cedb/coverage-7.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d20a15c622194234161535459affa8f7905830391c9ccfa060d495dbfe3a1c7f", size = 256144, upload-time = "2026-06-22T23:09:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/29/0e/613878555d734def11c5b20a2701a15cb3781b9e9ea749da27c5f436e928/coverage-7.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b488bd4b23397db62e7a9459129d01ff06a846582a732efd24834b24a6ada498", size = 251808, upload-time = "2026-06-22T23:09:12.057Z" }, + { url = "https://files.pythonhosted.org/packages/af/76/359c058c9cfdcf1e8b107663881225b03b364a320017eda24a2a66e55102/coverage-7.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a3693b4153394d265f44fb855fdc80e72403024d4d6f91c4871b334d028e4e0", size = 253579, upload-time = "2026-06-22T23:09:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d9/4ba2f060933a30ebe363cef9f67a365b0a317e580c0d5d9169d56a73ef1c/coverage-7.14.3-cp313-cp313-win32.whl", hash = "sha256:338b19131ab1a6b767b462bfcbaa692e7ae22f24463e39d49b02a83410ff6b37", size = 222741, upload-time = "2026-06-22T23:09:15.636Z" }, + { url = "https://files.pythonhosted.org/packages/76/e8/196ebc25d8f34c06d43a6e9c8513c9266ef8dbf3b5672beb1a00cf5e29fa/coverage-7.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:b3d77f7f196abdef7e01415de1bce09f216189e83e58159cfeef2b92d0464994", size = 223283, upload-time = "2026-06-22T23:09:17.478Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/51d2aac6417523a286f10fb25f09eb9518a84df9f1151e93ff6871f34849/coverage-7.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:e6230e688c7c3e65cedd41a774eb4ec221adc6bfee13768231015b702d5e4150", size = 222678, upload-time = "2026-06-22T23:09:19.7Z" }, + { url = "https://files.pythonhosted.org/packages/61/56/14e3b97facbfa1304dd19e676e26599ad359f04714bed32f7f1c5a88efdc/coverage-7.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:605ab2b566a22bd94834529d66d295c364aba84afd3e5498285c7a524017b1fc", size = 220741, upload-time = "2026-06-22T23:09:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/12/1d/db378b5cca433b90b893f26dab728b280ddd89f272a1fdfed4aeaa05c686/coverage-7.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3c2134809e80fac091bfed18a6991b5a5eb5df5ae32b17ac4f4f99864b73dd7", size = 221068, upload-time = "2026-06-22T23:09:23.452Z" }, + { url = "https://files.pythonhosted.org/packages/47/f0/3f8421b20d9c4fcd39be9a8ca3c3fda8bc204b44efbd09fede153afd3e2f/coverage-7.14.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c02efd507227bde9969cab0db8f48890eb3b5dcad6afac57a4792df4133543ce", size = 252117, upload-time = "2026-06-22T23:09:25.458Z" }, + { url = "https://files.pythonhosted.org/packages/27/ca/59ea35fb99743549ec8b37eff141ece4431fea590c89e536ed8032ef45cf/coverage-7.14.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1bb93c2aa61d2a5b38f1526546d95cf4132cb681e541a337bf8dfd092be816e5", size = 254622, upload-time = "2026-06-22T23:09:27.523Z" }, + { url = "https://files.pythonhosted.org/packages/c8/25/ec6de51ae7493b92a1cf74d1b763121c29636759167e2a593ba4db5881e4/coverage-7.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f502e948e03e866538048bba081c075caaa62e5bda6ea5b7432e45f587eb462a", size = 255968, upload-time = "2026-06-22T23:09:29.43Z" }, + { url = "https://files.pythonhosted.org/packages/5d/05/c8bfc77823f42b4664fb25842f13b567022f6f84a4c83c8ecbb16734b7cb/coverage-7.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9973ef2463f8e6cfb61a6324126bb3e17d67a85f22f58d856e583ea2e3ca6501", size = 258284, upload-time = "2026-06-22T23:09:31.397Z" }, + { url = "https://files.pythonhosted.org/packages/f6/15/1d1b242027124a32b26ef01f82018b8c4ef34ef174aa6aeba7b1eeef48e8/coverage-7.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9be4e7d4c5ca0427889f8f9d614bd630c2be741b1de7699bca3b2b6c0e41003e", size = 252143, upload-time = "2026-06-22T23:09:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/74/b6/d2a9842fd2a5d7d27f1ac851c043a734a494ad75402c5331db3da79ed691/coverage-7.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a574912f3bde4b0619f6e97d01aa590b70998859244793769eb3a6df78ee56d3", size = 253976, upload-time = "2026-06-22T23:09:35.351Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/e1600ddf7e226db5558bb5323d2186fff00f505c4b764643ec89ce5d8175/coverage-7.14.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e343fb086c9cd780b38622fea7c369acd64c1a0724312149b5d769c387a2b1f5", size = 251942, upload-time = "2026-06-22T23:09:37.313Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2c/9159de64f9dd648e324328d588a44cfab1e331eb5259ce1141afe2a92dfb/coverage-7.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:3c68df8e61f1e09633fefc7538297145623957a048534368c9d212782aa5e845", size = 256220, upload-time = "2026-06-22T23:09:39.165Z" }, + { url = "https://files.pythonhosted.org/packages/91/67/b7f536cc2c124f48e91b22fbb741d2261f4e3d310faf6f76007f47566e5d/coverage-7.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3e5b550a128419373c2f6cec28a244207013ef15f5cbcff6a5ca09d1dfaaf027", size = 251756, upload-time = "2026-06-22T23:09:41.056Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ec/f3718038e2d4860c715a55428377ca7f6c75872caf98cabd982e1d76967d/coverage-7.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2bfc4dd0a912329eccc7484a7d0b2a38032b38c40663b1e1ac595f10c457954b", size = 253413, upload-time = "2026-06-22T23:09:43.306Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a5/91f11efeef89b3cc9b30461128db15b0511ef813ab889a7b7ab636b3a497/coverage-7.14.3-cp314-cp314-win32.whl", hash = "sha256:0423d64c013057a06e70f070f073cec4b0cbc7d2b27f3c7007292f2ff1d52965", size = 222946, upload-time = "2026-06-22T23:09:45.261Z" }, + { url = "https://files.pythonhosted.org/packages/58/fd/98ac9f524d9ec378de831c034dbdeb544ca7ef7d2d9c9996daf232a037fd/coverage-7.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:92c22e19ce64ca3f2ad751f16f14df1468b4c231bd6af97185063a9c292a0cb3", size = 223436, upload-time = "2026-06-22T23:09:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a0/7cd612d650a772a0ae80144443406bf61981c896c3d57c9e6e79fb2cdbd1/coverage-7.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:41de778bd41780586e2b04912079c73089ab5d839624e28db3bdb26de638da92", size = 222861, upload-time = "2026-06-22T23:09:49.384Z" }, + { url = "https://files.pythonhosted.org/packages/55/57/017353fab573779c0d00448e47d102edd36c792f7b6f233a4d89a7a08384/coverage-7.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8427f370ca67db4c975d2a26acfc0e5783ca0b52444dbc50278ace0f35445949", size = 221474, upload-time = "2026-06-22T23:09:51.417Z" }, + { url = "https://files.pythonhosted.org/packages/69/92/90cf1f1a5c468a9c1b7ba2716e0e205293ad9b02f5f573a6de4318b15ba1/coverage-7.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8e88f335544a47e22ae2e45b344772925ec65166555c958720d5ed971880891", size = 221738, upload-time = "2026-06-22T23:09:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c0/4df964fa539f8399fd7679c09c472d73744de334686fd3f01e3a2465ce4e/coverage-7.14.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:beaab199b9e5ceaf5a225e16a9d4df136f2a1eae0a5c20de1e277c8a5225f388", size = 263101, upload-time = "2026-06-22T23:09:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/06/76/e5d33b2576ae3bf2be2058cd1cae57774b61e400f2c3c58f3783dc2ffb4a/coverage-7.14.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3ff255799f5a1676c71c1c32ec01fd043aa09d57b3d95764b24992757184784", size = 265225, upload-time = "2026-06-22T23:09:57.904Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/e52419afe391a39ba27fdefaf0737d8e34bf03faef6ab3b3006545bbd0d0/coverage-7.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:878832eaac515b62decfa76965aed558775f86bf1fc8cca76993c0c84ae31aed", size = 267643, upload-time = "2026-06-22T23:09:59.938Z" }, + { url = "https://files.pythonhosted.org/packages/58/7a/f2625d8d5006b6b20fba5afaef00b24a763fe96476ea798a3076cbc1f84e/coverage-7.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:611e62cb9386096d81b63e0a05330750268617231e7bd598e1fe77482a2c58a5", size = 268762, upload-time = "2026-06-22T23:10:01.943Z" }, + { url = "https://files.pythonhosted.org/packages/7d/bf/908024006bba57127354d74e938954b9c3cd765cc2e0412dc9c37b415cda/coverage-7.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:02c41de2a88011b893050fc9830267d927a50a215f7ad5ec17349db7090ccf26", size = 262208, upload-time = "2026-06-22T23:10:03.954Z" }, + { url = "https://files.pythonhosted.org/packages/34/a0/d4f9296441b909817442fdb26bd77a698f08272ec683a7394b00eb2e47a0/coverage-7.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:526ce9721116af23b1065089f0b75046fe521e7772ab94b641cd66b7a0421889", size = 265096, upload-time = "2026-06-22T23:10:05.936Z" }, + { url = "https://files.pythonhosted.org/packages/e8/da/4ae4f3f4e477b56a4ce1e5c48a35eff38a94b50130ce5bdc897024741cfc/coverage-7.14.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e4ed44705ca4bead6fc977a8b741f2145608289b33c8a9b42a95d0f15aedbf4d", size = 262699, upload-time = "2026-06-22T23:10:07.973Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/6927148073ff32856d78baa77b4ddc07a9be7e90020f9db0661c4ca523a1/coverage-7.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2415902f385a23dcc4ccd26e0ba803249a169af6a930c003a4c715eeb9a5444e", size = 266433, upload-time = "2026-06-22T23:10:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a7/774f658dbe9c4c3f5daa86a87e0459ac3832e4e3cc67affe078547f727b9/coverage-7.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b75ee850fc2d7c831e883220c445b035f2224de2ba6103f1e56dbd237ab913f7", size = 261547, upload-time = "2026-06-22T23:10:12.191Z" }, + { url = "https://files.pythonhosted.org/packages/3d/14/a0c18c0376c43cbf973f43ef6ca20019c950597180e6396232f7b6a27102/coverage-7.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dc9b4e35e7c3920e925ba7f14886fd5fbe481232754624e832ddba66c7535635", size = 263859, upload-time = "2026-06-22T23:10:14.492Z" }, + { url = "https://files.pythonhosted.org/packages/10/ac/43a3d0f460af524b131a6191805bc5d18b806ab4e828fbf82e8c8c3af446/coverage-7.14.3-cp314-cp314t-win32.whl", hash = "sha256:7b27c822a8161afbe48e99f1adfb098d270ae7e0f7d7b0555ce110529bdb69cc", size = 223250, upload-time = "2026-06-22T23:10:16.758Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5f/d5e5c56b0712e96ce8f69fe7dbf229ff938b437bc50862743c8a0d2cea84/coverage-7.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:39e1dbbb6ff2c338e0196a482558a792a1de3aa64261196f5cdb3da016ad9cda", size = 224082, upload-time = "2026-06-22T23:10:19.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/35/947cbd5be1d3bcbbdc43d6791de8a56c6501903311d42915ae06a82815f0/coverage-7.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:68520c90babfa2d560eca6d497921ed3a4f469623bd709733124491b2aa8ef3f", size = 223400, upload-time = "2026-06-22T23:10:21.24Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e3/a0aa32bfa3a081951f60a23bc0e7b512891ef0eecda1153cf1d8ba36c6b1/coverage-7.14.3-py3-none-any.whl", hash = "sha256:fb7e18afb6e903c1a92401a2f0501ac277dca527bb9ca6fe1f691a8a0026a0e8", size = 212469, upload-time = "2026-06-22T23:10:23.405Z" }, ] [[package]] @@ -1469,11 +1461,11 @@ wheels = [ [[package]] name = "distlib" -version = "0.4.0" +version = "0.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z" }, ] [[package]] @@ -1496,11 +1488,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.29.0" +version = "3.29.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028, upload-time = "2026-06-13T16:12:00.744Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, + { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, ] [[package]] @@ -1894,7 +1886,7 @@ wheels = [ [[package]] name = "habluetooth" -version = "6.9.0" +version = "6.18.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", @@ -1910,45 +1902,45 @@ dependencies = [ { name = "btsocket", marker = "python_full_version >= '3.14.2'" }, { name = "dbus-fast", marker = "python_full_version >= '3.14.2' and sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/71/85492125ecde8a1594e72129a149cc11cc63812d19d96010725191501200/habluetooth-6.9.0.tar.gz", hash = "sha256:e14fcbc42e6fe108e7a72d6110f092b98307367ed7c22502b6657631d827ce5b", size = 82003, upload-time = "2026-06-22T17:33:08.575Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/47/57edcb0bb98c8d5434c9c41370fcf28852e03d28dda4bfc536307308ca83/habluetooth-6.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a87fc745cae0c33b055328cd127138b614bff8bcdeaa3126dcd5a8c31b4f8cb", size = 808563, upload-time = "2026-06-22T17:48:33.78Z" }, - { url = "https://files.pythonhosted.org/packages/9a/32/f5529b4e777a7af82193bcc8f164d9406d57734aa0ea7d22c37844279280/habluetooth-6.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96a3d5b5633a02869c50a00f2fc42857993a8f57056d312d9147539910d12cc", size = 937391, upload-time = "2026-06-22T17:48:35.342Z" }, - { url = "https://files.pythonhosted.org/packages/7e/11/24aefe9dd9aef82803e7211ad9be1899d8942db78b119d204cc94065b619/habluetooth-6.9.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:52566686479ab94155a122b93e8a7a204038cecd8287a87cb83086ff3dee6ef6", size = 923793, upload-time = "2026-06-22T17:48:36.845Z" }, - { url = "https://files.pythonhosted.org/packages/02/a4/8952586feefe9945ba50ced68c10fc454ca9fc04f821be8224ea599309cd/habluetooth-6.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cb22a8f7a7de21481d52cf7f10d370a13216a2cd6e4db5238225de13d31d1663", size = 1003874, upload-time = "2026-06-22T17:48:38.364Z" }, - { url = "https://files.pythonhosted.org/packages/ec/80/24e63e2d33d3d515050a72e80480abc86ddc3ee5c6c4bc260a4a431a45e6/habluetooth-6.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f739b4a1d2f3c2c401b6f92ea7e650b2efec043b42fe6d32cb5b52cc30987ba", size = 950271, upload-time = "2026-06-22T17:48:39.742Z" }, - { url = "https://files.pythonhosted.org/packages/df/41/7a39480a502acb675953e12622628f9a482387269cc69a281c7842344297/habluetooth-6.9.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7f412889cb989f98d6f711c295480578f9190deaf969aaac2cc3914d27b14b94", size = 934820, upload-time = "2026-06-22T17:48:41.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/9d/5a06e7f73d7434627634e60f104333d269c5b31084b13a232158c6b6729d/habluetooth-6.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:223251aca62193cabeeab265ae352d70657551fd81a16e9842fdcf543cbc1628", size = 1012135, upload-time = "2026-06-22T17:48:42.569Z" }, - { url = "https://files.pythonhosted.org/packages/8a/52/568cc0ee63f5ad7447f5f8ba3eff1728fa8ecfe99d62bae09005303e3d43/habluetooth-6.9.0-cp312-cp312-win32.whl", hash = "sha256:d43a45808c4d156ac83fc426fa0c8c9f96df5b3562378fa3ff7b9e3cdf8911b2", size = 645028, upload-time = "2026-06-22T17:48:44.266Z" }, - { url = "https://files.pythonhosted.org/packages/6a/cb/a37444a975d4330b8fe7b914828082ddce2e8f0cee0c69b11de41f07d7fe/habluetooth-6.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:513ac357c6c5065d1647b32e1d97396094f83ea3fa3c023d5fd1ee2ef6dc20ea", size = 747634, upload-time = "2026-06-22T17:48:45.869Z" }, - { url = "https://files.pythonhosted.org/packages/f6/91/43c7f8b705c178420b2de022d0624668efa847eb9846425d479dcc0963ff/habluetooth-6.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:509b8ef86a8caa2afce8d2927e86666573199312abc5cc5935cf68031c7a281f", size = 804562, upload-time = "2026-06-22T17:48:47.216Z" }, - { url = "https://files.pythonhosted.org/packages/9a/41/b01e0155f9b66515799208fe938040bcd575b78902e6d84acbe30ff08834/habluetooth-6.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a64dbe398d8afd2c8c16e3e0ca5fc740ca3020ee7fe2be54fb4470761d141f73", size = 934469, upload-time = "2026-06-22T17:48:48.596Z" }, - { url = "https://files.pythonhosted.org/packages/25/68/6bd6ae638b84f7b3e6aca8e9fa065bc532c1fb232f8e2e3dae1f3d147047/habluetooth-6.9.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee28930af2dcb79927a760baa5f41740624dd7764b6db7e94d2ef28125d05d30", size = 921673, upload-time = "2026-06-22T17:48:50.156Z" }, - { url = "https://files.pythonhosted.org/packages/df/39/ed956e89ba732f78e689cc0f1d58f1d7cd0d7c2cfa975cedb43cf25fac50/habluetooth-6.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6fff96187fd01ee8981027659b5938506047cade7b5998a59109c55ce14edff8", size = 1000043, upload-time = "2026-06-22T17:48:51.714Z" }, - { url = "https://files.pythonhosted.org/packages/88/e8/51a436fdfb9ef3f118b9af6ba705fc6f038997ce744d6559b60c2dac4755/habluetooth-6.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dfd3dd0985fff3648e70d73d55c395e6b4ae48c89146c60130e23eb902248178", size = 947988, upload-time = "2026-06-22T17:48:53.228Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/e430544ba2ee0b2113937a7549a5b99d4aef442232ecfc667c0d470ddaeb/habluetooth-6.9.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c874579dcef8140b064d23b27455855a98426cd41b643d1992fd46a9f6c003", size = 932840, upload-time = "2026-06-22T17:48:54.825Z" }, - { url = "https://files.pythonhosted.org/packages/35/ba/c4c1e060fca438e02764e32cf5723e6b5628b2116263a57e2b7d21b4cb27/habluetooth-6.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a908257bc898e0ea7fb0beb9f7012b1b4ae0875bea2b7646c867a8a582ca0923", size = 1009365, upload-time = "2026-06-22T17:48:56.475Z" }, - { url = "https://files.pythonhosted.org/packages/24/4e/9f5aa74ef5f185a30306dfa81f5b16f6391889639a4289bf799ea4dab516/habluetooth-6.9.0-cp313-cp313-win32.whl", hash = "sha256:ed856da00ee347399e3e71a9c4c02a74ec77e1069edc61c019b31d02711530f4", size = 643687, upload-time = "2026-06-22T17:48:58.028Z" }, - { url = "https://files.pythonhosted.org/packages/be/73/fcc81307587118f1a76d7bc142f6bd72257e9b60acfc4952a3bcdb9f82dd/habluetooth-6.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c42ff0ff7c39fa56f1990d104f9bd3e21f4ce9fa1c2aabc4edc485c8418e63bb", size = 747809, upload-time = "2026-06-22T17:48:59.582Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b2/8fe4a0ce05b8157a455edb63a6a57c3d3340abe9318d04a75da45915a60d/habluetooth-6.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e822d024b2655e4ec1cfa2dc2952c3b5c24419b6a24753f8cf2d538657b3ea1b", size = 812142, upload-time = "2026-06-22T17:49:00.982Z" }, - { url = "https://files.pythonhosted.org/packages/bd/36/dbe753a7f36f219ace91391bc0f092fdb29eb988919414a1cd8cd7893f2d/habluetooth-6.9.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b91966f1c5bdf956dd03f95c34e37f3ef849dab1426692f9973416f6c5b2a80", size = 949464, upload-time = "2026-06-22T17:49:02.402Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c6/07bb6a8481850b22b81e45320013862aabc2106d07f488df92e67ecd1026/habluetooth-6.9.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bf77ce3dd7a201428c90f559685b0a8e94562346df820ab6f24e7d810fa75eff", size = 912542, upload-time = "2026-06-22T17:49:03.9Z" }, - { url = "https://files.pythonhosted.org/packages/6d/57/72e2100d1e87d1bead0cddd35c45807c930b8a732ddd9b41abf7b6738896/habluetooth-6.9.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f4f23ca490dfeb83139dd4cbb82b98b938685733008a448f09e299b4a61ba29", size = 1005740, upload-time = "2026-06-22T17:49:05.399Z" }, - { url = "https://files.pythonhosted.org/packages/60/43/41d758fbb026e507a95b9963f800a04121945e8136fc27d5e33a54cd7781/habluetooth-6.9.0-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:291b02e27bddfc089cb0658d20f9ebbc68aac62715adfdb5ebb55916332e9c1d", size = 1004362, upload-time = "2026-06-22T17:33:06.401Z" }, - { url = "https://files.pythonhosted.org/packages/d8/e0/ec4fe5ab11b098a3d041a334d0a0fe80e371db35b754c0c29c724804cca7/habluetooth-6.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15b637da907ff7dbca3c699004cee680582da500b3c650f3fa40268847d1433a", size = 963570, upload-time = "2026-06-22T17:49:07.055Z" }, - { url = "https://files.pythonhosted.org/packages/10/50/b713f749d3adfe6e557b45b02bbd6e82a847e0a8cef743e4a9dde5023e56/habluetooth-6.9.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b273b58fa72b28e8d1857551b08ae89386e3463890129ee2c902a99645f13fae", size = 918633, upload-time = "2026-06-22T17:49:08.411Z" }, - { url = "https://files.pythonhosted.org/packages/ab/12/1150090ae17d3b58f680ac1a839375165943aea9ccb2308846199b7670df/habluetooth-6.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:113903286266da35626844090a4289c77f659fbe9e33fe7cf92de569a416eaaf", size = 1015365, upload-time = "2026-06-22T17:49:09.892Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a0/e3c2467f753d064e0fddf7a0a2b554faf224f6b49bbf58a448f0fdb21ab2/habluetooth-6.9.0-cp314-cp314-win32.whl", hash = "sha256:44a36677dd664d7083d44463fff1819a73b1e1bc3c8240c41d9c929cf2e80e4c", size = 658939, upload-time = "2026-06-22T17:49:11.554Z" }, - { url = "https://files.pythonhosted.org/packages/99/88/7b4a01adeb32b22f48363efed76c0dd991e7ea25dddc43487d66b2c49186/habluetooth-6.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:4aaded40ea3fc3cb3a96aefce7d282003a7054b323c3b619d2c1db741b855f76", size = 763291, upload-time = "2026-06-22T17:49:13.449Z" }, - { url = "https://files.pythonhosted.org/packages/f3/82/bf6c3cac3ab7d84b7fbdd05c96d25a7e5f9710e474528c66d80e90117b6e/habluetooth-6.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7648dfd7569eaabca23b0f738f48b97dd0a73b6f89c22a2dc4b8f19a76800b98", size = 1600773, upload-time = "2026-06-22T17:49:14.996Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/42ec91408c9d29658003201122a8e892ec431b47833824c79969b1316598/habluetooth-6.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acb53e36cf0f8a134594a9515eb57141feef9390addd1332dbacf752d7018ac4", size = 1807804, upload-time = "2026-06-22T17:49:16.619Z" }, - { url = "https://files.pythonhosted.org/packages/30/2c/b0bbe01a2ecb36ff21e4b4994282321dbe86ef807885500c0f5062af54b3/habluetooth-6.9.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b14e42b89985665d8e7c1b600b3043a44d5930b69eef0257a375a871956f53", size = 874634, upload-time = "2026-06-22T17:49:18.367Z" }, - { url = "https://files.pythonhosted.org/packages/4d/34/ea49ce2b8d89501f98454ca19984dc8f08575aa3fd26e4b0bc4b0c48e4e1/habluetooth-6.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c0ecd03274f3746d869d393dacf89bc1ee0a3658770203fcb27e861db08411a", size = 1902278, upload-time = "2026-06-22T17:49:19.823Z" }, - { url = "https://files.pythonhosted.org/packages/4b/67/a2b337d3f56887090c1e041c1cef1951ff5c38ca2cf380d64133784b72bd/habluetooth-6.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:580b3b431faa1785995562a7b21c2e04970d0a756a853e6f5101aa114b3fe67e", size = 1837896, upload-time = "2026-06-22T17:49:21.308Z" }, - { url = "https://files.pythonhosted.org/packages/70/48/ad6516af1d865b5dc61210795028a1cfd911cba5cb0552c1837c88d405db/habluetooth-6.9.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:e160c9d66012b7243c6c5059421d4a2feba38abef7a87871d6d1981bb01fd228", size = 911242, upload-time = "2026-06-22T17:49:22.861Z" }, - { url = "https://files.pythonhosted.org/packages/65/7e/02d15e02c194664f87896123befe37ffecd3e06f3a468a031cbc4f97d6a8/habluetooth-6.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e7543fdfab8c22a820b6e90d41a9729ddb19eb74dc823daf8e994608d0d7ea3", size = 1924073, upload-time = "2026-06-22T17:49:24.444Z" }, - { url = "https://files.pythonhosted.org/packages/96/69/32855982913de109a39fa3515a9977ef5d16642bca3916c08f5027edbd17/habluetooth-6.9.0-cp314-cp314t-win32.whl", hash = "sha256:1d5ce957e6192f4c267196e540a96419e3e7cda6249f65225634ca8de632e8fe", size = 1281076, upload-time = "2026-06-22T17:49:26.685Z" }, - { url = "https://files.pythonhosted.org/packages/01/59/fd2ba240ee0511622d79c49ba8e11cd85d357556327d41b3845865b0ac79/habluetooth-6.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d9e5d92b9447a8e5b89f6977fe5c022918774aab3815784afc28c32dd9c65b5f", size = 1496497, upload-time = "2026-06-22T17:49:28.494Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/de/46/bcba05f45518e5733eeb823eafe8fa76767109a810000f0a45dea5715ef5/habluetooth-6.18.0.tar.gz", hash = "sha256:6f6eeb862b22401eb3e2bbc19edb53ae05a4be7d1fa2daddbd7e0c0413276a50", size = 104264, upload-time = "2026-06-24T01:20:41.29Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/d3/cbc93fda3408e3e675e84b7f8dcff43f75963d8c2838a2c8698b9e01f147/habluetooth-6.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd55d7ade35a557170ca2b83a4c157a53a14dbbcebfdf7af2c6b4f0df711a7e2", size = 867968, upload-time = "2026-06-24T01:32:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/45/95/8080ccaa703e5522f475ef9832ad4d33dab46fd02246d810c41154748a30/habluetooth-6.18.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07ab34db9b010b8b14559140253def3b07c7cde37180a6872a3f033cac1615b8", size = 1001539, upload-time = "2026-06-24T01:32:02.832Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/1f7023499a7ae7f075df567278a770d281376a368a6a6fee6306a8a1f19d/habluetooth-6.18.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dceb8c07528f88d2575c236685a47e8e63ec6cb28593de8e95b808866c677939", size = 987859, upload-time = "2026-06-24T01:32:04.336Z" }, + { url = "https://files.pythonhosted.org/packages/9c/70/aac7d053275d870a7961a751ea71d70803266a397ca4580dce039ac51d62/habluetooth-6.18.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da55eecf49b1847200e641b0e921a45d4a26518f812fabea14531779de84832", size = 1071589, upload-time = "2026-06-24T01:32:05.947Z" }, + { url = "https://files.pythonhosted.org/packages/49/fc/6c14caf6f017ced0cac16dcc605f3dc5e24c2e3a7d9538f75f1af6054d83/habluetooth-6.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be2e07488ca9862dafa499fbf00d360c35105d7e600156fdf5f2028ce061771a", size = 1014833, upload-time = "2026-06-24T01:32:07.531Z" }, + { url = "https://files.pythonhosted.org/packages/ed/2d/6e7c515f19bea4c190b0da138e58643ff322d9c8777b665c3ce1b0550c1d/habluetooth-6.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c09b6a23794a857eca89fca42380f53e6e83fa627f270de7f688c20eca77c8fb", size = 998635, upload-time = "2026-06-24T01:32:08.944Z" }, + { url = "https://files.pythonhosted.org/packages/57/a3/10035272876cdeaf7dc02686f87ba00960c64b3146cf44c5818f8b07f4a1/habluetooth-6.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cda7a95c433e32bb8770a4f613fb3196db655ba3d45d7789f1dfaa8be4a0d599", size = 1080449, upload-time = "2026-06-24T01:32:10.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/37/7c86ad346f798f307f35243981575bfba6b8bdf399654bede1028412e98b/habluetooth-6.18.0-cp312-cp312-win32.whl", hash = "sha256:a49eb4c59e2df56ceca8b9631b394789a9fb6bed738a28fc3c50ec3d85609303", size = 693804, upload-time = "2026-06-24T01:32:11.959Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/caef9ca6f09aac156b5dcc4b2572fb5d80a2379e3d7d10efc172d38fcaee/habluetooth-6.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2080664afd3713e833f7f32007ce59bc62288ec377c8866dbc7884298728f8f", size = 800926, upload-time = "2026-06-24T01:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/ff/de/8d58cbca61bbd027e0b2f2ef6dc070c12c4df023b3461b624540a4f0628a/habluetooth-6.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2fb96d825b2063ffb7f542495de85703dfa5c6d0e64e070cca744b80cbd1c05", size = 863519, upload-time = "2026-06-24T01:32:15.338Z" }, + { url = "https://files.pythonhosted.org/packages/27/77/e14a3293bd5a0a9356c6f95cbed8dcb495e0d38d3201a5f0a5fdaa8e8416/habluetooth-6.18.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d15bb863bab5fb2199d634869afe8266c54184e811d40d7051606570de0ad8", size = 996123, upload-time = "2026-06-24T01:32:16.905Z" }, + { url = "https://files.pythonhosted.org/packages/d6/80/b3a7d45a1a591932b9567a286e2f92e15b995ec867db9f1575a0a8f48193/habluetooth-6.18.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dfb93c2779a77ec6a6f4f1bcb5dd0267a041f6f96842adff6811bd81fd6a2849", size = 986397, upload-time = "2026-06-24T01:32:18.721Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d2/89fcc23c084aefdd77f20a2f3b10d87b8aaf628aa069299593c51234274d/habluetooth-6.18.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02dab8ffb8c71568d2ee20bcc6f7e8486b575d3730327613fdd8b4cc54137755", size = 1069007, upload-time = "2026-06-24T01:32:20.407Z" }, + { url = "https://files.pythonhosted.org/packages/03/54/a10de9c47ec5d69802defc654a500f492f122e43e8fc28402b3abdf865b3/habluetooth-6.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32ddcf2f7118f3373da905ab060a059a57c47036c0a53df70e405d9be616c8e2", size = 1010950, upload-time = "2026-06-24T01:32:21.951Z" }, + { url = "https://files.pythonhosted.org/packages/6f/87/d855532dd74f2f00d6f28f9fca7eb6a85a582056e9f56c12118a8f4cddc7/habluetooth-6.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4a0e155722faa56f30975708679b22242ed7f15c03b82c4341e4b0dc5242cc10", size = 999849, upload-time = "2026-06-24T01:32:23.409Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/ccbfd691b765e8542a278cf0f8e48cb04e4e4f7d23e036a83cb3c5ea030b/habluetooth-6.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:621d7c07554718d6e24ebd05a8eb7de58e5fbebca1af55d4eae035d79e127f5f", size = 1077501, upload-time = "2026-06-24T01:32:25.026Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f2/dab5326bde40e68fb90b65b96e2b01dbf1a5de5c220fc94473cd1d6af9c0/habluetooth-6.18.0-cp313-cp313-win32.whl", hash = "sha256:c3ac1596ec5edd0e26f53c4686b7c4b8784e1c15130cc5a8bf1f65794539c073", size = 692839, upload-time = "2026-06-24T01:32:27.094Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7f/96305660c32fbe14879104b0b615918acfd2ca70e6b592133e9a8aa05404/habluetooth-6.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:809802be1bd88b4620014f678ae5cef9aaa401a9d4394dc7f62d784632455eb2", size = 801247, upload-time = "2026-06-24T01:32:28.843Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c4/c3af1bcf42d89f4f05393e1feeb8706af6211919780417b7677ac593c997/habluetooth-6.18.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fe3b0a85d65eff6b6a6b757394344e6c9a6845c5ad465410d3ab3552da17db78", size = 872544, upload-time = "2026-06-24T01:32:30.361Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2d/fdbda59710a01421f9e5badf705584fcceec1cdd4b672910d7ce2030a1d9/habluetooth-6.18.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6066de8c01e82331fab426f02e2dfe9dc8be49d25c17aed1531ba1531f48b8a3", size = 1014569, upload-time = "2026-06-24T01:32:31.84Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4c/c2fe2158d35c1fc33d92c0340c450394edb51c5ff83f156f6439b4985699/habluetooth-6.18.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f021199a5cb0338edc44b16349d594012ecaa71f25462436f2870f7119efd848", size = 975202, upload-time = "2026-06-24T01:32:33.577Z" }, + { url = "https://files.pythonhosted.org/packages/cd/20/f3d8fa336ba5fce26edf6399a1a2ac6d72e4d5f043da37e6f44c119c55ab/habluetooth-6.18.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c948c741b3d0fd7ba0464ef07f45cbf726a7d0544ad83a843c9de3e29a922c3e", size = 1074332, upload-time = "2026-06-24T01:32:35.335Z" }, + { url = "https://files.pythonhosted.org/packages/25/69/f5ec99fcc158cd54d676da19bb8777bc0b31b8a94b62bec54ea41a48399f/habluetooth-6.18.0-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:e419997bfd6b955d585d60a14c0a4250355c33cda4cb5c56a50f1fdcf41c1421", size = 1073095, upload-time = "2026-06-24T01:20:39.697Z" }, + { url = "https://files.pythonhosted.org/packages/48/9f/2020a94c23c9d490be557265ce65a79b13aeff4c6b6ee5035a1d9cdfb351/habluetooth-6.18.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:364ac70a7ae8fd145b5acd4e294488ddcc4e42fa027908dbad3b23e19ed0b0ff", size = 1028409, upload-time = "2026-06-24T01:32:36.834Z" }, + { url = "https://files.pythonhosted.org/packages/be/29/dcfda7a8e808bce5a49d0abe6616c74d59a9ec004d9f5b1e27996b026c0f/habluetooth-6.18.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1ff8dddd1325f491806374f2a3b9b300978d340ba3eb7b7254a2e59f087ef626", size = 983734, upload-time = "2026-06-24T01:32:38.569Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/0aecac897c231d0644c01e5db6882d6c0d1185ae56f96aa887b951d28a38/habluetooth-6.18.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b2f456625be4b0335aefa0559566ecd784f74bfbc0966e751b2987bc0caece94", size = 1083514, upload-time = "2026-06-24T01:32:40.079Z" }, + { url = "https://files.pythonhosted.org/packages/58/d2/02378c689769502a5e9f3a14c46da847dfd8165109fb0b3b326ee2ce7d6a/habluetooth-6.18.0-cp314-cp314-win32.whl", hash = "sha256:adacefb4bebd05535dd225e30198e78959f434ac1e1dea1aef30f2ddd553be11", size = 706879, upload-time = "2026-06-24T01:32:41.786Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9a/250cafe308edce096057dab9924a4f5f362a21934f1b7064c2c9487e6449/habluetooth-6.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1236790983384e11f06b71e8f38eeb05d3d36e441798ab736c139e2b13caa33", size = 817081, upload-time = "2026-06-24T01:32:43.363Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/7143287224260c430fb5555e39c5272fd6e02463a38a633b4510ef9b0f62/habluetooth-6.18.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cdd749d6150e37767783c421d61b9ee41f6037401479dc57c28017d5ea56afec", size = 1699589, upload-time = "2026-06-24T01:32:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7c/d8f071eaaa99d631e533457995e2e8eb0e21837fcd002e4698ce7b4f21b3/habluetooth-6.18.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c292684ff188ebf3db8acb28bc46dd64136e8f2c3c4402e1941f92a79f3f0bd4", size = 1910874, upload-time = "2026-06-24T01:32:46.797Z" }, + { url = "https://files.pythonhosted.org/packages/43/cb/b4b7a6dfd7a74c0ebd206e519a8bcb5a3368152dad555ab7dc81d27aabcd/habluetooth-6.18.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d913e924c50fee5987dea15d9e0a529cb0d2e7c7e6b17762bcf827617bd35624", size = 936635, upload-time = "2026-06-24T01:32:48.446Z" }, + { url = "https://files.pythonhosted.org/packages/91/12/78a385768ee9a42d20112c80caa4289a38cb52566fcef7512264b3a772a0/habluetooth-6.18.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b5bf58b07b2b1ea78d7cdf330d1de268f245770a0bb4f5312f97e8e4408b228", size = 2012412, upload-time = "2026-06-24T01:32:50.511Z" }, + { url = "https://files.pythonhosted.org/packages/1b/5f/7f7fb8e47f44754f9d25d2bbc9d5f2e55bc8b27d864526359ce42907c7e4/habluetooth-6.18.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:13dbda83ffe6e398968f563d271b0833304755336db213717ca18c047a384cee", size = 1938742, upload-time = "2026-06-24T01:32:52.354Z" }, + { url = "https://files.pythonhosted.org/packages/46/9e/a91cd712b2beb4ff528e2cbf8ce93498b716a14e173acd92486247a9cfab/habluetooth-6.18.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21e415557a6ad5231a6e97e1271ea89d3089a94a6841c02b3c9deb6e84f60cfa", size = 976865, upload-time = "2026-06-24T01:32:53.996Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/c3f9b8653a5890fac5e8b12c459349dc40e5cc7a3963bb5438fb3a5be7df/habluetooth-6.18.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dd53283c72e5e99f13108c71a8defbc2ba2fdd38ece5ec43bba71010d0d2bc64", size = 2033798, upload-time = "2026-06-24T01:32:55.886Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d7/83b0e8ce7aae74d2bdb147afb3ae20a795297440736d4fb0e1f0c3b9811d/habluetooth-6.18.0-cp314-cp314t-win32.whl", hash = "sha256:76dc789b59426e6fdbc3d19339b4703ce21412e5d08417c9830c8c4276829bc0", size = 1356513, upload-time = "2026-06-24T01:32:57.588Z" }, + { url = "https://files.pythonhosted.org/packages/01/29/47c69bd55e945b0f9cdc2d4415fd56cffa260f1ad2d1ec0cb396e5cf26e0/habluetooth-6.18.0-cp314-cp314t-win_amd64.whl", hash = "sha256:a19935341c30caef7400abc6cdc534d6d48424f733eec8b4cfc722a789efb82c", size = 1582669, upload-time = "2026-06-24T01:32:59.218Z" }, ] [[package]] @@ -2034,7 +2026,7 @@ resolution-markers = [ "python_full_version >= '3.14.2' and python_full_version < '3.15'", ] dependencies = [ - { name = "habluetooth", version = "6.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "habluetooth", version = "6.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/6f/914fd8086f502748907751766e26e3f7fe5204347a679703ee9954e55e4d/home_assistant_bluetooth-2.0.0.tar.gz", hash = "sha256:3febd16b194812b156024d9bc8ce8ec7622cdd8d159047cac504cc77ca034ea1", size = 7604, upload-time = "2025-07-09T18:01:47.541Z" } wheels = [ @@ -2148,7 +2140,7 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version >= '3.14.2'" }, { name = "ulid-transform", version = "2.2.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, - { name = "uv", version = "0.11.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, + { name = "uv", marker = "python_full_version >= '3.14.2'" }, { name = "voluptuous", marker = "python_full_version >= '3.14.2'" }, { name = "voluptuous-openapi", version = "0.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "voluptuous-serialize", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, @@ -3477,14 +3469,14 @@ wheels = [ [[package]] name = "pytest-github-actions-annotate-failures" -version = "0.4.0" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/e1/8f2c242e6d75a26a8e5ddcc23f652a411e4aac3eedc4b923808ac0582685/pytest_github_actions_annotate_failures-0.4.0.tar.gz", hash = "sha256:77d6baa29c8c61c2dacc494fa76eb95a185f0ee61666714ac43fb12ea672d217", size = 10857, upload-time = "2026-03-02T18:57:40.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/a0/bdb91581b03c41016c78e16b8ec36c34d8508206fcb30f1951c9cdff2e97/pytest_github_actions_annotate_failures-0.4.2.tar.gz", hash = "sha256:5dd18304512361788bc7b5c5c805db853f03f4950c6be09b088de6bab8e2e6c9", size = 12158, upload-time = "2026-06-19T15:59:17.445Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/bd/11809f5c78d5d8da2d0e004845d2382768bc20798b3e0988bca61efd349f/pytest_github_actions_annotate_failures-0.4.0-py3-none-any.whl", hash = "sha256:285fed86e16b0b7a8eac6acdcde31913798fb739b15ef5b86895b4f5e32bf237", size = 6039, upload-time = "2026-03-02T18:57:39.991Z" }, + { url = "https://files.pythonhosted.org/packages/3c/09/c44e658f3a27c588c2017d858c8b0fa962612af9b74326beabbf010c839c/pytest_github_actions_annotate_failures-0.4.2-py3-none-any.whl", hash = "sha256:02911cd3b55f235328a334f8ca6037a89944398b8e8b028c82de97111eff4071", size = 6151, upload-time = "2026-06-19T15:59:16.486Z" }, ] [[package]] @@ -3539,15 +3531,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.4.0" +version = "1.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455, upload-time = "2026-05-28T01:15:37.639Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/1a/cbbaf13b730abb0a16b964d984e19f2fe520c21a4dc664051359a3f5a9e7/python_discovery-1.4.2.tar.gz", hash = "sha256:8f3746c4b4968d22afbb97d36e1a0e5b66e6c0f297290f2e95f05b9b8bf18690", size = 70277, upload-time = "2026-06-11T16:10:42.383Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217, upload-time = "2026-05-28T01:15:36.573Z" }, + { url = "https://files.pythonhosted.org/packages/1a/82/a70006589557f267f15bd384c0642ad49f0d97b690c3a05b166b9dcbad3b/python_discovery-1.4.2-py3-none-any.whl", hash = "sha256:475803f53b7b2ed6e490e27373f9d8340f7d2eebf9acdaf645d7d714c97bb500", size = 33886, upload-time = "2026-06-11T16:10:41.192Z" }, ] [[package]] @@ -4313,10 +4305,6 @@ wheels = [ name = "uv" version = "0.11.16" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version >= '3.14.2' and python_full_version < '3.15'", -] sdist = { url = "https://files.pythonhosted.org/packages/a1/99/025154611a4bd97a23851574c15d73bb71ada09d35f092d6972f9ac87f70/uv-0.11.16.tar.gz", hash = "sha256:4b435fcb0af8f34833dcc1903a8a223856437efd0d515c2160a2871def221238", size = 4177038, upload-time = "2026-05-21T22:10:01.009Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/55/e3/8b8cfc802bc476c67e31a39725538193265cf3a19585b4a60c232659f919/uv-0.11.16-py3-none-linux_armv6l.whl", hash = "sha256:c9e9d9cb73ee8cd2ad696dbf1bc3232abaac363270557684b6b85a2bdb8eb276", size = 23508087, upload-time = "2026-05-21T22:10:06.227Z" }, @@ -4339,40 +4327,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b6/68/3ed1c0bdfb4bec501e5cde73419b4f39c8a125ef905a85fc0f239f19eb9b/uv-0.11.16-py3-none-win_arm64.whl", hash = "sha256:d777cb29661cdfa7f90dae77406c85fb5b729bf8bc13941dc237958a1ea1ba00", size = 23502015, upload-time = "2026-05-21T22:09:56.014Z" }, ] -[[package]] -name = "uv" -version = "0.11.20" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and python_full_version < '3.14.2'", - "python_full_version >= '3.13.2' and python_full_version < '3.14'", - "python_full_version < '3.13.2'", -] -sdist = { url = "https://files.pythonhosted.org/packages/80/09/c29c0b90bc9308cfa6f5d77ce9b38ce97852210fda17d79019c7bcf9c3a1/uv-0.11.20.tar.gz", hash = "sha256:a246f30931cbc93d0a39d0cfc75be045fddd45773a734ddf8afa869aabc46c63", size = 4237464, upload-time = "2026-06-10T17:20:05.905Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/8a/25fc4d94ad3896e636466ee1fb03c4077f7a151c19cb25c1e6a731fa9cbf/uv-0.11.20-py3-none-linux_armv6l.whl", hash = "sha256:f867fd0807e39653fd101e16f2292ff488de14b5ffbb86a5678a87a27aa58ea0", size = 23713010, upload-time = "2026-06-10T17:19:32.058Z" }, - { url = "https://files.pythonhosted.org/packages/02/7d/bbaad5f0c616f7824149a4ac0271db14107b859b36bd75d16db1486c495f/uv-0.11.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f3bacc52778775cef671867ddab744b50c4183bc3cd6419a5fb4eb01a02f9526", size = 22918378, upload-time = "2026-06-10T17:19:20.828Z" }, - { url = "https://files.pythonhosted.org/packages/39/3e/e3d39361b95c262b43ccfe260f41184da71c536a08f39d4ad59ab962e459/uv-0.11.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c9f2062096e146b351fd5da3cd43e15a5c43bfa37166c509d884dab3dbb72f03", size = 21716975, upload-time = "2026-06-10T17:19:51.552Z" }, - { url = "https://files.pythonhosted.org/packages/24/30/9031204d7b592d1595322d7506944793728a74795a8c4a3c38bc4a8985f0/uv-0.11.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8fe143572a1f02d536c4e9c6994f0c89d9fa58ff6e5d91de55f61660658c694b", size = 23571826, upload-time = "2026-06-10T17:19:46.206Z" }, - { url = "https://files.pythonhosted.org/packages/98/c3/7f9f00c7a152e67d59ae5f25635d19ac4252929dd6ac454cdb1dee3119fa/uv-0.11.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:3d00754be09a381030829526f7ff47c06d0e28ca90760c4439be48e71c1bf13a", size = 23249218, upload-time = "2026-06-10T17:19:26.603Z" }, - { url = "https://files.pythonhosted.org/packages/35/a9/1ce58670a89c25d4a8b84532207183b5a97b3623d9b9151afe641499fbc8/uv-0.11.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:edb59d33e602fc462b6ed8fde66d404445294de41ad4de31039f7a2c41153601", size = 23302149, upload-time = "2026-06-10T17:20:01.111Z" }, - { url = "https://files.pythonhosted.org/packages/61/cf/3a498a315364f906bd655a94fa6b5f74f5240dc90875d6ab67ef28c081ec/uv-0.11.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14026b64ccbe0174e4fcf107f585f5d23f0f5b9f5b3e8b28394fe63fff3e60ff", size = 24652804, upload-time = "2026-06-10T17:19:16.692Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8c/68e4a805e3b49d834e95b3838e53a623d2e0ad956bb44e681ece54b3308b/uv-0.11.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73dcaf5543d1b24e4c6fa4c19af033ed015304171c132670ebe9ef01ddec3d17", size = 25660209, upload-time = "2026-06-10T17:20:08.156Z" }, - { url = "https://files.pythonhosted.org/packages/a6/de/e8e205a79b9a39454c5da5c2695e4911a42860a4404739e32802e599af0c/uv-0.11.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cac8ca5d187dc5040b1c22c83f6ed789195c3fc06b6a5a2b32c747c603f07820", size = 24866467, upload-time = "2026-06-10T17:19:37.981Z" }, - { url = "https://files.pythonhosted.org/packages/7e/38/f844d125db277d8ce0c921f0219078d292d0ee72d314d0c12f4cf510aa40/uv-0.11.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61588768f04a24b0b4d87b43f03b4521ba66d5eec3ab1aca37cb59b1d52337db", size = 24957448, upload-time = "2026-06-10T17:19:40.979Z" }, - { url = "https://files.pythonhosted.org/packages/ab/7d/b0e28abfa41c424d2a3df83be2b00b2fbe3ad5795baf7361262c6d800a62/uv-0.11.20-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:2d307d47b1a0cf8f76aa69bde850be407bca9482c13cff066142e586a5c57e77", size = 23671087, upload-time = "2026-06-10T17:19:54.595Z" }, - { url = "https://files.pythonhosted.org/packages/80/ff/92bce88101ce61d708e888db6ab7f5ebf4ccd61f54d3316e7e48a4be56a5/uv-0.11.20-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:bb5839cbb68d7469925fe1599dacd16cefdb7698a7adeaf9c8e4d8a7cd4122bf", size = 24324677, upload-time = "2026-06-10T17:19:43.534Z" }, - { url = "https://files.pythonhosted.org/packages/0f/90/d308bd88c7a53cae93f7fe13ce5c621895b8fb94e37911660a3de7283b67/uv-0.11.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:efefbd491ba443b326fdd344d7efc89c1de8a006cab5bc639a4d5ce9d1dd1ab9", size = 24429959, upload-time = "2026-06-10T17:19:29.352Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e6/b87c941b93b61dceaa11f4f8d02760de7aa7d1c58ea24a2298e7c5aafb4f/uv-0.11.20-py3-none-musllinux_1_1_i686.whl", hash = "sha256:daa41b97386699212b2266a80c178140c5396e3c444ff5553f68f79812334e41", size = 23880515, upload-time = "2026-06-10T17:19:23.733Z" }, - { url = "https://files.pythonhosted.org/packages/53/ab/11b07641f8387177889f4341a36318561208c18703837bc47163244aa11b/uv-0.11.20-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:37ca61eddb940d1c698fce46d63789adfda3033344dabab0e18e2958e1a69771", size = 25171603, upload-time = "2026-06-10T17:19:34.848Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7d/62af57509c7007600ce11cd5222b2cb84d5cd8f80f427a499d1b44ef3601/uv-0.11.20-py3-none-win32.whl", hash = "sha256:32893ee9f94657fbf89e22638ac88850db4529d454f102bfa7ceea5fc9fe8d77", size = 22570328, upload-time = "2026-06-10T17:19:48.808Z" }, - { url = "https://files.pythonhosted.org/packages/66/35/c9ee48cdce11f5cdac9e2be41a5446bae7382cde18d2ce1050bcd81dc05a/uv-0.11.20-py3-none-win_amd64.whl", hash = "sha256:4836044213bb23a3be1f5550db340d3a19babe1dfc3ca1313544e8b614085ce9", size = 25228859, upload-time = "2026-06-10T17:19:57.824Z" }, - { url = "https://files.pythonhosted.org/packages/8b/8d/00a382c2f8f44b328cf98f734a3fcd72957698c30bed2392bd241b573384/uv-0.11.20-py3-none-win_arm64.whl", hash = "sha256:442ae26f47bf6e58b072e99dbfd6d5296ab90308574b2c02adff1dace051008d", size = 23664943, upload-time = "2026-06-10T17:20:03.774Z" }, -] - [[package]] name = "virtualenv" -version = "21.4.1" +version = "21.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -4380,9 +4337,9 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/f0/b47ecf438211a25a97f8f0e4b23c22bc2496ebfea18dd6ec16210f09cc36/virtualenv-21.4.1.tar.gz", hash = "sha256:2ca543c713b72840ceffd94e9bdedfbd09a661defa1f7f69e5429ad4059442e2", size = 7613344, upload-time = "2026-05-28T04:12:49.905Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/a5/81f987504738e6defeed61ec1c47e2aefab3c35d8eeb87e1b3f38cf28254/virtualenv-21.5.1.tar.gz", hash = "sha256:dca3bf98275a59c652b69d68e73433e597d977c2da9198882479d1a7188009c8", size = 4578798, upload-time = "2026-06-16T16:23:58.603Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/dc/ac4f3a987a87e1a18556896f257c4e15c95ed157b7975347ec6b313b75ce/virtualenv-21.4.1-py3-none-any.whl", hash = "sha256:caf4ff72d1b4039057f41d8e8466e859513d67c0400d9c6b62c02c9d1ebc3e12", size = 7594078, upload-time = "2026-05-28T04:12:47.686Z" }, + { url = "https://files.pythonhosted.org/packages/2c/02/3623e6169bed617ed1e2d372f7c69f92ec28d54c4dfc997055c8578ec148/virtualenv-21.5.1-py3-none-any.whl", hash = "sha256:55aa670b67bbfb991b03fda39bd3276d92c419d702376e98c5df1c9989a26783", size = 4558820, upload-time = "2026-06-16T16:23:56.963Z" }, ] [[package]] @@ -4851,7 +4808,6 @@ dependencies = [ { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, { name = "bellows" }, { name = "frozendict" }, - { name = "zha-quirks" }, { name = "zigpy" }, { name = "zigpy-deconz" }, { name = "zigpy-xbee" }, @@ -4887,8 +4843,8 @@ ci = [ { name = "pytest-xdist" }, { name = "python-slugify" }, { name = "ruff" }, - { name = "uv", version = "0.11.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, - { name = "uv", version = "0.11.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "uv" }, + { name = "zha-quirks" }, ] testing = [ { name = "codespell" }, @@ -4906,8 +4862,8 @@ testing = [ { name = "pytest-timeout" }, { name = "python-slugify" }, { name = "ruff" }, - { name = "uv", version = "0.11.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, - { name = "uv", version = "0.11.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, + { name = "uv" }, + { name = "zha-quirks" }, ] [package.metadata] @@ -4919,8 +4875,7 @@ requires-dist = [ { name = "homeassistant", marker = "extra == 'testing'" }, { name = "pytest", marker = "extra == 'testing'" }, { name = "python-slugify", marker = "extra == 'testing'" }, - { name = "zha-quirks", specifier = ">=1.2.0" }, - { name = "zigpy", editable = "../zigpy" }, + { name = "zigpy", specifier = "==2.0.0" }, { name = "zigpy-deconz", specifier = "==0.25.5" }, { name = "zigpy-xbee", specifier = "==0.21.1" }, { name = "zigpy-zigate", specifier = "==0.14.0" }, @@ -4948,6 +4903,7 @@ ci = [ { name = "python-slugify" }, { name = "ruff", specifier = "==0.15.16" }, { name = "uv", specifier = ">=0.11.16" }, + { name = "zha-quirks", git = "https://github.com/puddly/zha-device-handlers?rev=7ac5b603cec5ae12fd24a7f40522aeb607aa4b1a" }, ] testing = [ { name = "codespell", specifier = "==2.4.2" }, @@ -4966,23 +4922,22 @@ testing = [ { name = "python-slugify" }, { name = "ruff", specifier = "==0.15.16" }, { name = "uv", specifier = ">=0.11.16" }, + { name = "zha-quirks", git = "https://github.com/puddly/zha-device-handlers?rev=7ac5b603cec5ae12fd24a7f40522aeb607aa4b1a" }, ] [[package]] name = "zha-quirks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } +version = "2.0.0" +source = { git = "https://github.com/puddly/zha-device-handlers?rev=7ac5b603cec5ae12fd24a7f40522aeb607aa4b1a#7ac5b603cec5ae12fd24a7f40522aeb607aa4b1a" } dependencies = [ + { name = "zha" }, { name = "zigpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/54/c3fceddc846bd77b96e5e02e16d76b033c7d648e7e4b85e1a395d0951644/zha_quirks-1.2.0.tar.gz", hash = "sha256:e2e24018fe0f30c6c1956a61045f2e17ac55b16e4f69b1765a315e9c16417614", size = 440807, upload-time = "2026-04-29T06:53:44.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/5e/6d34c2a36961abb5e9c8e370bee6f8558a342d6321e47e4227dec2639624/zha_quirks-1.2.0-py3-none-any.whl", hash = "sha256:fd7237e166901ce16e32e13883856eeec5fb3ed17afcd01abcf8acdcbd4e07c1", size = 546259, upload-time = "2026-04-29T06:53:43.612Z" }, -] [[package]] name = "zigpy" -source = { editable = "../zigpy" } +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14.2'" }, { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14.2'" }, @@ -4998,59 +4953,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "voluptuous" }, ] - -[package.metadata] -requires-dist = [ - { name = "aiohttp" }, - { name = "aiosqlite", specifier = ">=0.20.0,<0.22.0" }, - { name = "attrs" }, - { name = "crccheck" }, - { name = "cryptography" }, - { name = "frozendict" }, - { name = "jsonschema" }, - { name = "serialx", specifier = ">=1.4.0" }, - { name = "typing-extensions" }, - { name = "voluptuous" }, -] - -[package.metadata.requires-dev] -ci = [ - { name = "aiohttp", specifier = "<3.14" }, - { name = "aioresponses" }, - { name = "asynctest" }, - { name = "codespell", specifier = "==2.4.1" }, - { name = "coverage", extras = ["toml"] }, - { name = "coveralls" }, - { name = "filelock" }, - { name = "freezegun" }, - { name = "mypy", specifier = "==1.19.1" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio", specifier = ">=1.3.0" }, - { name = "pytest-cov" }, - { name = "pytest-github-actions-annotate-failures" }, - { name = "pytest-timeout" }, - { name = "pytest-xdist" }, - { name = "ruff", specifier = "==0.14.14" }, - { name = "tomli" }, -] -testing = [ - { name = "aiohttp", specifier = "<3.14" }, - { name = "aioresponses" }, - { name = "asynctest" }, - { name = "codespell", specifier = "==2.4.1" }, - { name = "coverage", extras = ["toml"] }, - { name = "coveralls" }, - { name = "filelock" }, - { name = "freezegun" }, - { name = "mypy", specifier = "==1.19.1" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio", specifier = ">=1.3.0" }, - { name = "pytest-cov" }, - { name = "pytest-timeout" }, - { name = "ruff", specifier = "==0.14.14" }, - { name = "tomli" }, +sdist = { url = "https://files.pythonhosted.org/packages/36/a6/abfdc26601b921b1a2983aa98ce2e596222470017c9de00849a1ce4a2daf/zigpy-2.0.0.tar.gz", hash = "sha256:4f8369273591b796845e9238db52d1e4b11d836565a2c723e2db078cf7fcd808", size = 325428, upload-time = "2026-06-24T00:58:53.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/92/c62a6dc4b369b2f3db03ab1e59ff42f05a7e48a444aa7b77e0d113064af6/zigpy-2.0.0-py3-none-any.whl", hash = "sha256:f861970db86c86f63f18799a0551bb0a8e8a924709e2938645ad564601cd7eec", size = 245023, upload-time = "2026-06-24T00:58:52.154Z" }, ] [[package]] From 9b262af4499be83a0557b9717495d3ce32d5056a Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:59:21 -0400 Subject: [PATCH 36/41] Fix pre-commit --- tests/test_button.py | 4 +--- tests/test_discover.py | 7 +------ tests/test_sensor.py | 4 +--- zha/application/platforms/__init__.py | 6 ++++-- zha/quirks.py | 13 +++++++++++-- zha/zigbee/device.py | 8 ++++++-- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/test_button.py b/tests/test_button.py index a1b1e543b..19a15eac8 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -378,9 +378,7 @@ async def test_quirks_v2_button_only_cluster_is_not_configured( registry = DeviceRegistry() ( - QuirkBuilder( - "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2" - ) + QuirkBuilder("Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2") .replaces(OppleCluster) .command_button( OppleCluster.ServerCommandDefs.self_test.name, diff --git a/tests/test_discover.py b/tests/test_discover.py index 9b8591ad0..4bb89f653 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -61,12 +61,7 @@ from zha.application.platforms.const import PHILIPS_REMOTE_CLUSTER from zha.application.platforms.light import HueLight from zha.application.platforms.number import BaseNumber, NumberMode -from zha.quirks import ( - QUIRK_REGISTRY_ENTRY_ATTR, - DeviceMatch, - DeviceRegistry, - ModelInfo, -) +from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch, DeviceRegistry, ModelInfo from zha.units import UnitOfTime diff --git a/tests/test_sensor.py b/tests/test_sensor.py index ca743e7e3..892c74d87 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1645,9 +1645,7 @@ async def test_quirks_v2_sensor_attribute_init_reads_cluster( registry = DeviceRegistry() ( - QuirkBuilder( - "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2" - ) + QuirkBuilder("Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2") .replaces(OppleCluster) .sensor( "last_feeding_size", diff --git a/zha/application/platforms/__init__.py b/zha/application/platforms/__init__.py index 608afe56b..675918e88 100644 --- a/zha/application/platforms/__init__.py +++ b/zha/application/platforms/__init__.py @@ -40,7 +40,7 @@ DEFAULT_UPDATE_GROUP_FROM_CHILD_DELAY: float = 0.5 -ENTITY_REGISTRY: dict[ClusterId, list[type[PlatformEntity]]] = defaultdict(list) +ENTITY_REGISTRY: dict[ClusterId | int, list[type[PlatformEntity]]] = defaultdict(list) GROUP_ENTITY_REGISTRY: list[type[GroupEntity]] = [] @@ -142,7 +142,9 @@ def __post_init__(self) -> None: ) -def register_entity[T: type[PlatformEntity]](cluster_id: ClusterId) -> Callable[[T], T]: +def register_entity[T: type[PlatformEntity]]( + cluster_id: ClusterId | int, +) -> Callable[[T], T]: """Register an entity class for discovery.""" def inner(cls: T) -> T: diff --git a/zha/quirks.py b/zha/quirks.py index df8377446..f7d96bb31 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -17,6 +17,9 @@ from zigpy.zcl.clusters.general import Ota if TYPE_CHECKING: + from zigpy.application import ControllerApplication + from zigpy.types import EUI64, NWK + from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) @@ -121,11 +124,17 @@ def from_class(cls, target: type) -> QuirkSource: ) +# A zigpy device class whose constructor takes the device it replaces as a 4th arg. +ReplacingZigpyDeviceFactory = Callable[ + [ControllerApplication, EUI64, NWK, zigpy.device.Device], zigpy.device.Device +] + + @dataclass(frozen=True) class ReplaceZigpyDevice: - """A transform wrapping a device in `device_cls` (a `BaseCustomDevice`).""" + """A transform wrapping a device in `device_cls` (a `ReplacingZigpyDeviceFactory`).""" - device_cls: type[zigpy.device.Device] + device_cls: ReplacingZigpyDeviceFactory def __call__(self, device: zigpy.device.Device) -> zigpy.device.Device: """Replace a zigpy device.""" diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 51cce76c0..d9a6a1cb0 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -87,7 +87,11 @@ from zha.event import EventBase from zha.exceptions import ZHAException from zha.mixins import LogMixin -from zha.quirks import QUIRK_REGISTRY_ENTRY_ATTR, DeviceMatch +from zha.quirks import ( + QUIRK_REGISTRY_ENTRY_ATTR, + DeviceMatch, + ReplacingZigpyDeviceFactory, +) from zha.zigbee.cluster_config import ( aggregate_cluster_configs, configure_cluster_configs, @@ -330,7 +334,7 @@ class Device(LogMixin, EventBase): # Authoring surface for hand-written quirks; `None` marks the unquirked fallback. _device_match: DeviceMatch | None = None - _zigpy_device_cls: type[zigpy.device.Device] | None = None + _zigpy_device_cls: ReplacingZigpyDeviceFactory | None = None _zigpy_device_transforms: tuple[ Callable[[zigpy.device.Device], zigpy.device.Device], ... ] = () From e284888248acc6d55cfd85fa0c334b63f6a4f675 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:34:19 -0400 Subject: [PATCH 37/41] Regenerate mypy ignores --- pyproject.toml | 138 ++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 70 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b3bbceca2..93c03f6ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,62 +109,60 @@ non_interactive = true # first; clean up a module's errors and re-run the tool to shrink the list. # Autogenerated mypy overrides: start [[tool.mypy.overrides]] -module = "zha.application.discovery" +module = "zha.application.platforms.button" disable_error_code = [ - "no-any-return", # 1 + "assignment", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.lock" +module = "zha.application.platforms.update" disable_error_code = [ - "no-untyped-def", # 1 + "arg-type", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.select" +module = "zha.application.platforms.virtual" disable_error_code = [ - "override", # 1 + "no-any-return", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.virtual" +module = "zha.quirks" disable_error_code = [ "no-any-return", # 1 ] [[tool.mypy.overrides]] -module = "zha.zigbee.endpoint" +module = "zha.application.platforms.number" disable_error_code = [ - "no-any-return", # 1 + "no-any-return", # 2 ] [[tool.mypy.overrides]] -module = "zha.quirks.metadata" +module = "zha.application.platforms.siren" disable_error_code = [ - "no-untyped-def", # 1 - "var-annotated", # 1 + "assignment", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.binary_sensor" +module = "zha.application.discovery" disable_error_code = [ - "no-any-return", # 1 - "no-untyped-def", # 1 - "override", # 1 + "no-any-return", # 3 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.device_tracker" +module = "zha.application.platforms.binary_sensor" disable_error_code = [ - "no-untyped-def", # 3 + "no-any-return", # 1 + "no-untyped-call", # 1 + "no-untyped-def", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.switch" +module = "zha.application.platforms.lock" disable_error_code = [ - "arg-type", # 1 - "no-any-return", # 1 - "override", # 1 + "arg-type", # 2 + "no-untyped-def", # 1 ] [[tool.mypy.overrides]] @@ -175,75 +173,77 @@ disable_error_code = [ ] [[tool.mypy.overrides]] -module = "zha.application.platforms.button" +module = "zha.zigbee.cluster_config" disable_error_code = [ - "assignment", # 2 - "override", # 2 + "arg-type", # 2 + "no-untyped-call", # 1 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.number" +module = "zha.application.platforms" disable_error_code = [ "arg-type", # 1 + "assignment", # 1 "no-any-return", # 2 - "override", # 1 ] [[tool.mypy.overrides]] -module = "zha.event" +module = "zha.application.platforms.device_tracker" disable_error_code = [ - "no-untyped-def", # 4 + "arg-type", # 1 + "no-untyped-def", # 3 ] [[tool.mypy.overrides]] -module = "zha.quirks" +module = "zha.application.platforms.switch" disable_error_code = [ "arg-type", # 1 "no-any-return", # 1 - "union-attr", # 3 -] - -[[tool.mypy.overrides]] -module = "zha.zigbee.group" -disable_error_code = [ - "no-any-return", # 2 - "no-untyped-def", # 3 + "no-untyped-call", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.gateway" +module = "zha.event" disable_error_code = [ - "no-any-return", # 2 - "no-untyped-call", # 4 + "no-untyped-def", # 4 ] [[tool.mypy.overrides]] -module = "zha.application.platforms" +module = "zha.zigbee.group" disable_error_code = [ - "arg-type", # 1 - "assignment", # 1 - "attr-defined", # 2 - "no-any-return", # 2 + "no-untyped-def", # 3 + "return-value", # 2 ] [[tool.mypy.overrides]] module = "zha.application.platforms.fan" disable_error_code = [ "arg-type", # 2 + "index", # 1 "no-any-return", # 3 "no-untyped-def", # 1 ] +[[tool.mypy.overrides]] +module = "zha.application.gateway" +disable_error_code = [ + "arg-type", # 3 + "assignment", # 2 + "no-untyped-call", # 4 + "return-value", # 1 +] + [[tool.mypy.overrides]] module = "zha.application.platforms.alarm_control_panel" disable_error_code = [ + "no-untyped-call", # 1 "no-untyped-def", # 11 ] [[tool.mypy.overrides]] module = "zha.application.platforms.cover" disable_error_code = [ - "no-any-return", # 2 + "no-any-return", # 3 "no-untyped-call", # 6 "no-untyped-def", # 5 ] @@ -251,52 +251,50 @@ disable_error_code = [ [[tool.mypy.overrides]] module = "zha.application.helpers" disable_error_code = [ - "arg-type", # 1 + "arg-type", # 2 "no-untyped-call", # 4 "no-untyped-def", # 10 ] -[[tool.mypy.overrides]] -module = "zha.quirks.v2" -disable_error_code = [ - "arg-type", # 18 - "assignment", # 2 -] - [[tool.mypy.overrides]] module = "zha.application.platforms.light" disable_error_code = [ - "arg-type", # 4 + "arg-type", # 5 "no-any-return", # 4 - "no-untyped-call", # 7 + "no-untyped-call", # 11 "no-untyped-def", # 8 + "type-var", # 2 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.sensor" +module = "zha.application.platforms.climate" disable_error_code = [ - "arg-type", # 2 - "no-any-return", # 13 - "no-untyped-call", # 2 - "no-untyped-def", # 3 - "override", # 3 + "call-overload", # 4 + "no-any-return", # 23 + "no-untyped-def", # 8 ] [[tool.mypy.overrides]] -module = "zha.application.platforms.climate" +module = "zha.application.platforms.sensor" disable_error_code = [ - "no-any-return", # 21 - "no-untyped-def", # 8 + "arg-type", # 10 + "assignment", # 3 + "attr-defined", # 5 + "no-any-return", # 9 + "no-untyped-call", # 4 + "no-untyped-def", # 3 + "operator", # 4 ] [[tool.mypy.overrides]] module = "zha.zigbee.device" disable_error_code = [ - "arg-type", # 10 - "assignment", # 2 - "no-any-return", # 14 - "no-untyped-call", # 1 + "arg-type", # 12 + "no-any-return", # 4 + "no-untyped-call", # 3 "no-untyped-def", # 10 + "return-value", # 1 + "union-attr", # 18 ] # Autogenerated mypy overrides: end From 9d3f64109a2bac09764d3046815c1a1ebc9efb9c Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:36:45 -0400 Subject: [PATCH 38/41] Fix typing on 3.12 --- zha/quirks.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/zha/quirks.py b/zha/quirks.py index f7d96bb31..7bc45cd3c 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -9,18 +9,16 @@ import inspect import logging from pathlib import Path -from typing import TYPE_CHECKING, NamedTuple +from typing import NamedTuple +from zigpy.application import ControllerApplication import zigpy.device import zigpy.profiles.zha +from zigpy.types import EUI64, NWK from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import Ota -if TYPE_CHECKING: - from zigpy.application import ControllerApplication - from zigpy.types import EUI64, NWK - - from zha.zigbee.device import Device +from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) From 4b53b539cfb0c8d1bbde59aa2ee839781e087619 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:44:57 -0400 Subject: [PATCH 39/41] Clean up circular export surfaced by xdist --- zha/quirks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zha/quirks.py b/zha/quirks.py index 7bc45cd3c..f297c89b8 100644 --- a/zha/quirks.py +++ b/zha/quirks.py @@ -9,7 +9,7 @@ import inspect import logging from pathlib import Path -from typing import NamedTuple +from typing import TYPE_CHECKING, NamedTuple from zigpy.application import ControllerApplication import zigpy.device @@ -18,7 +18,8 @@ from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import Ota -from zha.zigbee.device import Device +if TYPE_CHECKING: + from zha.zigbee.device import Device _LOGGER = logging.getLogger(__name__) From a310225ef516bb922481b1e40370f72ef29c9e76 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 23:13:49 -0400 Subject: [PATCH 40/41] Regenerate diagnostics for broken Ubisys device --- tests/data/devices/ubisys-j1-5502.json | 36 ++------------------------ 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/tests/data/devices/ubisys-j1-5502.json b/tests/data/devices/ubisys-j1-5502.json index 9e51b3e43..5aeedc677 100644 --- a/tests/data/devices/ubisys-j1-5502.json +++ b/tests/data/devices/ubisys-j1-5502.json @@ -338,7 +338,7 @@ "class_name": "Cover", "translation_key": "cover", "translation_placeholders": null, - "device_class": "awning", + "device_class": null, "state_class": null, "entity_category": null, "entity_registry_enabled_default": true, @@ -859,7 +859,7 @@ "state": { "class_name": "WindowCoveringTypeSensor", "available": true, - "state": "Awning" + "state": null } }, { @@ -892,38 +892,6 @@ } ], "switch": [ - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-258-inverted", - "migrate_unique_ids": [], - "platform": "switch", - "class_name": "WindowCoveringInversionSwitch", - "translation_key": "inverted", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "config_status", - "invert_attribute_name": null, - "force_inverted": false, - "off_value": 0, - "on_value": 1 - }, - "state": { - "class_name": "WindowCoveringInversionSwitch", - "available": true, - "state": false, - "inverted": false - } - }, { "info_object": { "fallback_name": "Detached mode", From 36e9d31548e38c9c0c04697c978324050faa3327 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 23 Jun 2026 23:25:03 -0400 Subject: [PATCH 41/41] Drop bad ubisys diagnostics --- tests/data/devices/ubisys-j1-5502.json | 931 ------------------------- 1 file changed, 931 deletions(-) delete mode 100644 tests/data/devices/ubisys-j1-5502.json diff --git a/tests/data/devices/ubisys-j1-5502.json b/tests/data/devices/ubisys-j1-5502.json deleted file mode 100644 index 5aeedc677..000000000 --- a/tests/data/devices/ubisys-j1-5502.json +++ /dev/null @@ -1,931 +0,0 @@ -{ - "version": 2, - "ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "nwk": "0x31A9", - "manufacturer": "ubisys", - "model": "J1 (5502)", - "friendly_manufacturer": "ubisys", - "friendly_model": "J1 (5502)", - "name": "ubisys J1 (5502)", - "quirk_applied": true, - "quirk_class": "zhaquirks.ubisys.cover_j1:(ubisys / J1 (5502))", - "exposes_features": [ - "se_poll_summation" - ], - "manufacturer_code": 4660, - "power_source": "Mains", - "lqi": null, - "rssi": null, - "last_seen": "2026-03-04T18:21:39.038488+00:00", - "available": true, - "device_type": "EndDevice", - "active_coordinator": false, - "node_descriptor": { - "logical_type": "EndDevice", - "complex_descriptor_available": false, - "user_descriptor_available": false, - "reserved": 0, - "aps_flags": 0, - "frequency_band": 8, - "mac_capability_flags": 140, - "manufacturer_code": 4660, - "maximum_buffer_size": 108, - "maximum_incoming_transfer_size": 1613, - "server_mask": 11264, - "maximum_outgoing_transfer_size": 1613, - "descriptor_capability_field": 0 - }, - "endpoints": { - "1": { - "profile_id": 260, - "device_type": { - "name": "WINDOW_COVERING_DEVICE", - "id": 514 - }, - "in_clusters": [ - { - "cluster_id": "0x0000", - "endpoint_attribute": "basic", - "attributes": [ - { - "id": "0x0004", - "name": "manufacturer", - "zcl_type": "string", - "value": "ubisys" - }, - { - "id": "0x0005", - "name": "model", - "zcl_type": "string", - "value": "J1 (5502)" - } - ] - }, - { - "cluster_id": "0x0003", - "endpoint_attribute": "identify", - "attributes": [] - }, - { - "cluster_id": "0x0102", - "endpoint_attribute": "window_covering", - "attributes": [ - { - "id": "0x0008", - "name": "current_position_lift_percentage", - "zcl_type": "uint8", - "unsupported": true - }, - { - "id": "0x0009", - "name": "current_position_tilt_percentage", - "zcl_type": "uint8", - "unsupported": true - }, - { - "id": "0x0017", - "name": "window_covering_mode", - "zcl_type": "map8", - "value": 0 - } - ] - }, - { - "cluster_id": "0xfbfe", - "endpoint_attribute": "ubisys_j1_calibration", - "attributes": [ - { - "id": "0x0004", - "name": "calibration_state", - "zcl_type": "enum8", - "value": 0 - }, - { - "id": "0x0002", - "name": "enter_calibration_mode", - "zcl_type": "bool", - "unsupported": true - }, - { - "id": "0x0003", - "name": "exit_calibration_mode", - "zcl_type": "bool", - "unsupported": true - }, - { - "id": "0x0000", - "name": "prepare_calibration", - "zcl_type": "bool", - "unsupported": true - }, - { - "id": "0x0001", - "name": "run_calibration", - "zcl_type": "bool", - "unsupported": true - } - ] - }, - { - "cluster_id": "0xfbff", - "endpoint_attribute": "ubisys_input_config", - "attributes": [ - { - "id": "0x0001", - "name": "detached", - "zcl_type": "bool", - "value": 0 - }, - { - "id": "0x0000", - "name": "input_mode", - "zcl_type": "enum8", - "value": 0 - } - ] - } - ], - "out_clusters": [ - { - "cluster_id": "0x000a", - "endpoint_attribute": "time", - "attributes": [] - } - ] - } - }, - "original_signature": { - "manufacturer": "ubisys", - "model": "J1 (5502)", - "node_desc": { - "logical_type": 2, - "complex_descriptor_available": 0, - "user_descriptor_available": 0, - "reserved": 0, - "aps_flags": 0, - "frequency_band": 8, - "mac_capability_flags": 140, - "manufacturer_code": 4660, - "maximum_buffer_size": 108, - "maximum_incoming_transfer_size": 1613, - "server_mask": 11264, - "maximum_outgoing_transfer_size": 1613, - "descriptor_capability_field": 0 - }, - "endpoints": { - "1": { - "profile_id": "0x0104", - "device_type": "0x0202", - "input_clusters": [ - "0x0000", - "0x0003", - "0x0102" - ], - "output_clusters": [ - "0x000a" - ] - } - } - }, - "zha_lib_entities": { - "button": [ - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-3", - "migrate_unique_ids": [], - "platform": "button", - "class_name": "IdentifyButton", - "translation_key": null, - "translation_placeholders": null, - "device_class": "identify", - "state_class": null, - "entity_category": "diagnostic", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "command": "identify", - "args": [ - 5 - ], - "kwargs": {} - }, - "state": { - "class_name": "IdentifyButton", - "available": true - } - }, - { - "info_object": { - "fallback_name": "Enter calibration mode", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-enter_calibration_mode", - "migrate_unique_ids": [], - "platform": "button", - "class_name": "WriteAttributeButton", - "translation_key": "enter_calibration_mode", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "enter_calibration_mode", - "attribute_value": true - }, - "state": { - "class_name": "WriteAttributeButton", - "available": true - } - }, - { - "info_object": { - "fallback_name": "Exit calibration mode", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-exit_calibration_mode", - "migrate_unique_ids": [], - "platform": "button", - "class_name": "WriteAttributeButton", - "translation_key": "exit_calibration_mode", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "exit_calibration_mode", - "attribute_value": true - }, - "state": { - "class_name": "WriteAttributeButton", - "available": true - } - }, - { - "info_object": { - "fallback_name": "Prepare manual calibration", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-prepare_calibration", - "migrate_unique_ids": [], - "platform": "button", - "class_name": "WriteAttributeButton", - "translation_key": "prepare_manual_calibration", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "prepare_calibration", - "attribute_value": true - }, - "state": { - "class_name": "WriteAttributeButton", - "available": true - } - }, - { - "info_object": { - "fallback_name": "Run auto-calibration", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-run_calibration", - "migrate_unique_ids": [], - "platform": "button", - "class_name": "WriteAttributeButton", - "translation_key": "run_auto_calibration", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "run_calibration", - "attribute_value": true - }, - "state": { - "class_name": "WriteAttributeButton", - "available": true - } - } - ], - "cover": [ - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-258", - "migrate_unique_ids": [], - "platform": "cover", - "class_name": "Cover", - "translation_key": "cover", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": null, - "entity_registry_enabled_default": true, - "enabled": true, - "primary": true, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null - }, - "state": { - "class_name": "Cover", - "available": true, - "current_position": null, - "current_tilt_position": null, - "state": null, - "is_opening": null, - "is_closing": null, - "is_closed": null, - "supported_features": 15 - } - } - ], - "number": [ - { - "info_object": { - "fallback_name": "Additional steps", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-additional_steps", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "additional_steps", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 100, - "native_min_value": 0, - "native_step": 1, - "native_unit_of_measurement": "%" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Inactive power threshold", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-inactive_power_threshold", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "inactive_power_threshold", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 65.535, - "native_min_value": 0, - "native_step": 0.001, - "native_unit_of_measurement": "W" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Closed limit lift", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_closed_limit_lift_config", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "closed_limit_lift", - "translation_placeholders": null, - "device_class": "distance", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 65535, - "native_min_value": 0, - "native_step": 1, - "native_unit_of_measurement": "cm" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Closed limit tilt", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_closed_limit_tilt_config", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "closed_limit_tilt", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 6553.5, - "native_min_value": 0, - "native_step": 0.1, - "native_unit_of_measurement": "\u00b0" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Open limit lift", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_open_limit_lift_config", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "open_limit_lift", - "translation_placeholders": null, - "device_class": "distance", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 65535, - "native_min_value": 0, - "native_step": 1, - "native_unit_of_measurement": "cm" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Open limit tilt", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-installed_open_limit_tilt_config", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "open_limit_tilt", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 6553.5, - "native_min_value": 0, - "native_step": 0.1, - "native_unit_of_measurement": "\u00b0" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Tilt turn time (open to close)", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-lift_to_tilt_transition_steps", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "tilt_turn_time_open_to_close", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 1310.7, - "native_min_value": 0, - "native_step": 0.02, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Tilt turn time (close to open)", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-lift_to_tilt_transition_steps_2", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "tilt_turn_time_close_to_open", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 1310.7, - "native_min_value": 0, - "native_step": 0.02, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Startup time", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-startup_steps", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "startup_time", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 1310.7, - "native_min_value": 0, - "native_step": 0.02, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Travel time (open to close)", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-total_steps", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "travel_time_open_to_close", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 1310.7, - "native_min_value": 0, - "native_step": 0.02, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Travel time (close to open)", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-total_steps_2", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "travel_time_close_to_open", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 1310.7, - "native_min_value": 0, - "native_step": 0.02, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Turnaround guard time", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-turnaround_guard_time", - "migrate_unique_ids": [], - "platform": "number", - "class_name": "NumberConfigurationEntity", - "translation_key": "turnaround_guard_time", - "translation_placeholders": null, - "device_class": "duration", - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "mode": "box", - "native_max_value": 12.7, - "native_min_value": 0.5, - "native_step": 0.05, - "native_unit_of_measurement": "s" - }, - "state": { - "class_name": "NumberConfigurationEntity", - "available": true, - "state": null - } - } - ], - "select": [ - { - "info_object": { - "fallback_name": "Window covering type", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-window_covering_type_config", - "migrate_unique_ids": [], - "platform": "select", - "class_name": "ZCLEnumSelectEntity", - "translation_key": "window_covering_type", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "enum": "WindowCoveringType", - "options": [ - "Rollershade", - "Rollershade two motors", - "Rollershade exterior", - "Rollershade exterior two motors", - "Drapery", - "Awning", - "Shutter", - "Tilt blind tilt only", - "Tilt blind tilt and lift", - "Projector screen" - ] - }, - "state": { - "class_name": "ZCLEnumSelectEntity", - "available": true, - "state": null - } - } - ], - "sensor": [ - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-0-lqi", - "migrate_unique_ids": [], - "platform": "sensor", - "class_name": "LQISensor", - "translation_key": "lqi", - "translation_placeholders": null, - "device_class": null, - "state_class": "measurement", - "entity_category": "diagnostic", - "entity_registry_enabled_default": false, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "suggested_display_precision": null, - "unit": null - }, - "state": { - "class_name": "LQISensor", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-0-rssi", - "migrate_unique_ids": [], - "platform": "sensor", - "class_name": "RSSISensor", - "translation_key": "rssi", - "translation_placeholders": null, - "device_class": "signal_strength", - "state_class": "measurement", - "entity_category": "diagnostic", - "entity_registry_enabled_default": false, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "suggested_display_precision": null, - "unit": "dBm" - }, - "state": { - "class_name": "RSSISensor", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": null, - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-258-window_covering_type", - "migrate_unique_ids": [], - "platform": "sensor", - "class_name": "WindowCoveringTypeSensor", - "translation_key": "window_covering_type", - "translation_placeholders": null, - "device_class": "enum", - "state_class": null, - "entity_category": "diagnostic", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "suggested_display_precision": null, - "unit": null - }, - "state": { - "class_name": "WindowCoveringTypeSensor", - "available": true, - "state": null - } - }, - { - "info_object": { - "fallback_name": "Auto-calibration state", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-calibration_state", - "migrate_unique_ids": [], - "platform": "sensor", - "class_name": "EnumSensor", - "translation_key": "auto_calibration_state", - "translation_placeholders": null, - "device_class": "enum", - "state_class": null, - "entity_category": "diagnostic", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "suggested_display_precision": null, - "unit": null - }, - "state": { - "class_name": "EnumSensor", - "available": true, - "state": "Idle" - } - } - ], - "switch": [ - { - "info_object": { - "fallback_name": "Detached mode", - "unique_id": "ab:cd:ef:12:d1:f5:5f:ec-1-detached", - "migrate_unique_ids": [], - "platform": "switch", - "class_name": "ConfigurableAttributeSwitch", - "translation_key": "detached", - "translation_placeholders": null, - "device_class": null, - "state_class": null, - "entity_category": "config", - "entity_registry_enabled_default": true, - "enabled": true, - "primary": false, - "device_ieee": "ab:cd:ef:12:d1:f5:5f:ec", - "endpoint_id": 1, - "available": true, - "group_id": null, - "attribute_name": "detached", - "invert_attribute_name": null, - "force_inverted": false, - "off_value": 0, - "on_value": 1 - }, - "state": { - "class_name": "ConfigurableAttributeSwitch", - "available": true, - "state": false, - "inverted": false - } - } - ] - }, - "neighbors": [], - "routes": [] -} \ No newline at end of file