eero-client 2.2.0__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 (47) hide show
  1. eero-client-2.2.0/LICENSE +21 -0
  2. eero-client-2.2.0/PKG-INFO +97 -0
  3. eero-client-2.2.0/README.md +73 -0
  4. eero-client-2.2.0/eero/__init__.py +14 -0
  5. eero-client-2.2.0/eero/client/__init__.py +3 -0
  6. eero-client-2.2.0/eero/client/api_client.py +68 -0
  7. eero-client-2.2.0/eero/client/clients/__init__.py +4 -0
  8. eero-client-2.2.0/eero/client/clients/eero.py +43 -0
  9. eero-client-2.2.0/eero/client/clients/eero_auth_handler.py +51 -0
  10. eero-client-2.2.0/eero/client/clients/network.py +34 -0
  11. eero-client-2.2.0/eero/client/models/__init__.py +38 -0
  12. eero-client-2.2.0/eero/client/models/ac_compat.py +5 -0
  13. eero-client-2.2.0/eero/client/models/account.py +87 -0
  14. eero-client-2.2.0/eero/client/models/burst_reporters.py +7 -0
  15. eero-client-2.2.0/eero/client/models/device.py +118 -0
  16. eero-client-2.2.0/eero/client/models/diagnostics.py +5 -0
  17. eero-client-2.2.0/eero/client/models/error_meta.py +14 -0
  18. eero-client-2.2.0/eero/client/models/forward.py +12 -0
  19. eero-client-2.2.0/eero/client/models/networks.py +442 -0
  20. eero-client-2.2.0/eero/client/models/no_data_meta.py +13 -0
  21. eero-client-2.2.0/eero/client/models/profile.py +51 -0
  22. eero-client-2.2.0/eero/client/models/reservation.py +9 -0
  23. eero-client-2.2.0/eero/client/models/routing.py +33 -0
  24. eero-client-2.2.0/eero/client/models/speedtest.py +9 -0
  25. eero-client-2.2.0/eero/client/models/support.py +9 -0
  26. eero-client-2.2.0/eero/client/models/thread.py +20 -0
  27. eero-client-2.2.0/eero/client/models/updates.py +24 -0
  28. eero-client-2.2.0/eero/client/routes/__init__.py +10 -0
  29. eero-client-2.2.0/eero/client/routes/method_factory.py +98 -0
  30. eero-client-2.2.0/eero/client/routes/routes.py +97 -0
  31. eero-client-2.2.0/eero/exceptions/__init__.py +3 -0
  32. eero-client-2.2.0/eero/exceptions/client_exception.py +10 -0
  33. eero-client-2.2.0/eero/logger.py +21 -0
  34. eero-client-2.2.0/eero/py.typed +0 -0
  35. eero-client-2.2.0/eero/session/__init__.py +4 -0
  36. eero-client-2.2.0/eero/session/abc.py +8 -0
  37. eero-client-2.2.0/eero/session/file.py +24 -0
  38. eero-client-2.2.0/eero/version.py +1 -0
  39. eero-client-2.2.0/eero_client.egg-info/PKG-INFO +97 -0
  40. eero-client-2.2.0/eero_client.egg-info/SOURCES.txt +45 -0
  41. eero-client-2.2.0/eero_client.egg-info/dependency_links.txt +1 -0
  42. eero-client-2.2.0/eero_client.egg-info/not-zip-safe +1 -0
  43. eero-client-2.2.0/eero_client.egg-info/requires.txt +10 -0
  44. eero-client-2.2.0/eero_client.egg-info/top_level.txt +1 -0
  45. eero-client-2.2.0/pyproject.toml +9 -0
  46. eero-client-2.2.0/setup.cfg +4 -0
  47. eero-client-2.2.0/setup.py +52 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.1
2
+ Name: eero-client
3
+ Version: 2.2.0
4
+ Summary: Manage eero network devices
5
+ Home-page: https://github.com/EvanSchalton/eero-client
6
+ Author: Evan Schalton
7
+ Author-email: Evan.Schalton@Gmail.com
8
+ License: MIT License
9
+ Keywords: eero
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Natural Language :: English
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Typing :: Typed
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Utilities
20
+ Classifier: License :: OSI Approved :: MIT License
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+
25
+ # Unofficial client for [Eero Mesh Routers](https://eero.com)
26
+
27
+
28
+ # SDK Design Considerations
29
+ The SDK leverages pydantic to return models for all supported endpoints, endpoints are created using a method factory and configurations in the [routes.py](/eero//client//routes/routes.py) file allowing for easy extension.
30
+
31
+ - `GET` Methods are exposed as class properties
32
+ - `POST` Methods are exposed as class methods with `**kwargs` for route params (if applicable).
33
+
34
+ As this is an undocumented API, I was only able to create models for the objects returned in my network, please consider contributing / updating models if your network has additional parameters.
35
+
36
+
37
+ # Usage Example
38
+ [example.ipynb](/example.ipynb)
39
+
40
+ ## Authentication
41
+ ```
42
+ from eero import Eero, FileSessionStorage
43
+
44
+ session = FileSessionStorage("session.cookie")
45
+ eero = Eero(session=session)
46
+
47
+ if not eero.is_authenticated:
48
+ auth_source = input("Phone Number or Email: ")
49
+ user_token = eero.login(auth_source)
50
+ verification_code = input("verification key from email or SMS: ")
51
+ eero.login_verify(verification_code, user_token)
52
+ ```
53
+
54
+ ## Routes
55
+ ### Get Requests
56
+ [GET_RESOURCES](/eero//client/routes/routes.py) defines the get properties in the structure of `{property_name: tuple[url, response_model]}`
57
+
58
+ The following get properties are defined:
59
+ ```
60
+ account
61
+ ac_compat
62
+ device_blacklist
63
+ devices
64
+ diagnostics
65
+ eeros
66
+ forwards
67
+ ouicheck
68
+ guestnetwork
69
+ profiles
70
+ reservations
71
+ speedtest
72
+ updates
73
+ support
74
+ insights
75
+ routing
76
+ thread
77
+ networks
78
+ ```
79
+ ### Post Requests
80
+ [POST_RESOURCES](/eero//client/routes/routes.py) defines the post methods in the structure of `{property_name: tuple[url, response_model]}`
81
+
82
+ The following get properties are defined:
83
+ ```
84
+ burst_reporters
85
+ reboot
86
+ reboot_eero - takes in the id of the error to reboot, available from the client.eeros get endpoint.
87
+ run_speedtest
88
+ ```
89
+ # Inspiration
90
+ Thank you to `Max von Webel` for his original work, [eero-client](https://github.com/343max/eero-client), I hadn't considered accesssing my router's data until I saw his project and only opted to fork and rewrite to add more of my own opinionated design patterns.
91
+
92
+ # Future Plans
93
+ ## Device Triangulation
94
+ I played with the idea of using the signal strength for each connected device captured while looping over the network turn off all but one eero at a time, and the relative 3D coordinates of the eero devices to create a 3D rendering of all of the devices in my home. This worked decently well for devices in a room with an eero, but for obstructed devices their positions there were too many intersection points with a mesh of four units. If I circle back to this in the future I may add code in a post.
95
+
96
+ ## Home Assistant Integration
97
+ My next goal is to expose the eeros & devices in Home Assistant through an integration, when I'm successful I'll post a link to that repo/HACS integration here my intent is to use this SDK as the foundation for that work.
@@ -0,0 +1,73 @@
1
+ # Unofficial client for [Eero Mesh Routers](https://eero.com)
2
+
3
+
4
+ # SDK Design Considerations
5
+ The SDK leverages pydantic to return models for all supported endpoints, endpoints are created using a method factory and configurations in the [routes.py](/eero//client//routes/routes.py) file allowing for easy extension.
6
+
7
+ - `GET` Methods are exposed as class properties
8
+ - `POST` Methods are exposed as class methods with `**kwargs` for route params (if applicable).
9
+
10
+ As this is an undocumented API, I was only able to create models for the objects returned in my network, please consider contributing / updating models if your network has additional parameters.
11
+
12
+
13
+ # Usage Example
14
+ [example.ipynb](/example.ipynb)
15
+
16
+ ## Authentication
17
+ ```
18
+ from eero import Eero, FileSessionStorage
19
+
20
+ session = FileSessionStorage("session.cookie")
21
+ eero = Eero(session=session)
22
+
23
+ if not eero.is_authenticated:
24
+ auth_source = input("Phone Number or Email: ")
25
+ user_token = eero.login(auth_source)
26
+ verification_code = input("verification key from email or SMS: ")
27
+ eero.login_verify(verification_code, user_token)
28
+ ```
29
+
30
+ ## Routes
31
+ ### Get Requests
32
+ [GET_RESOURCES](/eero//client/routes/routes.py) defines the get properties in the structure of `{property_name: tuple[url, response_model]}`
33
+
34
+ The following get properties are defined:
35
+ ```
36
+ account
37
+ ac_compat
38
+ device_blacklist
39
+ devices
40
+ diagnostics
41
+ eeros
42
+ forwards
43
+ ouicheck
44
+ guestnetwork
45
+ profiles
46
+ reservations
47
+ speedtest
48
+ updates
49
+ support
50
+ insights
51
+ routing
52
+ thread
53
+ networks
54
+ ```
55
+ ### Post Requests
56
+ [POST_RESOURCES](/eero//client/routes/routes.py) defines the post methods in the structure of `{property_name: tuple[url, response_model]}`
57
+
58
+ The following get properties are defined:
59
+ ```
60
+ burst_reporters
61
+ reboot
62
+ reboot_eero - takes in the id of the error to reboot, available from the client.eeros get endpoint.
63
+ run_speedtest
64
+ ```
65
+ # Inspiration
66
+ Thank you to `Max von Webel` for his original work, [eero-client](https://github.com/343max/eero-client), I hadn't considered accesssing my router's data until I saw his project and only opted to fork and rewrite to add more of my own opinionated design patterns.
67
+
68
+ # Future Plans
69
+ ## Device Triangulation
70
+ I played with the idea of using the signal strength for each connected device captured while looping over the network turn off all but one eero at a time, and the relative 3D coordinates of the eero devices to create a 3D rendering of all of the devices in my home. This worked decently well for devices in a room with an eero, but for obstructed devices their positions there were too many intersection points with a mesh of four units. If I circle back to this in the future I may add code in a post.
71
+
72
+ ## Home Assistant Integration
73
+ My next goal is to expose the eeros & devices in Home Assistant through an integration, when I'm successful I'll post a link to that repo/HACS integration here my intent is to use this SDK as the foundation for that work.
@@ -0,0 +1,14 @@
1
+ from .client import Eero
2
+ from .exceptions import ClientException
3
+ from .logger import log_setup
4
+ from .session import FileSessionStorage, SessionStorage
5
+ from .version import __version__
6
+
7
+ log_setup()
8
+ __all__ = [
9
+ "ClientException",
10
+ "Eero",
11
+ "SessionStorage",
12
+ "FileSessionStorage",
13
+ "__version__",
14
+ ]
@@ -0,0 +1,3 @@
1
+ from .clients import Eero
2
+
3
+ __all__ = ["Eero"]
@@ -0,0 +1,68 @@
1
+ import json
2
+ from http import HTTPStatus
3
+ from logging import getLogger
4
+ from typing import Any
5
+
6
+ import requests
7
+
8
+ from ..exceptions import ClientException
9
+ from .models import ErrorMeta
10
+
11
+ logger = getLogger("eero")
12
+
13
+ API_VERSION = "2.2"
14
+
15
+
16
+ class APIClient(object):
17
+ API_ENDPOINT = "https://api-user.e2ro.com/{}/{}"
18
+
19
+ def _parse_response(self, action, response) -> dict[str, Any]:
20
+ data = json.loads(response.text)
21
+ logger.debug("Response for %s: %s", action, data)
22
+ try:
23
+ if data["meta"]["code"] not in [
24
+ HTTPStatus.OK,
25
+ HTTPStatus.CREATED,
26
+ HTTPStatus.ACCEPTED,
27
+ ]:
28
+ client_exception = ClientException(
29
+ data["meta"]["code"], data["meta"].get("error", "Unknown Error")
30
+ )
31
+ logger.error(client_exception)
32
+
33
+ try:
34
+ ErrorMeta.model_validate(data)
35
+ return data
36
+ except Exception:
37
+ raise client_exception
38
+ except ClientException as e:
39
+ raise e
40
+ except Exception as e:
41
+ logger.error("KeyError - Failed to parse response: %s [%s]", data, e)
42
+ try:
43
+ raise ClientException(
44
+ data.get("result", {}).get("error", {}).get("requestId"),
45
+ data.get("result", {}).get("error", {}).get("message"),
46
+ ) from e
47
+ except KeyError:
48
+ raise ClientException(
49
+ data.get("meta", {}).get("code", HTTPStatus.INTERNAL_SERVER_ERROR),
50
+ data.get("meta", {}).get("error", "Unknown Error"),
51
+ ) from e
52
+ return data.get("data", data.get("meta", {}))
53
+
54
+ def request(self, method, action, **kwargs):
55
+ response = requests.request(
56
+ method, self.API_ENDPOINT.format(API_VERSION, action), **kwargs
57
+ )
58
+ return self._parse_response(action, response)
59
+
60
+ def post(self, action, **kwargs):
61
+ response = requests.post(
62
+ self.API_ENDPOINT.format(API_VERSION, action), **kwargs
63
+ )
64
+ return self._parse_response(action, response)
65
+
66
+ def get(self, action, **kwargs):
67
+ response = requests.get(self.API_ENDPOINT.format(API_VERSION, action), **kwargs)
68
+ return self._parse_response(action, response)
@@ -0,0 +1,4 @@
1
+ from .eero import Eero
2
+ from .network import NetworkClient
3
+
4
+ __all__ = ["Eero", "NetworkClient"]
@@ -0,0 +1,43 @@
1
+ from ..api_client import APIClient
2
+ from ..models.account import Account
3
+ from ..routes.method_factory import make_method
4
+ from ..routes.routes import GET_RESOURCES
5
+ from .eero_auth_handler import EeroAuthHandler
6
+ from .network import NetworkClient
7
+
8
+
9
+ class Eero(EeroAuthHandler):
10
+ def __init__(self, session, api_client=APIClient | None) -> None:
11
+ self.session = session
12
+ if api_client is None:
13
+ api_client = APIClient()
14
+ self.client = api_client
15
+ self._network_clients: dict[str, NetworkClient] | None = None # type: ignore
16
+
17
+ @property
18
+ def network_clients(self) -> dict[str, NetworkClient]: # type: ignore
19
+ if self._network_clients is None:
20
+ if not self.is_authenticated:
21
+ raise ValueError("Not authenticated")
22
+ self._network_clients = {
23
+ i.name: NetworkClient(
24
+ network_info=i,
25
+ session=self.session,
26
+ client=self.client,
27
+ **i.model_dump(),
28
+ )
29
+ for i in self.account().networks.data
30
+ }
31
+
32
+ return self._network_clients
33
+
34
+ @network_clients.setter
35
+ def network_clients(self, network_clients: dict[str, NetworkClient]): # type: ignore
36
+ self._network_clients = network_clients
37
+
38
+ def account(self) -> Account:
39
+ return make_method(
40
+ method="get",
41
+ action="account",
42
+ resource=GET_RESOURCES["account"],
43
+ )(self)
@@ -0,0 +1,51 @@
1
+ from ...exceptions import ClientException
2
+ from ..api_client import APIClient
3
+
4
+
5
+ class EeroAuthHandler:
6
+ def __init__(self, session):
7
+ self.session = session
8
+ self.client = APIClient()
9
+
10
+ @property
11
+ def _cookie_dict(self):
12
+ if not self.is_authenticated:
13
+ return dict()
14
+ else:
15
+ return dict(s=self.session.cookie)
16
+
17
+ @property
18
+ def is_authenticated(self) -> bool:
19
+ return self.session.cookie is not None
20
+
21
+ def login(self, identifier):
22
+ # type(string) -> string
23
+ json = dict(login=identifier)
24
+ data = self.client.post("login", json=json)
25
+ return data["user_token"]
26
+
27
+ def login_verify(self, verification_code, user_token):
28
+ response = self.client.post(
29
+ "login/verify",
30
+ json=dict(code=verification_code),
31
+ cookies=dict(s=user_token),
32
+ )
33
+ self.session.cookie = user_token
34
+ return response
35
+
36
+ def refreshed(self, func):
37
+ try:
38
+ return func()
39
+ except ClientException as exception:
40
+ if (
41
+ exception.status == 401
42
+ and exception.error_message == "error.session.refresh"
43
+ ):
44
+ self.login_refresh()
45
+ return func()
46
+ else:
47
+ raise
48
+
49
+ def login_refresh(self):
50
+ response = self.client.post("login/refresh", cookies=self._cookie_dict)
51
+ self.session.cookie = response["user_token"]
@@ -0,0 +1,34 @@
1
+ from typing import Any, cast
2
+
3
+ from ...session import SessionStorage
4
+ from ..api_client import APIClient
5
+ from ..models import NetworkInfo
6
+ from ..routes import APITypes, network_client_methods
7
+ from .eero_auth_handler import EeroAuthHandler
8
+
9
+
10
+ def __network_client____init___(
11
+ self,
12
+ session: SessionStorage,
13
+ network_info: NetworkInfo,
14
+ client: APIClient | None = None,
15
+ **kwargs: Any,
16
+ ) -> None:
17
+ self.network_info = network_info
18
+ self.session = session
19
+ if client is None:
20
+ client = APIClient()
21
+ self.client = client
22
+
23
+
24
+ NetworkClient = type(
25
+ "NetworkClient",
26
+ (APITypes, EeroAuthHandler),
27
+ {
28
+ "__init__": __network_client____init___,
29
+ "network_info": cast(NetworkInfo, None),
30
+ "session": cast(SessionStorage, None),
31
+ "client": cast(APIClient, None),
32
+ **network_client_methods,
33
+ },
34
+ )
@@ -0,0 +1,38 @@
1
+ from .ac_compat import ACCompat
2
+ from .account import Account, NetworkInfo
3
+ from .burst_reporters import BurstReporters
4
+ from .device import Device
5
+ from .diagnostics import Diagnostics
6
+ from .error_meta import ErrorMeta
7
+ from .forward import Forward
8
+ from .networks import EeroDevice, GuestNetwork, Networks
9
+ from .no_data_meta import NoDataMeta
10
+ from .profile import Profile
11
+ from .reservation import Reservation
12
+ from .routing import Routing
13
+ from .speedtest import Speedtest
14
+ from .support import Support
15
+ from .thread import Thread
16
+ from .updates import Updates
17
+
18
+ __all__ = [
19
+ "Account",
20
+ "NetworkInfo",
21
+ "Networks",
22
+ "Device",
23
+ "ACCompat",
24
+ "Diagnostics",
25
+ "EeroDevice",
26
+ "Forward",
27
+ "GuestNetwork",
28
+ "Profile",
29
+ "Reservation",
30
+ "Speedtest",
31
+ "Updates",
32
+ "Support",
33
+ "Routing",
34
+ "Thread",
35
+ "ErrorMeta",
36
+ "BurstReporters",
37
+ "NoDataMeta",
38
+ ]
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class ACCompat(BaseModel):
5
+ enabled: bool
@@ -0,0 +1,87 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class Phone(BaseModel):
7
+ value: str
8
+ country_code: str
9
+ national_number: str
10
+ verified: bool
11
+
12
+
13
+ class Email(BaseModel):
14
+ value: str
15
+ verified: bool
16
+
17
+
18
+ class NetworkInfo(BaseModel):
19
+ url: str
20
+ name: str
21
+ created: str
22
+ nickname_label: str | None = None
23
+ access_expires_on: datetime | None = None
24
+ amazon_directed_id: str
25
+
26
+ @property
27
+ def id(self):
28
+ return self.url.split("/")[-1]
29
+
30
+
31
+ class Networks(BaseModel):
32
+ count: int
33
+ data: list[NetworkInfo]
34
+
35
+
36
+ class Auth(BaseModel):
37
+ type: str
38
+ provider_id: str | None = None
39
+ service_id: str | None = None
40
+
41
+
42
+ class PremiumDetails(BaseModel):
43
+ trial_ends: datetime | None = None
44
+ has_payment_info: bool
45
+ tier: str
46
+ is_iap_customer: bool
47
+ payment_method: str | None = None
48
+ interval: str
49
+ next_billing_event_date: datetime | None = None
50
+
51
+
52
+ class PushSettings(BaseModel):
53
+ networkOffline: bool
54
+ nodeOffline: bool
55
+
56
+
57
+ class Consent(BaseModel):
58
+ consented: bool
59
+
60
+
61
+ class Consents(BaseModel):
62
+ marketing_emails: Consent
63
+
64
+
65
+ class Account(BaseModel):
66
+ name: str
67
+ phone: Phone
68
+ email: Email
69
+ log_id: str
70
+ organization_id: str | None = None
71
+ image_assets: list[str] | None = None
72
+ networks: Networks
73
+ auth: Auth
74
+ role: str | None = None
75
+ is_beta_bug_reporter_eligible: bool
76
+ can_transfer: bool
77
+ is_premium_capable: bool
78
+ payment_failed: bool
79
+ premium_status: str
80
+ premium_details: PremiumDetails
81
+ push_settings: PushSettings
82
+ trust_certificates_etag: str
83
+ consents: Consents
84
+ can_migrate_to_amazon_login: bool
85
+ eero_for_business: bool
86
+ mdu_program: bool
87
+ business_details: str | None = None
@@ -0,0 +1,7 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class BurstReporters(BaseModel):
7
+ next_burst: datetime
@@ -0,0 +1,118 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class DeviceSource(BaseModel):
7
+ location: str | None = None
8
+ is_gateway: bool | None = None
9
+ model: str | None = None
10
+ display_name: str | None = None
11
+ serial_number: str | None = None
12
+ is_proxied_node: bool | None = None
13
+ url: str | None = None
14
+
15
+
16
+ class RateInfo(BaseModel):
17
+ rate_bps: int | str | None = None
18
+ mcs: int | str | None = None
19
+ nss: int | str | None = None
20
+ guard_interval: str | None = None
21
+ channel_width: str | None = None
22
+ phy_type: str | None = None
23
+
24
+
25
+ class DeviceEthernetStatus(BaseModel):
26
+ has_carrier: bool
27
+ interface_number: int
28
+ speed: str
29
+ is_wan_port: bool
30
+ is_lte: bool
31
+ port_name: str
32
+
33
+
34
+ class DeviceConnectivity(BaseModel):
35
+ rx_bitrate: str | None = None
36
+ signal: str | None = None
37
+ signal_avg: str | None = None
38
+ score: float | None = None
39
+ score_bars: int | None = None
40
+ frequency: int | None = None
41
+ rx_rate_info: RateInfo | None = None
42
+ tx_rate_info: RateInfo | None = None
43
+ ethernet_status: DeviceEthernetStatus | None = None
44
+
45
+
46
+ class Interface(BaseModel):
47
+ frequency: str | None = None
48
+ frequency_unit: str | None = None
49
+
50
+
51
+ class Profile(BaseModel):
52
+ url: str
53
+ name: str
54
+ paused: bool
55
+
56
+
57
+ class HomeKit(BaseModel):
58
+ registered: bool
59
+ protection_mode: str
60
+
61
+
62
+ class DeviceRingLTE(BaseModel):
63
+ is_not_pausable: bool
64
+ ring_managed: bool
65
+ lte_enabled: bool
66
+
67
+
68
+ class AmazonDevicesDetail(BaseModel):
69
+ model_config = ConfigDict(protected_namespaces=())
70
+
71
+ model_name_internal: str | None = None
72
+
73
+
74
+ class RoutingDeviceData(BaseModel):
75
+ url: str
76
+ mac: str
77
+ nickname: str | None = None
78
+
79
+
80
+ class ProfileDevice(RoutingDeviceData):
81
+ model_config = ConfigDict(protected_namespaces=())
82
+ manufacturer: str | None = None
83
+ manufacturer_device_type_id: str | None = None
84
+ ip: str | None = None
85
+ ips: list[str]
86
+ hostname: str | None = None
87
+ connected: bool
88
+ wireless: bool
89
+ connection_type: str | None = None
90
+ source: DeviceSource
91
+ last_active: datetime | None = None
92
+ first_active: datetime | None = None
93
+ connectivity: DeviceConnectivity
94
+ interface: Interface
95
+ usage: str | None = None
96
+ homekit: HomeKit | None = None
97
+ device_type: str
98
+ auth: str | None = None
99
+ paused: bool
100
+ display_name: str | None = None
101
+ is_private: bool
102
+ model_name: str | None = None
103
+
104
+
105
+ class Device(ProfileDevice):
106
+ eui64: str
107
+ ipv6_addresses: list[str]
108
+ profile: Profile | None = None
109
+ blacklisted: bool | None = None
110
+ is_guest: bool
111
+ channel: int | None = None
112
+ secondary_wan_deny_access: bool
113
+ ring_lte: DeviceRingLTE
114
+ ipv4: str | None = None
115
+ is_proxied_node: bool
116
+ amazon_devices_detail: AmazonDevicesDetail | None = None
117
+ ssid: str | None = None
118
+ subnet_kind: str | None = None
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class Diagnostics(BaseModel):
5
+ status: str