Esempio n. 1
0
async def position(
    ctx: click.Context, pet_id: int, position: str, token: str | None = None
) -> None:
    """set pet position"""

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

    sp = Surepy(auth_token=str(token))

    pet: Pet | None
    location: Location | None

    if (pet := await sp.pet(pet_id=pet_id)) and (type(pet) == Pet):

        if position == "in":
            location = Location.INSIDE
        elif position == "out":
            location = Location.OUTSIDE
        else:
            return

        if location:
            if await sp.sac.set_position(pet.id, location):
                console.print(f"{pet.name} set to '{location.name}' 🐾")
            else:
                console.print(
                    f"setting to '{location.name}' probably worked but something else is fishy...!"
                )
Esempio n. 2
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up."""

    hass.data.setdefault(DOMAIN, {})

    try:
        surepy = Surepy(
            entry.data[CONF_USERNAME],
            entry.data[CONF_PASSWORD],
            auth_token=entry.data[CONF_TOKEN]
            if CONF_TOKEN in entry.data else None,
            api_timeout=SURE_API_TIMEOUT,
            session=async_get_clientsession(hass),
        )
    except SurePetcareAuthenticationError:
        _LOGGER.error(
            "🐾 \x1b[38;2;255;26;102m·\x1b[0m unable to auth. to surepetcare.io: wrong credentials"
        )
        return False
    except SurePetcareError as error:
        _LOGGER.error(
            "🐾 \x1b[38;2;255;26;102m·\x1b[0m unable to connect to surepetcare.io: %s",
            error,
        )
        return False

    spc = SurePetcareAPI(hass, entry, surepy)

    async def async_update_data():

        try:
            # asyncio.TimeoutError and aiohttp.ClientError already handled

            async with async_timeout.timeout(20):
                return await spc.surepy.get_entities(refresh=True)

        except SurePetcareAuthenticationError as err:
            raise ConfigEntryAuthFailed from err
        except SurePetcareError as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err

    spc.coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="sureha_sensors",
        update_method=async_update_data,
        update_interval=timedelta(seconds=150),
    )

    await spc.coordinator.async_config_entry_first_refresh()

    hass.data[DOMAIN][SPC] = spc

    return await spc.async_setup()
Esempio n. 3
0
async def pets(ctx: click.Context, token: str | None) -> None:
    """get pets"""

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

    async with ClientSession(connector=TCPConnector(ssl=False)) as session:
        sp = Surepy(auth_token=token, session=session)

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

        table = Table(box=box.MINIMAL)
        table.add_column("Name", style="bold")
        table.add_column("Where", justify="right")
        table.add_column("Feeding A", justify="right", style="bold")
        table.add_column("Feeding B", justify="right", style="bold")
        table.add_column("Lunch Time", justify="right", style="bold")
        table.add_column("Drinking", justify="right", style="bold")
        table.add_column("Drink Time", justify="right", style="bold")
        table.add_column("ID 👤 ", justify="right")
        table.add_column("Household 🏡", justify="right")

        for pet in pets:

            feeding_a = feeding_b = lunch_time = None
            drinking_change = drink_time = None

            if pet.feeding:
                feeding_a = f"{pet.feeding.change[0]}g"
                feeding_b = f"{pet.feeding.change[1]}g"
                lunch_time = pet.feeding.at.time() if pet.feeding.at else None
            if pet.drinking:
                drinking_change = f"{pet.drinking.change[0]}ml"
                drink_time = pet.drinking.at.time() if pet.drinking.at else None

            table.add_row(
                str(pet.name),
                str(pet.location),
                f"{feeding_a}",
                f"{feeding_b}",
                str(lunch_time),
                f"{drinking_change}",
                str(drink_time),
                str(pet.pet_id),
                str(pet.household_id),
            )

        console.print(table, "", sep="\n")
Esempio n. 4
0
async def token(ctx: click.Context, user: str, password: str) -> None:
    """get a token"""

    surepy_token: str | None = None

    with Halo(text="fetching token", spinner="dots", color="magenta") as spinner:
        async with ClientSession(connector=TCPConnector(ssl=False)) as session:
            sp = Surepy(email=user, password=password, session=session)

            if surepy_token := await sp.sac.get_token():

                spinner.succeed("token received!")

                if token_file.exists() and token != token_file.read_text(encoding="utf-8"):
                    copyfile(token_file, old_token_file)

                token_file.write_text(surepy_token, encoding="utf-8")
Esempio n. 5
0
async def locking(
    ctx: click.Context, device_id: int, mode: str, token: str | None = None
) -> None:
    """lock control"""

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

    sp = Surepy(auth_token=str(token))

    flap: Flap | None
    if (flap := await sp.flap(flap_id=device_id)) and (type(flap) == Flap):

        lock_control: Callable[..., Coroutine[Any, Any, Any]] | None = None

        if mode == "lock":
            lock_control = flap.lock
            state = "locked"
        elif mode == "in":
            lock_control = flap.lock_in
            state = "locked in"
        elif mode == "out":
            lock_control = flap.lock_out
            state = "locked out"
        elif mode == "unlock":
            lock_control = flap.unlock
            state = "unlocked"
        else:
            return

        if lock_control:
            with Halo(
                text=f"setting {flap.name} to '{state}'", spinner="dots", color="red"
            ) as spinner:

                if await lock_control(device_id=device_id) and (
                    device := await sp.flap(flap_id=device_id)
                ):
                    spinner.succeed(f"{device.name} set to '{state}' 🐾")
                else:
                    spinner.fail(
                        f"setting to '{state}' probably worked but something else is fishy...!"
                    )
Esempio n. 6
0
 def __init__(self, entry: ConfigEntry, hass: HomeAssistant) -> None:
     """Initialize the data handler."""
     self.surepy = Surepy(
         entry.data[CONF_USERNAME],
         entry.data[CONF_PASSWORD],
         auth_token=entry.data[CONF_TOKEN],
         api_timeout=SURE_API_TIMEOUT,
         session=async_get_clientsession(hass),
     )
     self.lock_states_callbacks = {
         LockState.UNLOCKED.name.lower(): self.surepy.sac.unlock,
         LockState.LOCKED_IN.name.lower(): self.surepy.sac.lock_in,
         LockState.LOCKED_OUT.name.lower(): self.surepy.sac.lock_out,
         LockState.LOCKED_ALL.name.lower(): self.surepy.sac.lock,
     }
     super().__init__(
         hass,
         _LOGGER,
         name=DOMAIN,
         update_interval=SCAN_INTERVAL,
     )
Esempio n. 7
0
async def is_valid(hass: core.HomeAssistant,
                   user_input: dict[str, Any]) -> str | None:
    """Check if we can log in with the supplied credentials."""

    try:
        surepy = Surepy(
            user_input[CONF_USERNAME],
            user_input[CONF_PASSWORD],
            auth_token=None,
            api_timeout=SURE_API_TIMEOUT,
            session=async_get_clientsession(hass),
        )

        return await surepy.sac.get_token()

    except SurePetcareAuthenticationError:
        _LOGGER.error(
            "Unable to connect to surepetcare.io: Wrong credentials!")
        return None

    except SurePetcareError as error:
        _LOGGER.error("Unable to connect to surepetcare.io: %s", error)
        return None
Esempio n. 8
0
async def notification(ctx: click.Context, token: str | None = None) -> None:
    """get notifications"""

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

    async with ClientSession(connector=TCPConnector(ssl=False)) as session:
        sp = Surepy(auth_token=token, session=session)

        json_data = await sp.get_notification() or None

        if json_data and (data := json_data.get("data")):

            table = Table(box=box.MINIMAL)

            all_keys: set[str] = set()
            all_keys.update(*[entry.keys() for entry in data])

            for key in all_keys:
                table.add_column(str(key))

            for entry in data:
                table.add_row(*([str(e) for e in entry.values()]))

            console.print(table, "", sep="\n")
Esempio n. 9
0
async def devices(ctx: click.Context, token: str | None) -> None:
    """get devices"""

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

    async with ClientSession(connector=TCPConnector(ssl=False)) as session:
        sp = Surepy(auth_token=token, session=session)

        devices: list[SurepyDevice] = await sp.get_devices()

        # await json_response(devices, ctx)

        # table = Table(title="[bold][#ff1d5e]·[/] Devices [#ff1d5e]·[/]", box=box.MINIMAL)
        table = Table(box=box.MINIMAL)
        table.add_column("ID", justify="right", style="")
        table.add_column("Household", justify="right", style="")
        table.add_column("Name", style="bold")
        table.add_column("Type", style="")
        table.add_column("Serial", style="")

        # sorted_devices = sorted(devices, key=lambda x: int(devices[x]["household_id"]))

        # devices = await sp.sac.get_devices()
        # devices = await sp.get_entities()

        for device in devices:

            table.add_row(
                str(device.id),
                str(device.household_id),
                str(device.name),
                str(device.type.name.replace("_", " ").title()),
                str(device.serial) or "-",
            )

        console.print(table, "", sep="\n")
Esempio n. 10
0
async def main():
    surepy = Surepy(auth_token=token)
    devices: List[SurepyDevice] = await surepy.get_devices()
    return devices
Esempio n. 11
0
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
    """Set up the Sure Petcare integration."""
    conf = config[DOMAIN]
    hass.data.setdefault(DOMAIN, {})

    try:
        surepy = Surepy(
            conf[CONF_USERNAME],
            conf[CONF_PASSWORD],
            auth_token=None,
            api_timeout=SURE_API_TIMEOUT,
            session=async_get_clientsession(hass),
        )
    except SurePetcareAuthenticationError:
        _LOGGER.error(
            "Unable to connect to surepetcare.io: Wrong credentials!")
        return False
    except SurePetcareError as error:
        _LOGGER.error("Unable to connect to surepetcare.io: Wrong %s!", error)
        return False

    spc = SurePetcareAPI(hass, surepy)
    hass.data[DOMAIN][SPC] = spc

    await spc.async_update()

    async_track_time_interval(hass, spc.async_update, SCAN_INTERVAL)

    # load platforms
    for platform in PLATFORMS:
        hass.async_create_task(
            hass.helpers.discovery.async_load_platform(platform, DOMAIN, {},
                                                       config))

    async def handle_set_lock_state(call):
        """Call when setting the lock state."""
        await spc.set_lock_state(call.data[ATTR_FLAP_ID],
                                 call.data[ATTR_LOCK_STATE])
        await spc.async_update()

    lock_state_service_schema = vol.Schema({
        vol.Required(ATTR_FLAP_ID):
        vol.All(cv.positive_int, vol.In(spc.states.keys())),
        vol.Required(ATTR_LOCK_STATE):
        vol.All(
            cv.string,
            vol.Lower,
            vol.In([
                # https://github.com/PyCQA/pylint/issues/2062
                # pylint: disable=no-member
                LockState.UNLOCKED.name.lower(),
                LockState.LOCKED_IN.name.lower(),
                LockState.LOCKED_OUT.name.lower(),
                LockState.LOCKED_ALL.name.lower(),
            ]),
        ),
    })

    hass.services.async_register(
        DOMAIN,
        SERVICE_SET_LOCK_STATE,
        handle_set_lock_state,
        schema=lock_state_service_schema,
    )

    return True
Esempio n. 12
0
async def report(
    ctx: click.Context, household_id: int, pet_id: int | None = None, token: str | None = None
) -> None:
    """get pet/household report"""

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

    async with ClientSession(connector=TCPConnector(ssl=False)) as session:
        sp = Surepy(auth_token=token, session=session)

        entities = await sp.get_entities()

        json_data = await sp.get_report(pet_id=pet_id, household_id=household_id)

        if data := json_data.get("data"):

            table = Table(box=box.MINIMAL)

            all_keys: list[str] = ["pet", "from", "to", "duration", "entry_device", "exit_device"]

            for key in all_keys:
                table.add_column(str(key))

            for pet in data:

                datapoints_drinking: list[dict[str, Any]] = pet.get("drinking", {}).get(
                    "datapoints", []
                )
                datapoints_feeding: list[dict[str, Any]] = pet.get("feeding", {}).get(
                    "datapoints", []
                )
                datapoints_movement: list[dict[str, Any]] = pet.get("movement", {}).get(
                    "datapoints", []
                )

                datapoints = datapoints_drinking + datapoints_feeding + datapoints_movement

                if datapoints:

                    # datapoints.sort(key=lambda x: datetime.fromisoformat(x["from"]), reverse=True)

                    for datapoint in datapoints[:25]:

                        from_time = datetime.fromisoformat(datapoint["from"])
                        to_time = (
                            datetime.fromisoformat(datapoint["to"])
                            if "active" not in datapoint
                            else None
                        )

                        if "active" in datapoint:
                            datapoint["duration"] = (
                                datetime.now(tz=from_time.tzinfo) - from_time
                            ).total_seconds()

                        entry_device = entities.get(datapoint.get("entry_device_id", 0), None)
                        exit_device = entities.pop(datapoint.get("exit_device_id", 0), None)

                        table.add_row(
                            str(entities[pet["pet_id"]].name),
                            str(from_time.strftime("%d/%m %H:%M")),
                            str(to_time.strftime("%d/%m %H:%M") if to_time else "-"),
                            str(natural_time(datapoint["duration"])),
                            str(entry_device.name if entry_device else "-"),
                            str(exit_device.name if exit_device else "-"),
                        )

            console.print(table, "", sep="\n")