violet-poolController-api 0.0.32__tar.gz → 0.0.33__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {violet_poolcontroller_api-0.0.32/violet_poolController_api.egg-info → violet_poolcontroller_api-0.0.33}/PKG-INFO +1 -1
  2. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/pyproject.toml +1 -1
  3. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33/violet_poolController_api.egg-info}/PKG-INFO +1 -1
  4. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/api.py +50 -4
  5. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/LICENSE +0 -0
  6. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/README.md +0 -0
  7. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/setup.cfg +0 -0
  8. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_api.py +0 -0
  9. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_api_smoke.py +0 -0
  10. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_circuit_breaker.py +0 -0
  11. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_mock_server.py +0 -0
  12. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_parsers.py +0 -0
  13. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/tests/test_readings.py +0 -0
  14. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolController_api.egg-info/SOURCES.txt +0 -0
  15. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolController_api.egg-info/dependency_links.txt +0 -0
  16. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolController_api.egg-info/requires.txt +0 -0
  17. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolController_api.egg-info/top_level.txt +0 -0
  18. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/__init__.py +0 -0
  19. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/circuit_breaker.py +0 -0
  20. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/const_api.py +0 -0
  21. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/const_devices.py +0 -0
  22. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/parsers.py +0 -0
  23. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/readings.py +0 -0
  24. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/utils_rate_limiter.py +0 -0
  25. {violet_poolcontroller_api-0.0.32 → violet_poolcontroller_api-0.0.33}/violet_poolcontroller_api/utils_sanitizer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.32
3
+ Version: 0.0.33
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
5
  Author-email: "Basti (Xerolux)" <git@xerolux.de>
6
6
  License: AGPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "violet-poolController-api"
7
- version = "0.0.32"
7
+ version = "0.0.33"
8
8
  authors = [
9
9
  { name="Basti (Xerolux)", email="git@xerolux.de" },
10
10
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violet-poolController-api
3
- Version: 0.0.32
3
+ Version: 0.0.33
4
4
  Summary: Asynchronous Python client for the Violet Pool Controller.
5
5
  Author-email: "Basti (Xerolux)" <git@xerolux.de>
6
6
  License: AGPL-3.0-or-later
@@ -19,6 +19,7 @@
19
19
  from __future__ import annotations
20
20
 
21
21
  import asyncio
22
+ import ipaddress
22
23
  import json
23
24
  import logging
24
25
  import math
@@ -331,18 +332,63 @@ class VioletPoolAPI:
331
332
  parsed = urlparse(host)
332
333
  host = parsed.netloc
333
334
 
334
- # Validate hostname format (allowing optional port)
335
- if not re.match(r"^[a-zA-Z0-9.-]+(?::[0-9]{1,5})?$", host):
335
+ try:
336
+ literal_ip = ipaddress.ip_address(host)
337
+ except ValueError:
338
+ literal_ip = None
339
+ if literal_ip is not None and literal_ip.version == 6:
340
+ protocol = "https" if use_ssl else "http"
341
+ return urlunparse((protocol, f"[{host}]", "", "", "", ""))
342
+
343
+ try:
344
+ parsed_host = urlparse(f"//{host}")
345
+ except ValueError as err:
346
+ msg = f"Invalid hostname format: {host}"
347
+ raise ValueError(msg) from err
348
+
349
+ if parsed_host.username or parsed_host.password:
350
+ msg = f"Invalid hostname format: {host}"
351
+ raise ValueError(msg)
352
+ if parsed_host.path or parsed_host.query or parsed_host.fragment:
353
+ msg = f"Invalid hostname format: {host}"
354
+ raise ValueError(msg)
355
+
356
+ hostname = parsed_host.hostname
357
+ if not hostname:
358
+ msg = f"Invalid hostname format: {host}"
359
+ raise ValueError(msg)
360
+
361
+ try:
362
+ port = parsed_host.port
363
+ except ValueError as err:
364
+ msg = f"Invalid port in hostname: {host}"
365
+ raise ValueError(msg) from err
366
+
367
+ if port is not None and not 1 <= port <= 65535:
368
+ msg = f"Invalid port in hostname: {host}"
369
+ raise ValueError(msg)
370
+
371
+ try:
372
+ ipaddress.ip_address(hostname)
373
+ is_ip_literal = True
374
+ except ValueError:
375
+ is_ip_literal = False
376
+
377
+ if not is_ip_literal and not re.match(r"^[a-zA-Z0-9.-]+$", hostname):
336
378
  msg = f"Invalid hostname format: {host}"
337
379
  raise ValueError(msg)
338
380
 
339
381
  # Additional validation
340
- if len(host) > _MAX_HOSTNAME_LENGTH or ".." in host or "//" in host:
382
+ if len(hostname) > _MAX_HOSTNAME_LENGTH or ".." in hostname or "//" in host:
341
383
  msg = f"Invalid hostname: {host}"
342
384
  raise ValueError(msg)
343
385
 
386
+ netloc = f"[{hostname}]" if ":" in hostname else hostname
387
+ if port is not None:
388
+ netloc = f"{netloc}:{port}"
389
+
344
390
  protocol = "https" if use_ssl else "http"
345
- return urlunparse((protocol, host, "", "", "", ""))
391
+ return urlunparse((protocol, netloc, "", "", "", ""))
346
392
 
347
393
  def _build_url(self, endpoint: str) -> str:
348
394
  """Construct the full URL for a given endpoint.