Skip to content

Commit

Permalink
Merge branch 'dev' into curfew
Browse files Browse the repository at this point in the history
  • Loading branch information
benleb authored Apr 4, 2023
2 parents 15d2ea0 + 15d1b6a commit 83782cd
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 11 deletions.
31 changes: 29 additions & 2 deletions surepy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
REFERER,
SUREPY_USER_AGENT,
USER_AGENT,
DEVICE_TAG_RESOURCE,
)
from .enums import Location, LockState
from .exceptions import SurePetcareAuthenticationError, SurePetcareConnectionError, SurePetcareError
Expand Down Expand Up @@ -218,7 +219,7 @@ async def call(
if not self._auth_token:
self._auth_token = await self.get_token()

if method not in ["GET", "PUT", "POST"]:
if method not in ["GET", "PUT", "POST", "DELETE"]:
raise HTTPException(f"unknown http method: {method}")

response_data = None
Expand Down Expand Up @@ -263,7 +264,7 @@ async def call(
)
self._auth_token = None
if not second_try:
token_refreshed = self.get_token()
token_refreshed = await self.get_token()
if token_refreshed:
await self.call(method="GET", resource=resource, second_try=True)

Expand All @@ -289,6 +290,10 @@ async def call(
responselen,
)

if method == "DELETE" and response.status == HTTPStatus.NO_CONTENT:
# TODO: this does not return any data, is there a better way?
return "DELETE 204 No Content"

return response_data

except (asyncio.TimeoutError, aiohttp.ClientError) as error:
Expand Down Expand Up @@ -405,3 +410,25 @@ async def set_curfew(

# return None
raise SurePetcareError("ERROR SETTING CURFEW - PLEASE CHECK IMMEDIATELY!")

async def _add_tag_to_device(self, device_id: int, tag_id: int) -> dict[str, Any] | None:
"""Add the specified tag ID to the specified device ID"""
resource = DEVICE_TAG_RESOURCE.format(BASE_RESOURCE=BASE_RESOURCE, device_id=device_id, tag_id=tag_id)

if(
response := await self.call(
method="PUT", resource=resource
)
):
return response

async def _remove_tag_from_device(self, device_id: int, tag_id: int) -> dict[str, Any] | None:
"""Removes the specified tag ID from the specified device ID"""
resource = DEVICE_TAG_RESOURCE.format(BASE_RESOURCE=BASE_RESOURCE, device_id=device_id, tag_id=tag_id)

if(
response := await self.call(
method="DELETE", resource=resource
)
):
return response
1 change: 1 addition & 0 deletions surepy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
CONTROL_RESOURCE: str = "{BASE_RESOURCE}/device/{device_id}/control"
POSITION_RESOURCE: str = "{BASE_RESOURCE}/pet/{pet_id}/position"
ATTRIBUTES_RESOURCE: str = f"{BASE_RESOURCE}/start"
DEVICE_TAG_RESOURCE: str = "{BASE_RESOURCE}/device/{device_id}/tag/{tag_id}"


API_TIMEOUT = 45
Expand Down
37 changes: 37 additions & 0 deletions surepy/entities/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,35 @@ def position(self) -> str | None:
def raw_data(self) -> dict[str, int | float | str]:
return self._data

class Tag:
"""Tags assigned to a device."""

def __init__(self, data: dict[str, int | float | str], feeder: Feeder):
"""Initialize a Sure Petcare sensor."""

self._data: dict[str, int | float | str] = data

@property
def id(self) -> int:
return int(self._data["id"])

def index(self) -> int:
return int(self._data["index"])

def profile(self) -> int:
return int(self._data["profile"])

def version(self) -> str:
return self._data["version"]

def created_at(self) -> str:
return str(self._data["created_at"])

def updated_at(self) -> str:
return self._data["updated_at"]

def raw_data(self) -> dict[str, int | float | str]:
return self._data

class Feeder(SurepyDevice):
"""Sure Petcare Cat- or Pet-Flap."""
Expand All @@ -140,6 +169,10 @@ def __init__(self, data: dict[str, Any]):

self.add_bowls()

self.tags: dict[int, Tag] = {}

self.add_tags()

@property
def bowl_count(self) -> int:
return len(self.bowls)
Expand All @@ -158,6 +191,10 @@ def icon(self) -> str | None:
"""Icon of the Felaqua."""
return urlparse("https://surehub.io/assets/images/feeder-left-menu.png").geturl()

def add_tags(self) -> None:
if tags := self._data.get("tags"):
for tag in tags:
self.tags[tag["index"]] = Tag(data=tag, feeder=self)

class Felaqua(SurepyDevice):
"""Sure Petcare Cat- or Pet-Flap."""
Expand Down
80 changes: 71 additions & 9 deletions surepy/surecli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from rich.table import Table

from surepy import Surepy, __name__ as sp_name, __version__ as sp_version, console, natural_time
from surepy.entities.devices import Flap, SurepyDevice
from surepy.entities.devices import Flap, SurepyDevice, Feeder
from surepy.entities.pet import Pet
from surepy.enums import Location, LockState

Expand Down Expand Up @@ -414,15 +414,14 @@ async def locking(ctx: click.Context, device_id: int, mode: str, token: str | No
else:
return

if lock_state:
console.print(f"setting {flap.name} to '{state}'...")
console.print(f"setting {flap.name} to '{state}'...")

if await sp.sac._set_lock_state(device_id=device_id, mode=lock_state) and (
device := await sp.get_device(device_id=device_id)
):
console.print(f"✅ {device.name} set to '{state}' 🐾")
else:
console.print(f"❌ setting to '{state}' may have worked but something is fishy..!")
if await sp.sac._set_lock_state(device_id=device_id, mode=lock_state) and (
device := await sp.get_device(device_id=device_id)
):
console.print(f"✅ {device.name} set to '{state}' 🐾")
else:
console.print(f"❌ setting to '{state}' may have worked but something is fishy..!")

# await sp.sac.close_session()

Expand Down Expand Up @@ -522,6 +521,69 @@ async def position(

# await sp.sac.close_session()

@cli.command()
@click.pass_context
@click.option(
"-d", "--device", "device_id", required=True, type=int, help="id of the sure petcare device"
)
@click.option("-p", "--pet", "pet_id", required=False, type=int, help="id of the pet")
@click.option(
"-m",
"--mode",
required=True,
type=click.Choice(["add", "remove", "list"]),
help="assignment action",
)
@click.option(
"-t", "--token", required=False, type=str, help="sure petcare api token", hide_input=True
)
@coro
async def feederassign(ctx: click.Context, device_id: int, mode: str, pet_id: int | None = None, token: str | None = None) -> None:
"""feeder pet assignment"""

token = token if token else ctx.obj.get("token", None)

sp = Surepy(auth_token=str(token))

if (feeder := await sp.get_device(device_id=device_id)) and (type(feeder) == Feeder):

pets: list[Pet] = await sp.get_pets()

if mode == "list":
table = Table(box=box.MINIMAL)
table.add_column("ID", style="bold")
table.add_column("Name", style="")
table.add_column("Created At", style="")
for tag in feeder.tags.values():
for pet in pets:
if tag.id == pet.tag_id:
table.add_row(
str(pet.id),
str(pet.name),
str(datetime.fromisoformat(tag.created_at())),
)
console.print(table, "", sep="\n")
if mode == "add":
for pet in pets:
if pet.id == pet_id:
for tag in feeder.tags.values():
if tag.id == pet.tag_id:
console.print(f"Pet is already assigned to this feeder.")
return
if await sp.sac._add_tag_to_device(device_id=device_id, tag_id=pet.tag_id):
console.print(f"✅ {pet.name} added to '{feeder.name}' 🐾")
if mode == "remove":
for pet in pets:
if pet.id == pet_id:
for tag in feeder.tags.values():
if tag.id == pet.tag_id:
if await sp.sac._remove_tag_from_device(device_id=device_id, tag_id=pet.tag_id):
console.print(f"✅ {pet.name} removed from '{feeder.name}' 🐾")
return
console.print("Pet is not assigned to this feeder.")
else:
return
# await sp.sac.close_session()

if __name__ == "__main__":
cli(obj={})

0 comments on commit 83782cd

Please sign in to comment.