smartbox 2.1.3__tar.gz → 2.2.1__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 (78) hide show
  1. smartbox-2.2.1/.github/ISSUE_TEMPLATE/new-reseller.md +18 -0
  2. {smartbox-2.1.3 → smartbox-2.2.1}/.github/workflows/publish-to-pypi.yml +1 -1
  3. {smartbox-2.1.3 → smartbox-2.2.1}/.github/workflows/python-package.yml +5 -5
  4. {smartbox-2.1.3 → smartbox-2.2.1}/PKG-INFO +8 -8
  5. {smartbox-2.1.3 → smartbox-2.2.1}/README.md +6 -6
  6. {smartbox-2.1.3 → smartbox-2.2.1}/pyproject.toml +2 -2
  7. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/__init__.py +5 -5
  8. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/cmd.py +5 -5
  9. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/error.py +2 -2
  10. smartbox-2.1.3/src/smartbox/resailer.py → smartbox-2.2.1/src/smartbox/reseller.py +49 -34
  11. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/session.py +13 -13
  12. {smartbox-2.1.3 → smartbox-2.2.1}/tests/conftest.py +8 -8
  13. {smartbox-2.1.3 → smartbox-2.2.1}/tests/test_cmd.py +4 -12
  14. smartbox-2.2.1/tests/test_reseller.py +58 -0
  15. {smartbox-2.1.3 → smartbox-2.2.1}/tests/test_session.py +11 -11
  16. {smartbox-2.1.3 → smartbox-2.2.1}/uv.lock +295 -295
  17. smartbox-2.1.3/.github/ISSUE_TEMPLATE/new-resailer.md +0 -18
  18. smartbox-2.1.3/tests/test_resailer.py +0 -58
  19. {smartbox-2.1.3 → smartbox-2.2.1}/.github/FUNDING.yml +0 -0
  20. {smartbox-2.1.3 → smartbox-2.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {smartbox-2.1.3 → smartbox-2.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {smartbox-2.1.3 → smartbox-2.2.1}/.github/dependabot.yml +0 -0
  23. {smartbox-2.1.3 → smartbox-2.2.1}/.gitignore +0 -0
  24. {smartbox-2.1.3 → smartbox-2.2.1}/.pre-commit-config.yaml +0 -0
  25. {smartbox-2.1.3 → smartbox-2.2.1}/.vscode/settings.json +0 -0
  26. {smartbox-2.1.3 → smartbox-2.2.1}/CHANGELOG.md +0 -0
  27. {smartbox-2.1.3 → smartbox-2.2.1}/CODEOWNERS +0 -0
  28. {smartbox-2.1.3 → smartbox-2.2.1}/LICENSE +0 -0
  29. {smartbox-2.1.3 → smartbox-2.2.1}/api-notes.md +0 -0
  30. {smartbox-2.1.3 → smartbox-2.2.1}/codecov.yml +0 -0
  31. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/models.py +0 -0
  32. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/py.typed +0 -0
  33. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/socket.py +0 -0
  34. {smartbox-2.1.3 → smartbox-2.2.1}/src/smartbox/update_manager.py +0 -0
  35. {smartbox-2.1.3 → smartbox-2.2.1}/tests/__init__.py +0 -0
  36. {smartbox-2.1.3 → smartbox-2.2.1}/tests/common.py +0 -0
  37. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/acm/1/samples.json +0 -0
  38. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/acm/1/setup.json +0 -0
  39. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/acm/1/status.json +0 -0
  40. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/connected.json +0 -0
  41. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/htr/0/samples.json +0 -0
  42. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/htr/0/setup.json +0 -0
  43. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/htr/0/status.json +0 -0
  44. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/htr_system/power_limit.json +0 -0
  45. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/mgr/away_status.json +0 -0
  46. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/mgr/nodes.json +0 -0
  47. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/pmo/3/samples.json +0 -0
  48. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/pmo/3/setup.json +0 -0
  49. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device1/pmo/3/status.json +0 -0
  50. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/connected.json +0 -0
  51. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/0/samples.json +0 -0
  52. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/0/setup.json +0 -0
  53. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/0/status.json +0 -0
  54. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/1/samples.json +0 -0
  55. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/1/setup.json +0 -0
  56. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/1/status.json +0 -0
  57. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/2/samples.json +0 -0
  58. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/2/setup.json +0 -0
  59. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/2/status.json +0 -0
  60. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/3/samples.json +0 -0
  61. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/3/setup.json +0 -0
  62. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/3/status.json +0 -0
  63. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/4/samples.json +0 -0
  64. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/4/setup.json +0 -0
  65. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/4/status.json +0 -0
  66. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/5/samples.json +0 -0
  67. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/5/setup.json +0 -0
  68. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_mod/5/status.json +0 -0
  69. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/htr_system/power_limit.json +0 -0
  70. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/mgr/away_status.json +0 -0
  71. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs/device2/mgr/nodes.json +0 -0
  72. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/devs.json +0 -0
  73. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/grouped_devs.json +0 -0
  74. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/groups/test_home/guest_users.json +0 -0
  75. {smartbox-2.1.3 → smartbox-2.2.1}/tests/fixtures/test_path.json +0 -0
  76. {smartbox-2.1.3 → smartbox-2.2.1}/tests/test_models.py +0 -0
  77. {smartbox-2.1.3 → smartbox-2.2.1}/tests/test_update_manager.py +0 -0
  78. {smartbox-2.1.3 → smartbox-2.2.1}/tox.ini +0 -0
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: New reseller
3
+ about: Add a new available reseller.
4
+ title: "[RESELLER] New available reseller"
5
+ labels: reseller
6
+ assignees: Delmael
7
+
8
+ ---
9
+
10
+ > [!NOTE]
11
+ > If you do not see your current reseller in the available list, you can ask to add it. We cannot ensure your device go through this platform
12
+
13
+ * [ ] I have check with the `smartbox resellers` command that my reseller is not already present
14
+ * **Website url (mandatory)** :
15
+ * Name of the resellers:
16
+ * API url :
17
+ * Basic auth headers :
18
+ * Serial id:
@@ -22,7 +22,7 @@ jobs:
22
22
  with:
23
23
  python-version: ${{ env.PYTHON_VERSION }}
24
24
  - name: Set up Python ${{ env.PYTHON_VERSION }}
25
- uses: actions/setup-python@v5.4.0
25
+ uses: actions/setup-python@v5.6.0
26
26
  with:
27
27
  python-version: ${{ env.PYTHON_VERSION }}
28
28
  - name: Build a binary wheel and a source tarball
@@ -25,7 +25,7 @@ jobs:
25
25
  with:
26
26
  python-version: ${{ matrix.python-version }}
27
27
  - name: Set up Python ${{ matrix.python-version }}
28
- uses: actions/setup-python@v5.4.0
28
+ uses: actions/setup-python@v5.6.0
29
29
  with:
30
30
  python-version: ${{ matrix.python-version }}
31
31
  - name: Install the project
@@ -47,7 +47,7 @@ jobs:
47
47
  with:
48
48
  python-version: ${{ matrix.python-version }}
49
49
  - name: Set up Python ${{ matrix.python-version }}
50
- uses: actions/setup-python@v5.4.0
50
+ uses: actions/setup-python@v5.6.0
51
51
  with:
52
52
  python-version: ${{ matrix.python-version }}
53
53
  - name: Install the project
@@ -70,13 +70,13 @@ jobs:
70
70
  python-version: ${{ matrix.python-version }}
71
71
 
72
72
  - name: Set up Python ${{ matrix.python-version }}
73
- uses: actions/setup-python@v5.4.0
73
+ uses: actions/setup-python@v5.6.0
74
74
  with:
75
75
  python-version: ${{ matrix.python-version }}
76
76
  - name: Install the project
77
77
  run: |
78
78
  uv sync --all-extras --dev
79
- tox -e py
79
+ uv run tox -e py
80
80
  - name: "Upload coverage to Codecov"
81
81
  uses: codecov/codecov-action@v5
82
82
  with:
@@ -86,4 +86,4 @@ jobs:
86
86
  if: ${{ !cancelled() }}
87
87
  uses: codecov/test-results-action@v1
88
88
  with:
89
- token: ${{ secrets.CODECOV_TOKEN }}
89
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smartbox
3
- Version: 2.1.3
3
+ Version: 2.2.1
4
4
  Summary: Python API to control heating 'smart boxes'
5
5
  Project-URL: Homepage, https://github.com/ajtudela/smartbox
6
6
  Project-URL: Issues, https://github.com/ajtudela/smartbox/issues
@@ -15,7 +15,7 @@ Requires-Dist: aiohttp>=3.11.11
15
15
  Requires-Dist: asyncclick>=8.1.8
16
16
  Requires-Dist: jq>=1.8.0
17
17
  Requires-Dist: pydantic>=2.10.4
18
- Requires-Dist: python-socketio==5.12.1
18
+ Requires-Dist: python-socketio==5.13.0
19
19
  Requires-Dist: requests>=2.32.3
20
20
  Requires-Dist: websocket-client>=1.8.0
21
21
  Description-Content-Type: text/markdown
@@ -53,7 +53,7 @@ A few common options are required for all commands:
53
53
  Verbose logging can be enabled with the `-v`/`--verbose` flag.
54
54
 
55
55
  ### Optional options
56
- These options are useful if your resailer is not configured.
56
+ These options are useful if your reseller is not configured.
57
57
 
58
58
  * `-b`/`--base-auth-creds`: An HTTP Basic Auth credential used to do initial
59
59
  authentication with the server. Use the base64 encoded string directly. See
@@ -61,7 +61,7 @@ These options are useful if your resailer is not configured.
61
61
  * `-a`/`--api-name`: The API name for your heater vendor. This is visible in
62
62
  the 'API Host' entry in the 'Version' menu item in the mobile app/web app. If
63
63
  the host name is of the form `api-foo.xxxx` or `api.xxxx` use the values
64
- `api-foo` or `api` respectively. The resailer has to be declared in the package.
64
+ `api-foo` or `api` respectively. The reseller has to be declared in the package.
65
65
  * `-r`/`--x-referer`: The referer of your request.
66
66
  * `-i`/`--x-serial-id`: The serial-id of your request.
67
67
 
@@ -132,11 +132,11 @@ The `health-check` command can be used to know if the API is alived
132
132
 
133
133
  smartbox <auth options...> health-check
134
134
 
135
- ### List available resailers
136
- The `resailers` command can be used to know which resailershas an automatic configuration.
137
- If your resailer is not present you can raise an issue in github, or use the optional options.
135
+ ### List available resellers
136
+ The `resellers` command can be used to know which resellers has an automatic configuration.
137
+ If your reseller is not present you can raise an issue in github, or use the optional options.
138
138
 
139
- smartbox <auth options...> resailers
139
+ smartbox <auth options...> resellers
140
140
 
141
141
 
142
142
  See [api-notes.md](./api-notes.md) for notes on REST and socket.io endpoints.
@@ -31,7 +31,7 @@ A few common options are required for all commands:
31
31
  Verbose logging can be enabled with the `-v`/`--verbose` flag.
32
32
 
33
33
  ### Optional options
34
- These options are useful if your resailer is not configured.
34
+ These options are useful if your reseller is not configured.
35
35
 
36
36
  * `-b`/`--base-auth-creds`: An HTTP Basic Auth credential used to do initial
37
37
  authentication with the server. Use the base64 encoded string directly. See
@@ -39,7 +39,7 @@ These options are useful if your resailer is not configured.
39
39
  * `-a`/`--api-name`: The API name for your heater vendor. This is visible in
40
40
  the 'API Host' entry in the 'Version' menu item in the mobile app/web app. If
41
41
  the host name is of the form `api-foo.xxxx` or `api.xxxx` use the values
42
- `api-foo` or `api` respectively. The resailer has to be declared in the package.
42
+ `api-foo` or `api` respectively. The reseller has to be declared in the package.
43
43
  * `-r`/`--x-referer`: The referer of your request.
44
44
  * `-i`/`--x-serial-id`: The serial-id of your request.
45
45
 
@@ -110,11 +110,11 @@ The `health-check` command can be used to know if the API is alived
110
110
 
111
111
  smartbox <auth options...> health-check
112
112
 
113
- ### List available resailers
114
- The `resailers` command can be used to know which resailershas an automatic configuration.
115
- If your resailer is not present you can raise an issue in github, or use the optional options.
113
+ ### List available resellers
114
+ The `resellers` command can be used to know which resellers has an automatic configuration.
115
+ If your reseller is not present you can raise an issue in github, or use the optional options.
116
116
 
117
- smartbox <auth options...> resailers
117
+ smartbox <auth options...> resellers
118
118
 
119
119
 
120
120
  See [api-notes.md](./api-notes.md) for notes on REST and socket.io endpoints.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "smartbox"
3
- version = "2.1.3"
3
+ version = "2.2.1"
4
4
  requires-python = ">=3.13"
5
5
  authors = [
6
6
  { name="Alberto Tudela", email="ajtudela@gmail.com" },
@@ -22,7 +22,7 @@ dependencies = [
22
22
  "asyncclick>=8.1.8",
23
23
  "jq>=1.8.0",
24
24
  "pydantic>=2.10.4",
25
- "python-socketio==5.12.1",
25
+ "python-socketio==5.13.0",
26
26
  "requests>=2.32.3",
27
27
  "websocket-client>=1.8.0",
28
28
  ]
@@ -5,7 +5,7 @@ import importlib.metadata
5
5
  from .error import (
6
6
  APIUnavailableError,
7
7
  InvalidAuthError,
8
- ResailerNotExistError,
8
+ ResellerNotExistError,
9
9
  SmartboxError,
10
10
  )
11
11
  from .models import (
@@ -21,7 +21,7 @@ from .models import (
21
21
  NodeStatus,
22
22
  SmartboxNodeType,
23
23
  )
24
- from .resailer import AvailableResailers, SmartboxResailer
24
+ from .reseller import AvailableResellers, SmartboxReseller
25
25
  from .session import AsyncSmartboxSession, Session
26
26
  from .socket import SocketSession
27
27
  from .update_manager import UpdateManager
@@ -33,7 +33,7 @@ __all__ = [
33
33
  "APIUnavailableError",
34
34
  "AcmNodeStatus",
35
35
  "AsyncSmartboxSession",
36
- "AvailableResailers",
36
+ "AvailableResellers",
37
37
  "DefaultNodeStatus",
38
38
  "GuestUser",
39
39
  "Guests",
@@ -44,11 +44,11 @@ __all__ = [
44
44
  "NodeFactoryOptions",
45
45
  "NodeSetup",
46
46
  "NodeStatus",
47
- "ResailerNotExistError",
47
+ "ResellerNotExistError",
48
48
  "Session",
49
49
  "SmartboxError",
50
50
  "SmartboxNodeType",
51
- "SmartboxResailer",
51
+ "SmartboxReseller",
52
52
  "SocketSession",
53
53
  "UpdateManager",
54
54
  ]
@@ -7,7 +7,7 @@ from typing import Any
7
7
  from aiohttp import ClientSession
8
8
  import asyncclick as click
9
9
 
10
- from smartbox.resailer import AvailableResailers
10
+ from smartbox.reseller import AvailableResellers
11
11
  from smartbox.session import AsyncSmartboxSession
12
12
  from smartbox.socket import SocketSession
13
13
 
@@ -403,10 +403,10 @@ async def api_version(ctx) -> None:
403
403
  _pretty_print(version)
404
404
 
405
405
 
406
- @smartbox.command(help="Get the availables resailers.")
407
- def resailers() -> None:
408
- """Get the availables resailers."""
409
- for item in AvailableResailers.resailers.items():
406
+ @smartbox.command(help="Get the availables resellers.")
407
+ def resellers() -> None:
408
+ """Get the availables resellers."""
409
+ for item in AvailableResellers.resellers.items():
410
410
  print(item)
411
411
 
412
412
 
@@ -15,5 +15,5 @@ class APIUnavailableError(aiohttp.ClientConnectionError):
15
15
  """API is unavailable."""
16
16
 
17
17
 
18
- class ResailerNotExistError(Exception):
19
- """Resailer is not known."""
18
+ class ResellerNotExistError(Exception):
19
+ """Reseller is not known."""
@@ -1,18 +1,18 @@
1
- """Resailer of Smartbox."""
1
+ """Reseller of Smartbox."""
2
2
 
3
3
  import logging
4
4
  from typing import ClassVar
5
5
 
6
6
  from pydantic import BaseModel, ValidationError
7
7
 
8
- from smartbox.error import ResailerNotExistError
8
+ from smartbox.error import ResellerNotExistError
9
9
 
10
10
  SMARTBOX_GENERIC_BASIC_AUTH = "NTRiY2NiZmI0MWE5YTUxMTNmMDQ4OGQwOnZkaXZkaQ=="
11
11
  _LOGGER = logging.getLogger(__name__)
12
12
 
13
13
 
14
- class SmartboxResailer(BaseModel):
15
- """Model of Smartbox Resailer config."""
14
+ class SmartboxReseller(BaseModel):
15
+ """Model of Smartbox Reseller config."""
16
16
 
17
17
  name: str = "Smartbox"
18
18
  web_url: str = ""
@@ -21,53 +21,65 @@ class SmartboxResailer(BaseModel):
21
21
  serial_id: int = 0
22
22
 
23
23
 
24
- class AvailableResailers:
25
- """Resailers that have been verified."""
24
+ class AvailableResellers:
25
+ """Resellers that have been verified."""
26
26
 
27
- resailers: ClassVar[dict[str, SmartboxResailer]] = {
28
- "api": SmartboxResailer(
27
+ resellers: ClassVar[dict[str, SmartboxReseller]] = {
28
+ "api": SmartboxReseller(
29
29
  name="Helki",
30
30
  web_url="https://app.helki.com/",
31
31
  api_url="api",
32
32
  serial_id=1,
33
33
  ),
34
- "api-ehc": SmartboxResailer(
34
+ "api-ehc": SmartboxReseller(
35
35
  name="Electric Heating Company",
36
36
  web_url="https://control.electric-heatingcompany.co.uk/",
37
37
  api_url="api-ehc",
38
38
  serial_id=3,
39
39
  ),
40
- "api-climastar": SmartboxResailer(
40
+ "api-climastar": SmartboxReseller(
41
41
  name="Climastar",
42
42
  web_url="https://avantwifi.climastar.es/",
43
43
  api_url="api-climastar",
44
44
  serial_id=5,
45
45
  ),
46
- "api-elnur": SmartboxResailer(
46
+ "api-elnur": SmartboxReseller(
47
47
  name="Elnur",
48
48
  web_url="https://remotecontrol.elnur.es/",
49
49
  api_url="api-elnur",
50
50
  serial_id=7,
51
51
  ),
52
- "api-hjm": SmartboxResailer(
52
+ "api-hjm": SmartboxReseller(
53
53
  name="HJM",
54
54
  web_url="https://api.calorhjm.com/",
55
55
  api_url="api-hjm",
56
56
  serial_id=10,
57
57
  ),
58
- "api-haverland": SmartboxResailer(
58
+ "api-evconfort": SmartboxReseller(
59
+ name="Electrorad",
60
+ web_url="https://app.controls-electrorad.co.uk/",
61
+ api_url="api-evconfort",
62
+ serial_id=12,
63
+ ),
64
+ "api-haverland": SmartboxReseller(
59
65
  name="Haverland",
60
66
  web_url="https://i2control.haverland.com/",
61
67
  api_url="api-haverland",
62
68
  basic_auth="NTU2ZDc0MWI3OGUzYmU5YjU2NjA3NTQ4OnZkaXZkaQ==",
63
69
  serial_id=14,
64
70
  ),
65
- "api-lhz": SmartboxResailer(
71
+ "api-technoterm": SmartboxReseller(
66
72
  name="Technotherm",
67
73
  web_url="https://ttiapp.technotherm.com/",
68
74
  api_url="api-lhz",
69
75
  serial_id=16,
70
76
  ),
77
+ "api-smartcontrol": SmartboxReseller(
78
+ name="SmartControl",
79
+ web_url="https://app.smart-control.eu/",
80
+ api_url="api-lhz",
81
+ serial_id=17,
82
+ ),
71
83
  }
72
84
 
73
85
  def __init__(
@@ -78,7 +90,7 @@ class AvailableResailers:
78
90
  serial_id: int | None = None,
79
91
  name: str = "Smartbox",
80
92
  ) -> None:
81
- """Check if resailer is already available or try to create one."""
93
+ """Check if reseller is already available or try to create one."""
82
94
  self._api_url = api_url
83
95
  self._basic_auth = basic_auth
84
96
  self._web_url = web_url
@@ -86,27 +98,30 @@ class AvailableResailers:
86
98
  self._name = name
87
99
 
88
100
  @property
89
- def resailer(self) -> SmartboxResailer:
90
- """Get the resailer."""
91
- resailer = self.resailers.get(self._api_url, None)
92
- if resailer is None:
101
+ def reseller(self) -> SmartboxReseller:
102
+ """Get the reseller."""
103
+ reseller = next(
104
+ (r for r in self.resellers.values() if r.api_url == self._api_url),
105
+ None,
106
+ )
107
+ if reseller is None:
93
108
  if (
94
109
  self._basic_auth is None
95
110
  or self._web_url is None
96
111
  or self._serial_id is None
97
112
  ):
98
- msg = "This Resailer is not yet available or some arguments are missing."
99
- raise ResailerNotExistError(msg)
113
+ msg = f"This reseller {self._api_url} is not yet available or some arguments are missing."
114
+ raise ResellerNotExistError(msg)
100
115
  try:
101
116
  _LOGGER.debug(
102
- "Creating a new resailer api_url (%s), name=%s, web_url %s, basic_auth=%s, serial_id=%s",
117
+ "Creating a new reseller api_url (%s), name=%s, web_url %s, basic_auth=%s, serial_id=%s",
103
118
  self._api_url,
104
119
  self._name,
105
120
  self._web_url,
106
121
  self._basic_auth,
107
122
  self._serial_id,
108
123
  )
109
- resailer = SmartboxResailer(
124
+ reseller = SmartboxReseller(
110
125
  api_url=self._api_url,
111
126
  basic_auth=self._basic_auth,
112
127
  web_url=self._web_url,
@@ -114,26 +129,26 @@ class AvailableResailers:
114
129
  name=self._name,
115
130
  )
116
131
  except ValidationError as e:
117
- raise ResailerNotExistError from e
132
+ raise ResellerNotExistError from e
118
133
  _LOGGER.debug(
119
- "Resailer api_url (%s), name=%s, web_url %s",
120
- resailer.api_url,
121
- resailer.name,
122
- resailer.web_url,
134
+ "Reseller api_url (%s), name=%s, web_url %s",
135
+ reseller.api_url,
136
+ reseller.name,
137
+ reseller.web_url,
123
138
  )
124
- return resailer
139
+ return reseller
125
140
 
126
141
  @property
127
142
  def api_url(self) -> str:
128
143
  """Get the api sub domain url."""
129
- return self.resailer.api_url
144
+ return self.reseller.api_url
130
145
 
131
146
  @property
132
147
  def name(self) -> str:
133
- """Get the name of resailer."""
134
- return self.resailer.name
148
+ """Get the name of reseller."""
149
+ return self.reseller.name
135
150
 
136
151
  @property
137
152
  def web_url(self) -> str:
138
- """Get the public websit of the resailer."""
139
- return self.resailer.web_url
153
+ """Get the public websit of the reseller."""
154
+ return self.reseller.web_url
@@ -31,7 +31,7 @@ from smartbox.models import (
31
31
  SmartboxNodeType,
32
32
  Token,
33
33
  )
34
- from smartbox.resailer import AvailableResailers, SmartboxResailer
34
+ from smartbox.reseller import AvailableResellers, SmartboxReseller
35
35
 
36
36
  _DEFAULT_RETRY_ATTEMPTS = 5
37
37
  _DEFAULT_BACKOFF_FACTOR = 0.1
@@ -59,13 +59,13 @@ class AsyncSession:
59
59
  x_referer: str | None = None,
60
60
  ) -> None:
61
61
  """Init the session."""
62
- self._resailer = AvailableResailers(
62
+ self._reseller = AvailableResellers(
63
63
  api_url=api_name,
64
64
  basic_auth=basic_auth_credentials,
65
65
  serial_id=x_serial_id,
66
66
  web_url=x_referer,
67
- ).resailer
68
- self._api_host: str = f"https://{self.resailer.api_url}.helki.com"
67
+ ).reseller
68
+ self._api_host: str = f"https://{self.reseller.api_url}.helki.com"
69
69
  self._basic_auth_credentials: str | None = basic_auth_credentials
70
70
  self._retry_attempts: int = retry_attempts
71
71
  self._backoff_factor: float = backoff_factor
@@ -78,20 +78,20 @@ class AsyncSession:
78
78
  "Authorization": f"Bearer {self._access_token}",
79
79
  "Content-Type": "application/json",
80
80
  }
81
- if self.resailer.serial_id:
82
- self._headers.update({"x-serialid": str(self.resailer.serial_id)})
83
- if self.resailer.web_url:
84
- self._headers.update({"x-referer": self.resailer.web_url})
81
+ if self.reseller.serial_id:
82
+ self._headers.update({"x-serialid": str(self.reseller.serial_id)})
83
+ if self.reseller.web_url:
84
+ self._headers.update({"x-referer": self.reseller.web_url})
85
85
 
86
86
  @property
87
- def resailer(self) -> SmartboxResailer:
88
- """Get the resailer."""
89
- return self._resailer
87
+ def reseller(self) -> SmartboxReseller:
88
+ """Get the reseller."""
89
+ return self._reseller
90
90
 
91
91
  @property
92
92
  def api_name(self) -> str:
93
93
  """Get the api sub domain url."""
94
- return self.resailer.api_url
94
+ return self.reseller.api_url
95
95
 
96
96
  @property
97
97
  def api_host(self) -> str:
@@ -150,7 +150,7 @@ class AsyncSession:
150
150
  del token_headers["Authorization"]
151
151
  token_headers.update(
152
152
  {
153
- "authorization": f"Basic {self.resailer.basic_auth}",
153
+ "authorization": f"Basic {self.reseller.basic_auth}",
154
154
  "Content-Type": "application/x-www-form-urlencoded",
155
155
  },
156
156
  )
@@ -3,7 +3,7 @@ from unittest.mock import patch
3
3
  from asyncclick.testing import CliRunner
4
4
  import pytest
5
5
 
6
- from smartbox.resailer import SmartboxResailer
6
+ from smartbox.reseller import SmartboxReseller
7
7
  from smartbox.session import AsyncSession, AsyncSmartboxSession, Session
8
8
  from smartbox.update_manager import UpdateManager
9
9
  from tests.common import fake_get_request
@@ -25,7 +25,7 @@ def update_manager(mock_session):
25
25
 
26
26
 
27
27
  @pytest.fixture
28
- def async_smartbox_session(mocker, resailer):
28
+ def async_smartbox_session(mocker, reseller):
29
29
  async_smartbox_session = AsyncSmartboxSession(
30
30
  api_name="test_api",
31
31
  username="test_user",
@@ -47,7 +47,7 @@ def async_smartbox_session(mocker, resailer):
47
47
 
48
48
 
49
49
  @pytest.fixture
50
- def session(resailer):
50
+ def session(reseller):
51
51
  return Session(
52
52
  api_name="test_api",
53
53
  username="test_user",
@@ -56,7 +56,7 @@ def session(resailer):
56
56
 
57
57
 
58
58
  @pytest.fixture
59
- def async_session(resailer):
59
+ def async_session(reseller):
60
60
  api_name = "test_api"
61
61
  username = "test_user"
62
62
  password = "test_password"
@@ -76,7 +76,7 @@ def async_session(resailer):
76
76
 
77
77
  @pytest.fixture
78
78
  def person():
79
- return SmartboxResailer(
79
+ return SmartboxReseller(
80
80
  name="test",
81
81
  api_url="test_api",
82
82
  basic_auth="test_credentials",
@@ -86,12 +86,12 @@ def person():
86
86
 
87
87
 
88
88
  @pytest.fixture
89
- def resailer(mocker):
89
+ def reseller(mocker):
90
90
  return mocker.patch(
91
- "smartbox.resailer.AvailableResailers.resailers",
91
+ "smartbox.reseller.AvailableResellers.resellers",
92
92
  new_callable=mocker.PropertyMock,
93
93
  return_value={
94
- "test_api": SmartboxResailer(
94
+ "test_api": SmartboxReseller(
95
95
  name="test",
96
96
  api_url="test_api",
97
97
  basic_auth="test_credentials",
@@ -263,22 +263,14 @@ async def test_node_samples(runner, mock_session):
263
263
 
264
264
 
265
265
  @pytest.mark.anyio
266
- async def test_resailers(runner, mocker):
267
- mock_resailers = mocker.patch("smartbox.cmd.AvailableResailers.resailers")
268
- mock_resailers.items.return_value = [
269
- ("resailer1", "details1"),
270
- ("resailer2", "details2"),
271
- ]
272
-
266
+ async def test_reseller(runner, mocker, reseller):
273
267
  result = await runner.invoke(
274
268
  smartbox,
275
- [*DEFAULT_ARGS, "resailers"],
269
+ [*DEFAULT_ARGS, "resellers"],
276
270
  )
277
271
  assert result.exit_code == 0
278
- assert "resailer1" in result.output
279
- assert "details1" in result.output
280
- assert "resailer2" in result.output
281
- assert "details2" in result.output
272
+ assert "test_api" in result.output
273
+ assert "http" in result.output
282
274
 
283
275
 
284
276
  @pytest.mark.anyio
@@ -0,0 +1,58 @@
1
+ import pytest
2
+
3
+ from smartbox import AsyncSmartboxSession, ResellerNotExistError
4
+ from smartbox.reseller import AvailableResellers
5
+
6
+
7
+ def test_available_resellers_existing_reseller():
8
+ reseller = AvailableResellers(api_url="api").reseller
9
+ assert reseller.name == "Helki"
10
+ assert reseller.api_url == "api"
11
+
12
+
13
+ def test_available_resellers_non_existing_reseller():
14
+ with pytest.raises(ResellerNotExistError):
15
+ AvailableResellers(api_url="non-existing-api").reseller
16
+
17
+
18
+ def test_available_resellers_custom_reseller():
19
+ serial_id = 99
20
+ _reseller = AvailableResellers(
21
+ api_url="custom-api",
22
+ basic_auth="custom-auth",
23
+ web_url="https://custom-url.com",
24
+ serial_id=serial_id,
25
+ name="Custom",
26
+ )
27
+ reseller = _reseller.reseller
28
+ assert reseller.name == "Custom"
29
+ assert reseller.api_url == "custom-api"
30
+ assert reseller.basic_auth == "custom-auth"
31
+ assert reseller.web_url == "https://custom-url.com"
32
+ assert reseller.serial_id == serial_id
33
+
34
+ assert _reseller.name == "Custom"
35
+ assert _reseller.api_url == "custom-api"
36
+ assert _reseller.web_url == "https://custom-url.com"
37
+
38
+
39
+ def test_reseller_invalid_data():
40
+ with pytest.raises(ResellerNotExistError):
41
+ AvailableResellers(
42
+ api_url="invalid-api",
43
+ basic_auth="invalid-auth",
44
+ web_url="invalid-url",
45
+ serial_id="invalid-serial-id",
46
+ ).reseller
47
+
48
+
49
+ @pytest.mark.asyncio
50
+ async def test_all_resellers():
51
+ for reseller in AvailableResellers.resellers.values():
52
+ _session = AsyncSmartboxSession(
53
+ username="", password="", api_name=reseller.api_url
54
+ )
55
+ check = await _session.health_check()
56
+ assert check is not None
57
+ version = await _session.api_version()
58
+ assert "major" in version