zwave-js-server-python 0.56.0__tar.gz → 0.57.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/PKG-INFO +3 -3
  2. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/pyproject.toml +2 -2
  3. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/__init__.py +3 -3
  4. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/meter.py +1 -0
  5. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/inclusion_and_provisioning.py +36 -32
  6. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/device_class.py +0 -14
  7. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/__init__.py +13 -15
  8. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/value.py +0 -8
  9. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/PKG-INFO +3 -3
  10. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/LICENSE +0 -0
  11. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/MANIFEST.in +0 -0
  12. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/README.md +0 -0
  13. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/setup.cfg +0 -0
  14. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/__init__.py +0 -0
  15. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/__main__.py +0 -0
  16. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/client.py +0 -0
  17. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/__init__.py +0 -0
  18. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/barrier_operator.py +0 -0
  19. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/basic.py +0 -0
  20. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/central_scene.py +0 -0
  21. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/color_switch.py +0 -0
  22. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/energy_production.py +0 -0
  23. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/entry_control.py +0 -0
  24. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/humidity_control.py +0 -0
  25. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/lock.py +0 -0
  26. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/multilevel_sensor.py +0 -0
  27. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/multilevel_switch.py +0 -0
  28. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/notification.py +0 -0
  29. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/power_level.py +0 -0
  30. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/protection.py +0 -0
  31. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/scene_activation.py +0 -0
  32. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/sound_switch.py +0 -0
  33. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/thermostat.py +0 -0
  34. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/wake_up.py +0 -0
  35. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/const/command_class/window_covering.py +0 -0
  36. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/dump.py +0 -0
  37. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/event.py +0 -0
  38. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/exceptions.py +0 -0
  39. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/firmware.py +0 -0
  40. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/__init__.py +0 -0
  41. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/association.py +0 -0
  42. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/command_class.py +0 -0
  43. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/__init__.py +0 -0
  44. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/data_model.py +0 -0
  45. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/event_model.py +0 -0
  46. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/firmware.py +0 -0
  47. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/rebuild_routes.py +0 -0
  48. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/controller/statistics.py +0 -0
  49. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/device_config.py +0 -0
  50. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/driver.py +0 -0
  51. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/duration.py +0 -0
  52. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/endpoint.py +0 -0
  53. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/log_config.py +0 -0
  54. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/log_message.py +0 -0
  55. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/data_model.py +0 -0
  56. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/event_model.py +0 -0
  57. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/firmware.py +0 -0
  58. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/health_check.py +0 -0
  59. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/node/statistics.py +0 -0
  60. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/notification.py +0 -0
  61. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/statistics.py +0 -0
  62. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/utils.py +0 -0
  63. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/model/version.py +0 -0
  64. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/py.typed +0 -0
  65. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/__init__.py +0 -0
  66. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/command_class/__init__.py +0 -0
  67. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/command_class/energy_production.py +0 -0
  68. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/command_class/meter.py +0 -0
  69. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/command_class/multilevel_sensor.py +0 -0
  70. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/helpers.py +0 -0
  71. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/lock.py +0 -0
  72. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/multicast.py +0 -0
  73. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/util/node.py +0 -0
  74. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server/version.py +0 -0
  75. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/SOURCES.txt +0 -0
  76. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/dependency_links.txt +0 -0
  77. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/entry_points.txt +0 -0
  78. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/requires.txt +0 -0
  79. {zwave_js_server_python-0.56.0 → zwave_js_server_python-0.57.0}/zwave_js_server_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zwave-js-server-python
3
- Version: 0.56.0
3
+ Version: 0.57.0
4
4
  Summary: Python wrapper for zwave-js-server
5
5
  Author-email: Home Assistant Team <hello@home-assistant.io>
6
6
  License: Apache License
@@ -212,9 +212,9 @@ Classifier: Development Status :: 4 - Beta
212
212
  Classifier: Intended Audience :: Developers
213
213
  Classifier: Natural Language :: English
214
214
  Classifier: Programming Language :: Python :: 3
215
- Classifier: Programming Language :: Python :: 3.11
215
+ Classifier: Programming Language :: Python :: 3.12
216
216
  Classifier: Topic :: Home Automation
217
- Requires-Python: >=3.11
217
+ Requires-Python: >=3.12
218
218
  Description-Content-Type: text/markdown
219
219
  License-File: LICENSE
220
220
  Requires-Dist: aiohttp>3
@@ -7,7 +7,7 @@ name = "zwave-js-server-python"
7
7
  authors = [{ name = "Home Assistant Team", email = "hello@home-assistant.io" }]
8
8
  description = "Python wrapper for zwave-js-server"
9
9
  readme = "README.md"
10
- requires-python = ">=3.11"
10
+ requires-python = ">=3.12"
11
11
  license = { file = "LICENSE" }
12
12
  keywords = ["home", "automation", "zwave", "zwave-js"]
13
13
  classifiers = [
@@ -15,7 +15,7 @@ classifiers = [
15
15
  "Intended Audience :: Developers",
16
16
  "Natural Language :: English",
17
17
  "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
19
  "Topic :: Home Automation",
20
20
  ]
21
21
  dependencies = ["aiohttp>3", "pydantic>=1.10.0"]
@@ -8,12 +8,12 @@ import logging
8
8
  from typing import TypedDict
9
9
 
10
10
  PACKAGE_NAME = "zwave-js-server-python"
11
- __version__ = "0.56.0"
11
+ __version__ = "0.57.0"
12
12
 
13
13
  # minimal server schema version we can handle
14
- MIN_SERVER_SCHEMA_VERSION = 35
14
+ MIN_SERVER_SCHEMA_VERSION = 36
15
15
  # max server schema version we can handle (and our code is compatible with)
16
- MAX_SERVER_SCHEMA_VERSION = 35
16
+ MAX_SERVER_SCHEMA_VERSION = 36
17
17
 
18
18
  VALUE_UNKNOWN = "unknown"
19
19
 
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from enum import IntEnum
6
6
 
7
7
  VALUE_PROPERTY = "value"
8
+ RESET_PROPERTY = "reset"
8
9
 
9
10
  CC_SPECIFIC_SCALE = "scale"
10
11
  CC_SPECIFIC_METER_TYPE = "meterType"
@@ -76,13 +76,13 @@ class ProvisioningEntry:
76
76
  security_classes=[
77
77
  SecurityClass(sec_cls) for sec_cls in data["securityClasses"]
78
78
  ],
79
- additional_properties={
80
- k: v
81
- for k, v in data.items()
82
- if k
83
- not in {"dsk", "securityClasses", "requestedSecurityClasses", "status"}
84
- },
85
79
  )
80
+ if additional_properties := {
81
+ k: v
82
+ for k, v in data.items()
83
+ if k not in ("dsk", "securityClasses", "requestedSecurityClasses", "status")
84
+ }:
85
+ cls_instance.additional_properties = additional_properties
86
86
  if "requestedSecurityClasses" in data:
87
87
  cls_instance.requested_security_classes = [
88
88
  SecurityClass(sec_cls) for sec_cls in data["requestedSecurityClasses"]
@@ -148,6 +148,12 @@ class QRProvisioningInformation(ProvisioningEntry, QRProvisioningInformationMixi
148
148
  @classmethod
149
149
  def from_dict(cls, data: dict[str, Any]) -> QRProvisioningInformation:
150
150
  """Return QRProvisioningInformation from data dict."""
151
+ supported_protocols: list[Protocols] | None = None
152
+ if "supportedProtocols" in data:
153
+ supported_protocols = [
154
+ Protocols(supported_protocol)
155
+ for supported_protocol in data["supportedProtocols"]
156
+ ]
151
157
  cls_instance = cls(
152
158
  version=QRCodeVersion(data["version"]),
153
159
  security_classes=[
@@ -163,33 +169,31 @@ class QRProvisioningInformation(ProvisioningEntry, QRProvisioningInformationMixi
163
169
  application_version=data["applicationVersion"],
164
170
  max_inclusion_request_interval=data.get("maxInclusionRequestInterval"),
165
171
  uuid=data.get("uuid"),
166
- supported_protocols=[
167
- Protocols(supported_protocol)
168
- for supported_protocol in data.get("supportedProtocols", [])
169
- ],
170
- additional_properties={
171
- k: v
172
- for k, v in data.items()
173
- if k
174
- not in {
175
- "version",
176
- "securityClasses",
177
- "requestedSecurityClasses",
178
- "dsk",
179
- "genericDeviceClass",
180
- "specificDeviceClass",
181
- "installerIconType",
182
- "manufacturerId",
183
- "productType",
184
- "productId",
185
- "applicationVersion",
186
- "maxInclusionRequestInterval",
187
- "uuid",
188
- "supportedProtocols",
189
- "status",
190
- }
191
- },
172
+ supported_protocols=supported_protocols,
192
173
  )
174
+ if additional_properties := {
175
+ k: v
176
+ for k, v in data.items()
177
+ if k
178
+ not in (
179
+ "version",
180
+ "securityClasses",
181
+ "requestedSecurityClasses",
182
+ "dsk",
183
+ "genericDeviceClass",
184
+ "specificDeviceClass",
185
+ "installerIconType",
186
+ "manufacturerId",
187
+ "productType",
188
+ "productId",
189
+ "applicationVersion",
190
+ "maxInclusionRequestInterval",
191
+ "uuid",
192
+ "supportedProtocols",
193
+ "status",
194
+ )
195
+ }:
196
+ cls_instance.additional_properties = additional_properties
193
197
  if "requestedSecurityClasses" in data:
194
198
  cls_instance.requested_security_classes = [
195
199
  SecurityClass(sec_cls) for sec_cls in data["requestedSecurityClasses"]
@@ -23,8 +23,6 @@ class DeviceClassDataType(TypedDict):
23
23
  basic: DeviceClassItemDataType
24
24
  generic: DeviceClassItemDataType
25
25
  specific: DeviceClassItemDataType
26
- mandatorySupportedCCs: list[int]
27
- mandatoryControlledCCs: list[int]
28
26
 
29
27
 
30
28
  @dataclass
@@ -43,8 +41,6 @@ class DeviceClass:
43
41
  self._basic = DeviceClassItem(**data["basic"])
44
42
  self._generic = DeviceClassItem(**data["generic"])
45
43
  self._specific = DeviceClassItem(**data["specific"])
46
- self._mandatory_supported_ccs: list[int] = data["mandatorySupportedCCs"]
47
- self._mandatory_controlled_ccs: list[int] = data["mandatoryControlledCCs"]
48
44
 
49
45
  @property
50
46
  def basic(self) -> DeviceClassItem:
@@ -60,13 +56,3 @@ class DeviceClass:
60
56
  def specific(self) -> DeviceClassItem:
61
57
  """Return specific DeviceClass."""
62
58
  return self._specific
63
-
64
- @property
65
- def mandatory_supported_ccs(self) -> list[int]:
66
- """Return list of mandatory Supported CC id's."""
67
- return self._mandatory_supported_ccs
68
-
69
- @property
70
- def mandatory_controlled_ccs(self) -> list[int]:
71
- """Return list of mandatory Controlled CC id's."""
72
- return self._mandatory_controlled_ccs
@@ -19,12 +19,7 @@ from ...const import (
19
19
  SecurityClass,
20
20
  )
21
21
  from ...event import Event, EventBase
22
- from ...exceptions import (
23
- FailedCommand,
24
- NotFoundError,
25
- UnparseableValue,
26
- UnwriteableValue,
27
- )
22
+ from ...exceptions import NotFoundError, UnparseableValue, UnwriteableValue
28
23
  from ..command_class import CommandClassInfo
29
24
  from ..device_class import DeviceClass
30
25
  from ..device_config import DeviceConfig
@@ -50,7 +45,6 @@ from ..value import (
50
45
  ValueMetadata,
51
46
  ValueNotification,
52
47
  _get_value_id_str_from_dict,
53
- _init_value,
54
48
  )
55
49
  from .data_model import NodeDataType
56
50
  from .event_model import NODE_EVENT_MODEL_MAP
@@ -139,6 +133,12 @@ class Node(EventBase):
139
133
  self.client.driver == other.client.driver and self.node_id == other.node_id
140
134
  )
141
135
 
136
+ def _init_value(self, val: ValueDataType) -> Value | ConfigurationValue:
137
+ """Initialize a Value object from ValueDataType."""
138
+ if val["commandClass"] == CommandClass.CONFIGURATION:
139
+ return ConfigurationValue(self, val)
140
+ return Value(self, val)
141
+
142
142
  @property
143
143
  def node_id(self) -> int:
144
144
  """Return node ID property."""
@@ -429,7 +429,7 @@ class Node(EventBase):
429
429
  if value_id in self.values:
430
430
  self.values[value_id].update(val)
431
431
  else:
432
- self.values[value_id] = _init_value(self, val)
432
+ self.values[value_id] = self._init_value(val)
433
433
  except UnparseableValue:
434
434
  # If we can't parse the value, don't store it
435
435
  pass
@@ -448,8 +448,9 @@ class Node(EventBase):
448
448
  )
449
449
  if last_seen := data.get("lastSeen"):
450
450
  self._last_seen = datetime.fromisoformat(last_seen)
451
- if not self._statistics.last_seen:
451
+ if not self._statistics.last_seen and self.last_seen:
452
452
  self._statistics.last_seen = self.last_seen
453
+ self._statistics.data["lastSeen"] = self.last_seen.isoformat()
453
454
 
454
455
  self._update_values(self.data.pop("values"))
455
456
  self._update_endpoints(self.data.pop("endpoints"))
@@ -593,12 +594,9 @@ class Node(EventBase):
593
594
  data = await self.async_send_command(
594
595
  "get_defined_value_ids", wait_for_result=True
595
596
  )
596
-
597
- if data is None:
598
- # We should never reach this code
599
- raise FailedCommand("Command failed", "failed_command")
597
+ assert data
600
598
  return [
601
- _init_value(self, cast(ValueDataType, value_id))
599
+ self._init_value(cast(ValueDataType, value_id))
602
600
  for value_id in data["valueIds"]
603
601
  ]
604
602
 
@@ -1061,7 +1059,7 @@ class Node(EventBase):
1061
1059
  value_id = _get_value_id_str_from_dict(self, evt_val_data)
1062
1060
  value = self.values.get(value_id)
1063
1061
  if value is None:
1064
- value = _init_value(self, evt_val_data)
1062
+ value = self._init_value(evt_val_data)
1065
1063
  self.values[value.value_id] = event.data["value"] = value
1066
1064
  else:
1067
1065
  value.receive_event(event)
@@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Any, TypedDict
8
8
 
9
9
  from ..const import (
10
10
  VALUE_UNKNOWN,
11
- CommandClass,
12
11
  CommandStatus,
13
12
  ConfigurationValueType,
14
13
  SetValueStatus,
@@ -75,13 +74,6 @@ class ValueDataType(TypedDict, total=False):
75
74
  ccVersion: int # required
76
75
 
77
76
 
78
- def _init_value(node: Node, val: ValueDataType) -> Value | ConfigurationValue:
79
- """Initialize a Value object from ValueDataType."""
80
- if val["commandClass"] == CommandClass.CONFIGURATION:
81
- return ConfigurationValue(node, val)
82
- return Value(node, val)
83
-
84
-
85
77
  def _get_value_id_str_from_dict(node: Node, val: ValueDataType) -> str:
86
78
  """Return string ID of value from ValueDataType dict."""
87
79
  return get_value_id_str(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zwave-js-server-python
3
- Version: 0.56.0
3
+ Version: 0.57.0
4
4
  Summary: Python wrapper for zwave-js-server
5
5
  Author-email: Home Assistant Team <hello@home-assistant.io>
6
6
  License: Apache License
@@ -212,9 +212,9 @@ Classifier: Development Status :: 4 - Beta
212
212
  Classifier: Intended Audience :: Developers
213
213
  Classifier: Natural Language :: English
214
214
  Classifier: Programming Language :: Python :: 3
215
- Classifier: Programming Language :: Python :: 3.11
215
+ Classifier: Programming Language :: Python :: 3.12
216
216
  Classifier: Topic :: Home Automation
217
- Requires-Python: >=3.11
217
+ Requires-Python: >=3.12
218
218
  Description-Content-Type: text/markdown
219
219
  License-File: LICENSE
220
220
  Requires-Dist: aiohttp>3