googlewifiapi 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,5 @@
1
+ from .api import GoogleWifiAPI
2
+ from .const import LOGGER
3
+ from .status import GoogleWifiStatus
4
+
5
+ __all__ = ["GoogleWifiAPI", "GoogleWifiStatus", "LOGGER"]
googlewifiapi/api.py ADDED
@@ -0,0 +1,57 @@
1
+ """Google Wifi API wrapper."""
2
+
3
+ from typing import Any
4
+
5
+ import aiohttp
6
+ from yarl import URL
7
+
8
+ from .const import DEFAULT_HOST, ENDPOINT
9
+ from .exception import (
10
+ GoogleWifiClientError,
11
+ GoogleWifiDataValidationError,
12
+ GoogleWifiException,
13
+ )
14
+ from .status import GoogleWifiStatus
15
+
16
+
17
+ class GoogleWifiAPI:
18
+ """Get the latest data and update the states."""
19
+
20
+ raw_data: dict[str, Any] | None = None
21
+ available: bool = True
22
+ _resource: URL
23
+ data: GoogleWifiStatus | None = None
24
+ sess: aiohttp.ClientSession
25
+
26
+ def __init__(
27
+ self, host: str = DEFAULT_HOST, sess: aiohttp.ClientSession | None = None
28
+ ) -> None:
29
+ """Initialize the API wrapper."""
30
+ self._resource = URL(f"http://{host}{ENDPOINT}")
31
+ self.sess = sess or aiohttp.ClientSession()
32
+
33
+ async def async_update(self) -> GoogleWifiStatus:
34
+ """Get the latest data from the router."""
35
+ try:
36
+ resp = await self.sess.get(self._resource)
37
+ raw_data = await resp.json()
38
+ except (aiohttp.ClientError, ValueError) as err:
39
+ raise GoogleWifiClientError() from err
40
+
41
+ if not isinstance(raw_data, dict):
42
+ raise GoogleWifiDataValidationError(
43
+ f"The router provided data that could not be validated. "
44
+ f"Raw data: {raw_data}"
45
+ )
46
+
47
+ self.raw_data = raw_data
48
+ try:
49
+ self.data = GoogleWifiStatus.from_dict(raw_data)
50
+ return self.data
51
+ except (LookupError, TypeError, ValueError) as err:
52
+ raise GoogleWifiDataValidationError(
53
+ f"The router provided data that could not be validated. "
54
+ f"Raw data: {raw_data}"
55
+ ) from err
56
+ except Exception as err:
57
+ raise GoogleWifiException() from err
googlewifiapi/const.py ADDED
@@ -0,0 +1,7 @@
1
+ """Constants for the Google Wifi API wrapper."""
2
+
3
+ import logging
4
+
5
+ DEFAULT_HOST = "192.168.86.1"
6
+ ENDPOINT = "/api/v1/status"
7
+ LOGGER = logging.getLogger(__package__)
@@ -0,0 +1,13 @@
1
+ """Exceptions for the Google Wifi API Wrapper."""
2
+
3
+
4
+ class GoogleWifiException(Exception):
5
+ """Raised when there is a generic exception while wrapping the API."""
6
+
7
+
8
+ class GoogleWifiDataValidationError(GoogleWifiException):
9
+ """Raised when the data cannot be validated."""
10
+
11
+
12
+ class GoogleWifiClientError(GoogleWifiException):
13
+ """Raised when the connection fails in some way."""
@@ -0,0 +1,93 @@
1
+ """Google Wifi Status using mashumaro dataclasses."""
2
+
3
+ import ipaddress
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime, timedelta
6
+ from typing import Literal, Union
7
+
8
+ from mashumaro import DataClassDictMixin
9
+ from mashumaro.config import BaseConfig
10
+
11
+
12
+ IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class GoogleWifiStatus(DataClassDictMixin):
17
+ """Google Wifi status."""
18
+
19
+ dns: "GoogleWifiStatus.DNS"
20
+ software: "GoogleWifiStatus.Software"
21
+ system: "GoogleWifiStatus.System"
22
+ wan: "GoogleWifiStatus.WAN"
23
+
24
+ class Config(BaseConfig):
25
+ serialize_by_alias = True
26
+ deserialize_by_alias = True
27
+
28
+ @dataclass(frozen=True)
29
+ class DNS(DataClassDictMixin):
30
+ mode: Literal["automatic", "custom"]
31
+ servers: list[IPAddress]
32
+
33
+ @dataclass(frozen=True)
34
+ class Software(DataClassDictMixin):
35
+ software_version: str
36
+ update_status: str
37
+ update_required: bool
38
+ update_progress: float
39
+
40
+ class Config(BaseConfig):
41
+ serialize_by_alias = True
42
+ deserialize_by_alias = True
43
+ aliases = {
44
+ "software_version": "softwareVersion",
45
+ "update_status": "updateStatus",
46
+ "update_required": "updateRequired",
47
+ "update_progress": "updateProgress",
48
+ }
49
+
50
+ @dataclass(frozen=True)
51
+ class System(DataClassDictMixin):
52
+ country_code: str
53
+ model_id: str
54
+ uptime: int
55
+ last_restart: datetime = field(init=False)
56
+
57
+ class Config(BaseConfig):
58
+ serialize_by_alias = True
59
+ deserialize_by_alias = True
60
+ aliases = {
61
+ "country_code": "countryCode",
62
+ "model_id": "modelId",
63
+ }
64
+
65
+ def __post_init__(self):
66
+ """Compute last_restart from uptime."""
67
+ object.__setattr__(
68
+ self,
69
+ "last_restart",
70
+ datetime.now() - timedelta(seconds=self.uptime),
71
+ )
72
+
73
+ @dataclass(frozen=True)
74
+ class WAN(DataClassDictMixin):
75
+ online: bool
76
+ ethernet_link: bool
77
+ gateway_ip_address: IPAddress
78
+ local_ip_address: IPAddress
79
+ ip_method: str
80
+ ip_prefix_length: int
81
+ name_servers: list[IPAddress]
82
+
83
+ class Config(BaseConfig):
84
+ serialize_by_alias = True
85
+ deserialize_by_alias = True
86
+ aliases = {
87
+ "ethernet_link": "ethernetLink",
88
+ "gateway_ip_address": "gatewayIpAddress",
89
+ "local_ip_address": "localIpAddress",
90
+ "ip_method": "ipMethod",
91
+ "ip_prefix_length": "ipPrefixLength",
92
+ "name_servers": "nameServers",
93
+ }
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: googlewifiapi
3
+ Version: 0.1.0
4
+ Summary: Asynchronous Python wrapper for the local Google Wifi JSON API
5
+ Author-email: Eddie Reasoner <eddie.reasoner@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/EReaso/GoogleWifiApi
8
+ Project-URL: Bug Tracker, https://github.com/EReaso/GoogleWifiApi/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Requires-Dist: aiohttp<3.14.0,>=3.11.0
15
+ Requires-Dist: mashumaro>=3.22
16
+ Requires-Dist: yarl>=1.24.5
17
+ Dynamic: license-file
18
+
19
+ # googlewifiapi
20
+
21
+ An asynchronous Python client for reading status from the local JSON API exposed
22
+ by Google Wifi routers.
23
+ The package is designed for integrations such as Home Assistant, but can be used by any Python application that needs router status data.
24
+
25
+ ## About this README
26
+
27
+ This README was generated by AI with human review.
28
+ All code is either written by a human or is thoroughly reviewed.
29
+ I'll try to keep the README updated, but sometimes I might forget.
30
+
31
+ ## Requirements
32
+
33
+ - Python 3.13 or newer
34
+ - A Google Wifi router reachable on the local network
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install googlewifiapi
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Pass an `aiohttp.ClientSession` to the client so your application controls the
45
+ session lifecycle:
46
+
47
+ ```python
48
+ import asyncio
49
+
50
+ import aiohttp
51
+
52
+ from googlewifiapi import GoogleWifiAPI
53
+
54
+
55
+ async def main() -> None:
56
+ async with aiohttp.ClientSession() as session:
57
+ router = GoogleWifiAPI(host="192.168.86.1", sess=session)
58
+ await router.async_update()
59
+
60
+ if router.data is not None:
61
+ print(router.data.wan.online)
62
+ print(router.data.software.software_version)
63
+ print(router.data.system.last_restart)
64
+
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ The host defaults to `192.168.86.1`. After a successful update:
70
+
71
+ - `router.raw_data` contains the raw JSON response.
72
+ - `router.data` contains a validated, typed `GoogleWifiStatus`.
73
+ - IP address fields are parsed as `IPv4Address` or `IPv6Address` objects.
74
+ - `system.last_restart` is calculated from the router's uptime.
75
+
76
+ The status object exposes `dns`, `software`, `system`, and `wan` sections. Their
77
+ fields use Python names such as `software_version`, `gateway_ip_address`, and
78
+ `ip_prefix_length`, while the client handles the router API's camelCase keys.
79
+
80
+ ## Errors
81
+
82
+ All library errors inherit from `GoogleWifiException`:
83
+
84
+ - `GoogleWifiClientError` indicates a request, connection, timeout, or JSON
85
+ decoding failure.
86
+ - `GoogleWifiDataValidationError` indicates that the response does not match
87
+ the expected status schema.
88
+
89
+ ## Development
90
+
91
+ Install the test dependencies and run the test suite with [uv](https://docs.astral.sh/uv/):
92
+
93
+ ```bash
94
+ uv sync --group test
95
+ uv run pytest
96
+ ```
97
+
98
+ ## License
99
+
100
+ This project is licensed under the MIT License. See [LICENSE.txt](LICENSE.txt).
101
+
102
+ ## Disclaimer
103
+
104
+ This is an independent, community-developed project and is not affiliated with
105
+ or endorsed by Google LLC. It relies on a local API that Google may change or
106
+ remove without notice.
@@ -0,0 +1,10 @@
1
+ googlewifiapi/__init__.py,sha256=vFHdWJlq6-StukcpM6lp1fv7SFC0IznlJiI6-s9TqCI,153
2
+ googlewifiapi/api.py,sha256=G9iVBrh5UTn0OaIbjE9xIHLv8hQFFUSbDtRfB7onXN4,1801
3
+ googlewifiapi/const.py,sha256=iqyR3lS01vQXVKuHg4vf1QOuGxcPb4lJ3tcPjjtaT7A,164
4
+ googlewifiapi/exception.py,sha256=Np7EofKO8CyMYwPpWeW0yPeFV9UbX4pBPhhDnI_4ROY,385
5
+ googlewifiapi/status.py,sha256=5ZVbujcgqkHF4hZv3UoXx4qE9ps64gsu-0ZAvT_ojak,2778
6
+ googlewifiapi-0.1.0.dist-info/licenses/LICENSE.txt,sha256=l2iRY7G89sXwOXio9L2752hqHxP9bOmaX49p6jCLUiE,1071
7
+ googlewifiapi-0.1.0.dist-info/METADATA,sha256=sVrW_lZB0XhR62qFxuZupHcw6I1SVajaA7smbKLepT0,3129
8
+ googlewifiapi-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ googlewifiapi-0.1.0.dist-info/top_level.txt,sha256=rxzXa5_G4xRQvIYMBB2cafHTT2QqJhNbEoWLwmhQ_1Q,14
10
+ googlewifiapi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Eddie Reasoner
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 @@
1
+ googlewifiapi