Skip to content

Commit

Permalink
golang support
Browse files Browse the repository at this point in the history
  • Loading branch information
Eitol committed Nov 10, 2024
1 parent 8195e9b commit 97e3ab2
Show file tree
Hide file tree
Showing 67 changed files with 6,086 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
venv/
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ Note that some calls can be made via Stream instead of gRPC unary calls.

The following languages are supported for client libraries:

- [x] [GO](libs/golang/client/GOLANG_LIB.md)
- [ ] Python
- [ ] Node (Typescript)
- [x] [GO](libs/golang/client/README)
- [ ] [Python](libs/python/starlink-client/README.md)
- [ ] Javascript
- [ ] Dart
- [ ] Java
- [ ] Kotlin
Expand Down
6 changes: 4 additions & 2 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ plugins:

# Python
- plugin: buf.build/grpc/python
out: libs/python/gen
out: libs/python/starlink-client
- plugin: buf.build/protocolbuffers/python
out: libs/python/gen
out: libs/python/starlink-client
- plugin: buf.build/protocolbuffers/pyi:v28.3
out: libs/python/starlink-client

# Dart
- plugin: buf.build/protocolbuffers/dart
Expand Down
File renamed without changes.
8 changes: 6 additions & 2 deletions libs/golang/client/client/grpc_web_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ func (c *Client) doGrpcWebCall(req proto.Message) ([]byte, int, error) {
httpResp, err := c.sendGrpcWebRequest(httpReq)
if httpResp != nil {
defer func(Body io.ReadCloser) {
err := Body.Close()
err = Body.Close()
if err != nil {
fmt.Printf("Error closing body: %v\n", err)
}
}(httpResp.Body)
if httpResp.StatusCode == http.StatusUnauthorized {
return nil, httpResp.StatusCode, fmt.Errorf("unauthorized - status %v", httpResp.Status)
err = c.refreshAuth()
if err != nil {
return nil, httpResp.StatusCode, fmt.Errorf("error refreshing auth: %v", err)
}
return c.doGrpcWebCall(req)
}
}
if err != nil {
Expand Down
58 changes: 58 additions & 0 deletions libs/python/starlink-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Starlink API Python Client

#### Example 1: List devices
```python
from spacex.api.device.wifi_config_pb2 import WifiConfig
from starlink_client.grpc_web_client import GrpcWebClient

if __name__ == "__main__":
initial_cookies = "cookies HERE"
client = GrpcWebClient(initial_cookies, "dir_cookies")

status = client.get_wifi_status("Router-010000000000000000499851")
print("-------------------------")
print("ID: " + status.device_info.id)
print("Software Version: " + status.device_info.software_version)
print("Networks: ")
for n in status.config.networks:
for bss in n.basic_service_sets:
if bss.band == WifiConfig.RF_2GHZ:
print(f"\t2.4ghz: {bss.ssid}")
elif bss.band in [WifiConfig.RF_5GHZ, WifiConfig.RF_5GHZ_HIGH]:
print(f"\t5ghz: {bss.ssid}")
print("Clients:")
for client in status.clients:
if client.ip_address == "":
continue
print(f"\t{client.name} | {client.ip_address}")
```

Output:
```text
ID: Router-010000000000000000499851
Software Version: 2024.05.31.mr36376
Networks:
2.4ghz: AURORITA 2021
5ghz: AURORITA 2021
Clients:
mk tickets | 192.168.1.21
infinix-hot-30i | 192.168.1.22
galaxy-s24-ultra | 192.168.1.23
| 192.168.1.24
```


#### Example 2: Call directly to gRPC service

```python
from spacex.api.device.device_pb2 import Request, GetStatusRequest
from starlink_client.grpc_web_client import GrpcWebClient

if __name__ == "__main__":
router_id = "your router id here"
initial_cookies = "cookies HERE"
client = GrpcWebClient(initial_cookies, "dir_cookies")
grpc_req = Request(target_id=router_id, get_status=GetStatusRequest())
resp = client.call(grpc_req)
print(resp.wifi_get_status)
```
11 changes: 11 additions & 0 deletions libs/python/starlink-client/examples/grpc_direct_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from spacex.api.device.device_pb2 import Request, GetStatusRequest
from starlink_client.grpc_web_client import GrpcWebClient

if __name__ == "__main__":
router_id = "your router id here"
initial_cookies = "cookies HERE"
client = GrpcWebClient(initial_cookies, "dir_cookies")
grpc_req = Request(target_id=router_id, get_status=GetStatusRequest())
resp = client.call(grpc_req)
print(resp.wifi_get_status)

23 changes: 23 additions & 0 deletions libs/python/starlink-client/examples/list_devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from spacex.api.device.wifi_config_pb2 import WifiConfig
from starlink_client.grpc_web_client import GrpcWebClient

if __name__ == "__main__":
initial_cookies = "cookies HERE"
client = GrpcWebClient(initial_cookies, "dir_cookies")

status = client.get_wifi_status("Router-010000000000000000499851")
print("-------------------------")
print("ID: " + status.device_info.id)
print("Software Version: " + status.device_info.software_version)
print("Networks: ")
for n in status.config.networks:
for bss in n.basic_service_sets:
if bss.band == WifiConfig.RF_2GHZ:
print(f"\t2.4ghz: {bss.ssid}")
elif bss.band in [WifiConfig.RF_5GHZ, WifiConfig.RF_5GHZ_HIGH]:
print(f"\t5ghz: {bss.ssid}")
print("Clients:")
for client in status.clients:
if client.ip_address == "":
continue
print(f"\t{client.name} | {client.ip_address}")
14 changes: 14 additions & 0 deletions libs/python/starlink-client/google/protobuf/timestamp_pb2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from google.protobuf.internal import well_known_types as _well_known_types
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional

DESCRIPTOR: _descriptor.FileDescriptor

class Timestamp(_message.Message, _well_known_types.Timestamp):
__slots__ = ("seconds", "nanos")
SECONDS_FIELD_NUMBER: _ClassVar[int]
NANOS_FIELD_NUMBER: _ClassVar[int]
seconds: int
nanos: int
def __init__(self, seconds: _Optional[int] = ..., nanos: _Optional[int] = ...) -> None: ...
59 changes: 59 additions & 0 deletions libs/python/starlink-client/google/protobuf/wrappers_pb2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional

DESCRIPTOR: _descriptor.FileDescriptor

class DoubleValue(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: float
def __init__(self, value: _Optional[float] = ...) -> None: ...

class FloatValue(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: float
def __init__(self, value: _Optional[float] = ...) -> None: ...

class Int64Value(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: int
def __init__(self, value: _Optional[int] = ...) -> None: ...

class UInt64Value(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: int
def __init__(self, value: _Optional[int] = ...) -> None: ...

class Int32Value(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: int
def __init__(self, value: _Optional[int] = ...) -> None: ...

class UInt32Value(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: int
def __init__(self, value: _Optional[int] = ...) -> None: ...

class BoolValue(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: bool
def __init__(self, value: bool = ...) -> None: ...

class StringValue(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: str
def __init__(self, value: _Optional[str] = ...) -> None: ...

class BytesValue(_message.Message):
__slots__ = ("value",)
VALUE_FIELD_NUMBER: _ClassVar[int]
value: bytes
def __init__(self, value: _Optional[bytes] = ...) -> None: ...
7 changes: 7 additions & 0 deletions libs/python/starlink-client/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
grpcio
grpcio-status
proto-plus
protobuf
requests
httpx
pydantic
28 changes: 28 additions & 0 deletions libs/python/starlink-client/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from setuptools import setup, find_packages

setup(
name="starlink-client",
version="0.1.0",
packages=find_packages(include=["starlink_client", "starlink_client.*"]),
package_dir={"": "starlink_client"},
include_package_data=True,
install_requires=[
"grpcio",
"grpcio-status",
"proto-plus",
"protobuf",
"requests",
"httpx",
"pydantic",
],
description="A Python client for Starlink.",
author="Hector Oliveros",
author_email="[email protected]",
url="https://github.com/Eitol/starlink-client",
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.9',
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional

DESCRIPTOR: _descriptor.FileDescriptor

class Status(_message.Message):
__slots__ = ("code", "message")
CODE_FIELD_NUMBER: _ClassVar[int]
MESSAGE_FIELD_NUMBER: _ClassVar[int]
code: int
message: str
def __init__(self, code: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ...
71 changes: 71 additions & 0 deletions libs/python/starlink-client/spacex/api/device/command_pb2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Optional as _Optional, Union as _Union

DESCRIPTOR: _descriptor.FileDescriptor

class Capability(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
READ: _ClassVar[Capability]
READ_INTERNAL: _ClassVar[Capability]
READ_PRIVATE: _ClassVar[Capability]
LOCAL: _ClassVar[Capability]
WRITE: _ClassVar[Capability]
WRITE_PERSISTENT: _ClassVar[Capability]
DEBUG: _ClassVar[Capability]
ADMIN: _ClassVar[Capability]
SETUP: _ClassVar[Capability]
SET_SKU: _ClassVar[Capability]
REFRESH: _ClassVar[Capability]
FUSE: _ClassVar[Capability]
RESET: _ClassVar[Capability]
TEST: _ClassVar[Capability]
SSH: _ClassVar[Capability]
GUEST: _ClassVar[Capability]

class User(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
NO_USER: _ClassVar[User]
GOD: _ClassVar[User]
LAN: _ClassVar[User]
CLOUD: _ClassVar[User]
FACTORY: _ClassVar[User]
ROUTER: _ClassVar[User]
GUEST_LAN: _ClassVar[User]
SENSITIVE_COMMANDING: _ClassVar[User]
READ: Capability
READ_INTERNAL: Capability
READ_PRIVATE: Capability
LOCAL: Capability
WRITE: Capability
WRITE_PERSISTENT: Capability
DEBUG: Capability
ADMIN: Capability
SETUP: Capability
SET_SKU: Capability
REFRESH: Capability
FUSE: Capability
RESET: Capability
TEST: Capability
SSH: Capability
GUEST: Capability
NO_USER: User
GOD: User
LAN: User
CLOUD: User
FACTORY: User
ROUTER: User
GUEST_LAN: User
SENSITIVE_COMMANDING: User

class PublicKey(_message.Message):
__slots__ = ("key", "capabilities", "user")
KEY_FIELD_NUMBER: _ClassVar[int]
CAPABILITIES_FIELD_NUMBER: _ClassVar[int]
USER_FIELD_NUMBER: _ClassVar[int]
key: str
capabilities: _containers.RepeatedScalarFieldContainer[Capability]
user: User
def __init__(self, key: _Optional[str] = ..., capabilities: _Optional[_Iterable[_Union[Capability, str]]] = ..., user: _Optional[_Union[User, str]] = ...) -> None: ...
Loading

0 comments on commit 97e3ab2

Please sign in to comment.