start data structures
This commit is contained in:
parent
6933b03b4a
commit
99d1049ab9
140
phlyght/api.py
140
phlyght/api.py
|
@ -2,12 +2,16 @@ __all__ = ("Router", "route", "RouterMeta", "SubRouter", "HughApi")
|
|||
|
||||
from inspect import signature
|
||||
from re import compile
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from httpx import AsyncClient
|
||||
from httpx._urls import URL as _URL
|
||||
from yarl import URL as UR
|
||||
|
||||
# from rich import print
|
||||
|
||||
from .models import Lights
|
||||
|
||||
|
||||
STR_FMT_RE = compile(r"""(?=(\{([^:]+)(?::([^}]+))?\}))\1""")
|
||||
URL_TYPES = {"str": str, "int": int}
|
||||
|
@ -34,6 +38,24 @@ class URL(_URL):
|
|||
return URL(str(UR(f"{self}") / other.lstrip("/")))
|
||||
|
||||
|
||||
def ret_cls(cls):
|
||||
def wrapped(fn):
|
||||
async def sub_wrap(self, *args, **kwargs):
|
||||
kwargs.pop("base_uri", None)
|
||||
ret = (await fn(self, *args, **kwargs)).json().get("data", [])
|
||||
_rets = []
|
||||
if isinstance(ret, list):
|
||||
for r in ret:
|
||||
_rets.append(cls(**r))
|
||||
else:
|
||||
return cls(**ret)
|
||||
return _rets
|
||||
|
||||
return sub_wrap
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def route(method, endpoint) -> Any:
|
||||
def wrapped(fn):
|
||||
async def sub_wrap(
|
||||
|
@ -44,6 +66,7 @@ def route(method, endpoint) -> Any:
|
|||
params: Optional[dict[str, str]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
params = params or {}
|
||||
data = data or {}
|
||||
for param_name, param in signature(fn).parameters.items():
|
||||
if param_name == "self":
|
||||
|
@ -51,7 +74,9 @@ def route(method, endpoint) -> Any:
|
|||
|
||||
if param_name in fn.__annotations__:
|
||||
anno = fn.__annotations__[param_name]
|
||||
if anno._name == "Optional":
|
||||
if isinstance(anno, type):
|
||||
type_ = anno
|
||||
elif anno._name == "Optional":
|
||||
if hasattr(anno.__args__[0], "_name"):
|
||||
type_ = str
|
||||
else:
|
||||
|
@ -94,6 +119,7 @@ def route(method, endpoint) -> Any:
|
|||
new_endpoint = URL(f"{self._api_path}") / endpoint.format(**url_args)
|
||||
else:
|
||||
new_endpoint = URL(f"{self._api_path}") / endpoint
|
||||
|
||||
return await self._client.request(
|
||||
method,
|
||||
new_endpoint,
|
||||
|
@ -121,8 +147,8 @@ class RouterMeta(type):
|
|||
_api = f"{_root}{_base}"
|
||||
|
||||
def set_key(v, base_uri=None):
|
||||
def wrap(self, **_kwds):
|
||||
return v(self, base_uri=base_uri, **_kwds)
|
||||
def wrap(self, *args, **_kwds):
|
||||
return v(self, *args, base_uri=base_uri, **_kwds)
|
||||
|
||||
return wrap
|
||||
|
||||
|
@ -182,16 +208,24 @@ class SubRouter(metaclass=RouterMeta):
|
|||
class HughApi(SubRouter):
|
||||
BASE_URI = "/clip/v2"
|
||||
|
||||
@ret_cls(Lights.Light)
|
||||
@route("GET", "/resource/light")
|
||||
async def get_lights(self, friendly_name: Optional[str] = None):
|
||||
...
|
||||
|
||||
@ret_cls(Lights.Light)
|
||||
@route("GET", "/resource/light/{light_id}")
|
||||
async def get_light(self, light_id: int):
|
||||
async def get_light(self, light_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/light/{light_id}")
|
||||
async def set_light(self, light_id: int, **kwargs):
|
||||
async def set_light(
|
||||
self,
|
||||
light_id: int,
|
||||
on: Optional[dict[Literal["on"], bool]] = None,
|
||||
dimming: Optional[dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/scene")
|
||||
|
@ -203,15 +237,15 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/scene/{scene_id}")
|
||||
async def get_scene(self, scene_id: int):
|
||||
async def get_scene(self, scene_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/scene/{scene_id}")
|
||||
async def set_scene(self, scene_id: int, **kwargs):
|
||||
async def set_scene(self, scene_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("DELETE", "/resource/scene/{scene_id}")
|
||||
async def delete_scene(self, scene_id: int):
|
||||
async def delete_scene(self, scene_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/room")
|
||||
|
@ -223,15 +257,15 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/room/{room_id}")
|
||||
async def get_room(self, room_id: int):
|
||||
async def get_room(self, room_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/room/{room_id}")
|
||||
async def set_room(self, room_id: int, **kwargs):
|
||||
async def set_room(self, room_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("DELETE", "/resource/room/{room_id}")
|
||||
async def delete_room(self, room_id: int):
|
||||
async def delete_room(self, room_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/zone")
|
||||
|
@ -243,15 +277,15 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/zone/{zone_id}")
|
||||
async def get_zone(self, zone_id: int):
|
||||
async def get_zone(self, zone_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/zone/{zone_id}")
|
||||
async def set_zone(self, zone_id: int, **kwargs):
|
||||
async def set_zone(self, zone_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("DELETE", "/resource/zone/{zone_id}")
|
||||
async def delete_zone(self, zone_id: int):
|
||||
async def delete_zone(self, zone_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/bridge_home")
|
||||
|
@ -259,11 +293,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/bridge_home/{bridge_home_id}")
|
||||
async def get_bridge_home(self, bridge_home_id: int):
|
||||
async def get_bridge_home(self, bridge_home_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/bridge_home/{bridge_home_id}")
|
||||
async def set_bridge_home(self, bridge_home_id: int, **kwargs):
|
||||
async def set_bridge_home(self, bridge_home_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/grouped_light")
|
||||
|
@ -271,11 +305,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/grouped_light/{grouped_light_id}")
|
||||
async def get_grouped_light(self, grouped_light_id: int):
|
||||
async def get_grouped_light(self, grouped_light_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/grouped_light/{grouped_light_id}")
|
||||
async def set_grouped_light(self, grouped_light_id: int, **kwargs):
|
||||
async def set_grouped_light(self, grouped_light_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/device")
|
||||
|
@ -283,11 +317,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/device/{device_id}")
|
||||
async def get_device(self, device_id: int):
|
||||
async def get_device(self, device_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/device/{device_id}")
|
||||
async def set_device(self, device_id: int, **kwargs):
|
||||
async def set_device(self, device_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/bridges")
|
||||
|
@ -295,11 +329,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/bridges/{bridge_id}")
|
||||
async def get_bridge(self, bridge_id: int):
|
||||
async def get_bridge(self, bridge_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/bridges/{bridge_id}")
|
||||
async def set_bridge(self, bridge_id: int, **kwargs):
|
||||
async def set_bridge(self, bridge_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/device_power")
|
||||
|
@ -307,11 +341,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/device_power/{device_power_id}")
|
||||
async def get_device_power(self, device_power_id: int):
|
||||
async def get_device_power(self, device_power_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/device_power/{device_power_id}")
|
||||
async def set_device_power(self, device_power_id: int, **kwargs):
|
||||
async def set_device_power(self, device_power_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/zigbee_connectivity")
|
||||
|
@ -319,11 +353,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/zigbee_connectivity/{zigbee_connectivity_id}")
|
||||
async def get_zigbee_connectivity(self, zigbee_connectivity_id: int):
|
||||
async def get_zigbee_connectivity(self, zigbee_connectivity_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/zigbee_connectivity/{zigbee_connectivity_id}")
|
||||
async def set_zigbee_connectivity(self, zigbee_connectivity_id: int, **kwargs):
|
||||
async def set_zigbee_connectivity(self, zigbee_connectivity_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/zgb_connectivity")
|
||||
|
@ -331,11 +365,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/zgb_connectivity/{zgb_connectivity_id}")
|
||||
async def get_zgb_connectivity(self, zgb_connectivity_id: int):
|
||||
async def get_zgb_connectivity(self, zgb_connectivity_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/zgb_connectivity/{zgb_connectivity_id}")
|
||||
async def set_zgb_connectivity(self, zgb_connectivity_id: int, **kwargs):
|
||||
async def set_zgb_connectivity(self, zgb_connectivity_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/motion")
|
||||
|
@ -343,11 +377,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/motion/{motion_id}")
|
||||
async def get_motion(self, motion_id: int):
|
||||
async def get_motion(self, motion_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/motion/{motion_id}")
|
||||
async def set_motion(self, motion_id: int, **kwargs):
|
||||
async def set_motion(self, motion_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/temperature")
|
||||
|
@ -355,11 +389,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/temperature/{temperature_id}")
|
||||
async def get_temperature(self, temperature_id: int):
|
||||
async def get_temperature(self, temperature_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/temperature/{temperature_id}")
|
||||
async def set_temperature(self, temperature_id: int, **kwargs):
|
||||
async def set_temperature(self, temperature_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/light_level")
|
||||
|
@ -367,11 +401,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/light_level/{light_level_id}")
|
||||
async def get_light_level(self, light_level_id: int):
|
||||
async def get_light_level(self, light_level_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/light_level/{light_level_id}")
|
||||
async def set_light_level(self, light_level_id: int, **kwargs):
|
||||
async def set_light_level(self, light_level_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/button")
|
||||
|
@ -379,11 +413,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/button/{button_id}")
|
||||
async def get_button(self, button_id: int):
|
||||
async def get_button(self, button_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/button/{button_id}")
|
||||
async def set_button(self, button_id: int, **kwargs):
|
||||
async def set_button(self, button_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/behavior_script")
|
||||
|
@ -391,7 +425,7 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/behavior_script/{behavior_script_id}")
|
||||
async def get_behavior_script(self, behavior_script_id: int):
|
||||
async def get_behavior_script(self, behavior_script_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/behavior_instance")
|
||||
|
@ -403,15 +437,15 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/behavior_instance/{behavior_instance_id}")
|
||||
async def get_behavior_instance(self, behavior_instance_id: int):
|
||||
async def get_behavior_instance(self, behavior_instance_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/behavior_instance/{behavior_instance_id}")
|
||||
async def set_behavior_instance(self, behavior_instance_id: int, **kwargs):
|
||||
async def set_behavior_instance(self, behavior_instance_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("DELETE", "/resource/behavior_instance/{behavior_instance_id}")
|
||||
async def delete_behavior_instance(self, behavior_instance_id: int):
|
||||
async def delete_behavior_instance(self, behavior_instance_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/geofence_client")
|
||||
|
@ -423,15 +457,15 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/geofence_client/{geofence_client_id}")
|
||||
async def get_geofence_client(self, geofence_client_id: int):
|
||||
async def get_geofence_client(self, geofence_client_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/geofence_client/{geofence_client_id}")
|
||||
async def set_geofence_client(self, geofence_client_id: int, **kwargs):
|
||||
async def set_geofence_client(self, geofence_client_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("DELETE", "/resource/geofence_client/{geofence_client_id}")
|
||||
async def delete_geofence_client(self, geofence_client_id: int):
|
||||
async def delete_geofence_client(self, geofence_client_id: str):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/geolocation")
|
||||
|
@ -439,11 +473,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/geolocation/{geolocation_id}")
|
||||
async def get_geolocation(self, geolocation_id: int):
|
||||
async def get_geolocation(self, geolocation_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/geolocation/{geolocation_id}")
|
||||
async def set_geolocation(self, geolocation_id: int, **kwargs):
|
||||
async def set_geolocation(self, geolocation_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/entertainment_configuration")
|
||||
|
@ -458,7 +492,7 @@ class HughApi(SubRouter):
|
|||
"GET", "/resource/entertainment_configuration/{entertainment_configuration_id}"
|
||||
)
|
||||
async def get_entertainment_configuration(
|
||||
self, entertainment_configuration_id: int
|
||||
self, entertainment_configuration_id: str
|
||||
):
|
||||
...
|
||||
|
||||
|
@ -466,7 +500,7 @@ class HughApi(SubRouter):
|
|||
"PUT", "/resource/entertainment_configuration/{entertainment_configuration_id}"
|
||||
)
|
||||
async def set_entertainment_configuration(
|
||||
self, entertainment_configuration_id: int, **kwargs
|
||||
self, entertainment_configuration_id: str, **kwargs
|
||||
):
|
||||
...
|
||||
|
||||
|
@ -475,7 +509,7 @@ class HughApi(SubRouter):
|
|||
"/resource/entertainment_configuration/{entertainment_configuration_id}",
|
||||
)
|
||||
async def delete_entertainment_configuration(
|
||||
self, entertainment_configuration_id: int
|
||||
self, entertainment_configuration_id: str
|
||||
):
|
||||
...
|
||||
|
||||
|
@ -484,11 +518,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/entertainment/{entertainment_id}")
|
||||
async def get_entertainment(self, entertainment_id: int):
|
||||
async def get_entertainment(self, entertainment_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/entertainment/{entertainment_id}")
|
||||
async def set_entertainment(self, entertainment_id: int, **kwargs):
|
||||
async def set_entertainment(self, entertainment_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource/homekit")
|
||||
|
@ -496,11 +530,11 @@ class HughApi(SubRouter):
|
|||
...
|
||||
|
||||
@route("GET", "/resource/homekit/{homekit_id}")
|
||||
async def get_homekit(self, homekit_id: int):
|
||||
async def get_homekit(self, homekit_id: str):
|
||||
...
|
||||
|
||||
@route("PUT", "/resource/homekit/{homekit_id}")
|
||||
async def set_homekit(self, homekit_id: int, **kwargs):
|
||||
async def set_homekit(self, homekit_id: str, **kwargs):
|
||||
...
|
||||
|
||||
@route("GET", "/resource")
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
from typing import Literal, Optional
|
||||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Lights:
|
||||
class MetaData(BaseModel):
|
||||
archetype: str
|
||||
name: str
|
||||
|
||||
class Owner(BaseModel):
|
||||
rid: UUID
|
||||
rtype: str
|
||||
|
||||
class On(BaseModel):
|
||||
on: bool
|
||||
|
||||
class Dimming(BaseModel):
|
||||
brightness: float
|
||||
min_dim_level: float
|
||||
|
||||
class MirekSchema(BaseModel):
|
||||
mirek_minimum: float
|
||||
mirek_maximum: float
|
||||
|
||||
class ColorTemperature(BaseModel):
|
||||
mirek: Optional[int]
|
||||
mirek_valid: bool
|
||||
mirek_schema: "Lights.MirekSchema"
|
||||
|
||||
class XY(BaseModel):
|
||||
x: float
|
||||
y: float
|
||||
|
||||
class Gamut(BaseModel):
|
||||
red: "Lights.XY"
|
||||
green: "Lights.XY"
|
||||
blue: "Lights.XY"
|
||||
|
||||
class Color(BaseModel):
|
||||
xy: "Lights.XY"
|
||||
gamut: "Lights.Gamut"
|
||||
gamut_type: Literal["A"] | Literal["B"] | Literal["C"]
|
||||
|
||||
class Dynamics(BaseModel):
|
||||
status: str
|
||||
status_values: list[str]
|
||||
speed: float
|
||||
speed_valid: bool
|
||||
|
||||
class Alert(BaseModel):
|
||||
action_values: list[str]
|
||||
|
||||
class GradientColor(BaseModel):
|
||||
xy: "Lights.XY"
|
||||
|
||||
class Gradient(BaseModel):
|
||||
points: list["Lights.GradientColor"]
|
||||
points_capable: int
|
||||
|
||||
class Effects(BaseModel):
|
||||
effect: Optional[list[Literal["fire", "candle", "no_effect"]]]
|
||||
status_values: list[Literal["fire", "candle", "no_effect"]]
|
||||
status: Literal["fire", "candle", "no_effect"]
|
||||
effect_values: list[Literal["fire", "candle", "no_effect"]]
|
||||
|
||||
class TimedEffects(BaseModel):
|
||||
effect: Literal["sunrise", "no_effect"]
|
||||
duration: int
|
||||
status_values: list[Literal["sunrise", "no_effect"]]
|
||||
status: Literal["sunrise", "no_effect"]
|
||||
effect_values: list[Literal["sunrise", "no_effect"]]
|
||||
|
||||
class Light(BaseModel):
|
||||
id: UUID
|
||||
id_v1: str
|
||||
owner: "Lights.Owner"
|
||||
metadata: "Lights.MetaData"
|
||||
on: "Lights.On"
|
||||
dimming: "Lights.Dimming"
|
||||
dimming_delta: dict
|
||||
color_temperature: Optional["Lights.ColorTemperature"]
|
||||
color_temperature_delta: Optional[dict]
|
||||
color: Optional["Lights.Color"]
|
||||
gradient: Optional["Lights.Gradient"]
|
||||
dynamics: "Lights.Dynamics"
|
||||
alert: "Lights.Alert"
|
||||
signaling: dict
|
||||
mode: str
|
||||
effects: "Lights.Effects"
|
||||
type: str
|
||||
|
||||
|
||||
for k in Lights.__dict__.values():
|
||||
if hasattr(k, "update_forward_refs"):
|
||||
k.update_forward_refs()
|
|
@ -10,6 +10,7 @@ rich = ">=12.6.0"
|
|||
aioredis = ">=2.0.1"
|
||||
httpx = "^0.23.1"
|
||||
yarl = "^1.8.1"
|
||||
pydantic = "^1.10.2"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = ">=22.10.0"
|
||||
|
|
6
test.py
6
test.py
|
@ -5,9 +5,11 @@ from phlyght.api import Router
|
|||
|
||||
|
||||
async def main():
|
||||
router = Router("ur api key")
|
||||
router = Router("TzPrxDf9hyWZoR5jvUaGDZn4Hlxp2XF67ue4ynSI")
|
||||
lights = await router.get_lights()
|
||||
print(lights.json())
|
||||
for light in lights:
|
||||
detailed_light = await router.get_light(light_id=str(light.id))
|
||||
print(detailed_light)
|
||||
|
||||
|
||||
run(main())
|
||||
|
|
Loading…
Reference in New Issue