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.
- eero-client-2.2.0/LICENSE +21 -0
- eero-client-2.2.0/PKG-INFO +97 -0
- eero-client-2.2.0/README.md +73 -0
- eero-client-2.2.0/eero/__init__.py +14 -0
- eero-client-2.2.0/eero/client/__init__.py +3 -0
- eero-client-2.2.0/eero/client/api_client.py +68 -0
- eero-client-2.2.0/eero/client/clients/__init__.py +4 -0
- eero-client-2.2.0/eero/client/clients/eero.py +43 -0
- eero-client-2.2.0/eero/client/clients/eero_auth_handler.py +51 -0
- eero-client-2.2.0/eero/client/clients/network.py +34 -0
- eero-client-2.2.0/eero/client/models/__init__.py +38 -0
- eero-client-2.2.0/eero/client/models/ac_compat.py +5 -0
- eero-client-2.2.0/eero/client/models/account.py +87 -0
- eero-client-2.2.0/eero/client/models/burst_reporters.py +7 -0
- eero-client-2.2.0/eero/client/models/device.py +118 -0
- eero-client-2.2.0/eero/client/models/diagnostics.py +5 -0
- eero-client-2.2.0/eero/client/models/error_meta.py +14 -0
- eero-client-2.2.0/eero/client/models/forward.py +12 -0
- eero-client-2.2.0/eero/client/models/networks.py +442 -0
- eero-client-2.2.0/eero/client/models/no_data_meta.py +13 -0
- eero-client-2.2.0/eero/client/models/profile.py +51 -0
- eero-client-2.2.0/eero/client/models/reservation.py +9 -0
- eero-client-2.2.0/eero/client/models/routing.py +33 -0
- eero-client-2.2.0/eero/client/models/speedtest.py +9 -0
- eero-client-2.2.0/eero/client/models/support.py +9 -0
- eero-client-2.2.0/eero/client/models/thread.py +20 -0
- eero-client-2.2.0/eero/client/models/updates.py +24 -0
- eero-client-2.2.0/eero/client/routes/__init__.py +10 -0
- eero-client-2.2.0/eero/client/routes/method_factory.py +98 -0
- eero-client-2.2.0/eero/client/routes/routes.py +97 -0
- eero-client-2.2.0/eero/exceptions/__init__.py +3 -0
- eero-client-2.2.0/eero/exceptions/client_exception.py +10 -0
- eero-client-2.2.0/eero/logger.py +21 -0
- eero-client-2.2.0/eero/py.typed +0 -0
- eero-client-2.2.0/eero/session/__init__.py +4 -0
- eero-client-2.2.0/eero/session/abc.py +8 -0
- eero-client-2.2.0/eero/session/file.py +24 -0
- eero-client-2.2.0/eero/version.py +1 -0
- eero-client-2.2.0/eero_client.egg-info/PKG-INFO +97 -0
- eero-client-2.2.0/eero_client.egg-info/SOURCES.txt +45 -0
- eero-client-2.2.0/eero_client.egg-info/dependency_links.txt +1 -0
- eero-client-2.2.0/eero_client.egg-info/not-zip-safe +1 -0
- eero-client-2.2.0/eero_client.egg-info/requires.txt +10 -0
- eero-client-2.2.0/eero_client.egg-info/top_level.txt +1 -0
- eero-client-2.2.0/pyproject.toml +9 -0
- eero-client-2.2.0/setup.cfg +4 -0
- 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,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,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,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,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
|