Я смеюсь над этой историей все выходные, так что не могу не поделиться. Засидевшись до поздней ночи на Amazon, я купил автокормушку для животных PetKit FreshElement Solo. У меня имелось две проблемы, с решением которых она могла мне помочь: во-первых, мне сложно было себя замотивировать на работу над побочным проектом, во-вторых, я всерьез подсел на миндаль в темном шоколаде с морской солью из Target. Раз уж я кодер-мартышка, так почему бы не подкормить внутреннюю мартышку, когда выдаю код?



Найти USB-механизм для раздачи сладостей мне не удалось (даже на thinkgeek заглянул – ничего похожего), поэтому я решил попробовать кошачью кормушку. Отличная вещь, а под катом я расскажу, какие шаги предпринять, чтобы получить такой же результат, как у меня.

Вот тизер. Код лежит здесь. Чтобы запустить самим, вам придется выбрать имя пользователя и пароль по этой ссылке.

Обзор


REST API – это, по сути, язык, на котором говорит интернет. У большинства продуктов общение между устройством и сервером происходит посредством их. Мне попался старый фрагмент кода на Python, в котором уже была реализован процесс авторизации, так что было от чего оттолкнуться.

Я покопался в поисках API-запроса для кормления вручную, скопировал его на Python с помощью Requests и привязал скрипт к кнопке на своем контроллере для стриминга – она же отвечает за отправку коммита командой ctl+enter.

Есть и другие способы (причем получше) получить тот же результат – AWS, вебхуки, хуки в git (хуки в git – пожалуй, самый надежный вариант), но мне хотелось, чтобы система работала строго локально, чтобы конфеты не сыпались, если я уеду куда-нибудь. Нет смысла всё усложнять! В худшем случае забуду нажать на кнопку и съем меньше сладкого.

Аппаратное обеспечение


Говорю без преувеличений: для выдачи вкусностей ничего лучше этого устройства не найдешь. Порция еды отмеряется при помощи аккуратненького механизма с силиконовым стержнем и силиконовыми лопастями, отделяющими нужное количество, которое и выпадает в лоток выдачи. Ловко придумано и для продуктов безопасно – они не крошатся и не портятся.




А это просто трогательно:



Прилагается шкала для измерения частиц еды; говорится, что размер должен быть менее 12 миллиметров. Но измерение и высыпание происходит примерно так:



поэтому даже если там все 12 миллиметров, может нормально пройти под углом. Я планирую поэкспериментировать с кусочками вяленого мяса и сырными шариками. Там гигроскопическая прокладка сверху, чтобы всё оставалось свежим… думаю, можно изощряться со спокойной душой.

На момент написания статьи такие кормушки стоили 70 $ за штуку, но работают они отлично и сделаны на совесть. Купить можно здесь.

Приложение


Приложение неплохое. Единственное, немного странно с ним работать, когда домашних животных у тебя нет… Пришлось назваться собакой и выбрать себе породу.



Зато после этого я смог воспользоваться API. После нескольких тестов приложения на предмет размера порции, я узнал, что половина оборота – это примерно десятая часть стакана, то есть в среднем пять конфет.

Изыскания


Для ручного запуска кормления я использовал Packet Capture для Android.



И нашел там вот такой запрос:



Палочки-выручалочки не обнаружилось, мне пришлось просмотреть несколько групп запросов, чтобы отыскать то, что требовалось. Но мы видим /latest/d4/saveDailyFeed в качестве конечной точки, а это существенная подсказка! Секция URLENCODED говорит нам, что amount, time и deviceId входят в URL… Значит, всё можно повторить локально! В этой статье мы с Энди описали, как это делается на десктопе.

Код


Код можно найти здесь.

Я начал с ответвления PyPetKit авторства geeks4hire. Всё, что касается авторизации, там было уже сделано за меня, оставалось только добавить выход на API. Из упомянутого выше пакета я узнал, в какую конечную точку нужно метить, и добавил непримечательную функцию для отправки запросов к API в моё ответвление пакета:

def send_api_request(self, path, method="POST", params=None, json=None):
        """
        Sends an API request.
        """
        custom_headers = {
            "X-Session": self._access_token,
            "User-Agent": "PETKIT/7.26.1 (iPhone; iOS 14.7.1; Scale/3.00)",
            "X-Timezone": f"{round(self._tzone._utcoffset.seconds/60/60)}.0",
            "X-Api-Version": "7.26.1",
            "X-Img-Version": "1",
            "X-TimezoneId": self._tzone.zone,
            "X-Client": "ios(14.7.1;iPhone13,4)",
            "X-Locale": self._locale.replace("-", "_"),
        }

        return requests.request(
            method,
            self._apiServerBaseURL + path,
            headers=custom_headers,
            params=params,
            json=json,
        ).json()


Выглядит сложно, но все заголовки скопированы у человека, который делал всё ровно то же самое, но на айфоне. Здесь используются запросы (requests.request) для создания HTTP-запроса произвольным методом. То есть то самое, что делает браузер, только на Python. В той статье про скрейпинг, о которой говорилось выше, мы разбираем это подробно – если есть возможность скопировать заголовки, это многое дает!

И вот, чтобы добраться до вожделенных конфет, только и нужно, что:

from pypetkit import PetKitAPI
from settings import (
    API_USERNAME,
    API_PASSWORD,
    API_COUNTRY_CODE,
    API_LOCALE_CODE,
    API_TIMEZONE,
)
from pprint import pprint

petkit_api = PetKitAPI(
    API_USERNAME, API_PASSWORD, API_COUNTRY_CODE, API_LOCALE_CODE, API_TIMEZONE
)

#! Sign in
petkit_api.request_token()

print(f"Authorized: {petkit_api.is_authorized}")

#! Send the actual feeding request
pprint(
    petkit_api.send_api_request(
        "d4/saveDailyFeed", params={"deviceId": 10019856, "amount": 10, "time": -1}
    )
)


Мы задействовали реализацию авторизации из существующего кода и теперь посылаем запрос. Параметры соответствуют запросу, который мы обнаружили, когда вручную активировали кормушку из приложения. Я, конечно, попытался уменьшить размер порции, но, похоже, десять единиц – минимальное количество, которое может отмерить механизм. -1, кажется, запускает немедленную выдачу еды. В приложении можно задавать часы кормления, уверен, что и даты тоже.

Если всё работает как надо, выдача с run.py будет такой:



Затем негромкий писк и восхитительный звук конфет, падающих в (не заморачивайтесь, не так уж это и унизительно) миску.

«Развертывание»


Развертывание – это, конечно, сильно сказано, но иначе не назовешь. Система работает локально, чтобы еда не поступала, когда меня нет дома. У меня есть Elgato Stream Deck, который я очень люблю, и с его помощью я создал горячую клавишу.



System:Hotkey отправляет Ctl+Enter, чтобы отослать коммит, когда я допишу. System:Open открывает python c://run.py и тем самым обеспечивает мне сладкое шоколадное подкрепление правильных программистских привычек.

Всё это абсолютно точно можно реализовать «лучше», но в облаках и всяком таком просто нет необходимости – это маленький проект чисто для смеха.

В заключение


Огромное спасибо тем, кто провел обратную разработку в отношении этого агрегата до меня. Мне только и осталось, что найти конечную точку и создать запрос – проще простого. Не знаю, подстегнет ли это меня в самом деле писать больше кода, но у других я такого еще не видел и рад, что предпринял эту попытку. Она определенно подсластила мне решение проблем с непрерывной интеграцией.

Примечание о безопасности


Как вы увидите, данные у этих продуктов отправляются простым текстом, что на сервер, что с сервера. Это очень сомнительно. Также они отправляют все сведения о местонахождении, что еще более сомнительно. Я бы посоветовал пользоваться этим приложением по минимуму и установить одноразовый пароль, на случай если произойдет утечка данных… а это рано или поздно вполне может случиться.

Ссылки


PETKIT Fresh Element Solo
Репозиторий на Github
Packet Capture для Android
Elgato Stream Deck
Миндаль в темном шоколаде с морской солью на Target (серьезно, он обалденный)

Комментарии (8)


  1. Moraiatw
    14.10.2022 06:57
    +2

    булка_хлеба_и_троллейбус.жпг


  1. DustCn
    14.10.2022 07:08
    +5

    Еще поменять кресло на колесиках на унитаз и все...


  1. v1000
    14.10.2022 09:03
    +2

    Я запрограммировал кошачью кормушку, чтобы она выдавала мне конфеты за код

    теперь вы кот, который пишет код. :)


  1. ihouser
    14.10.2022 11:11
    +3

    Хороший мотиватор для удаленщиков. Сделал коммит -- босс выдал жратвы конфет.


    1. DanilinS
      14.10.2022 12:17

      Это плохо работает. Будет лучше если и отрицательное подкрепление будет. Ошибка в коде - удар тока.


      1. ihouser
        14.10.2022 12:22

        Ну тогда для выдачи корма, программист обязан нажать кнопку. Код без ошибок -- получи конфетку, иначе -- удар током.


  1. SuperTEHb
    14.10.2022 11:42
    +1

    Делать устройство для выдачи лакомства за работу вместо занятия самой работой во имя повышения производительности. Какой очаровательный способ прокрастинации! Не воспримите как издевательство, я сам подобные прикольные штука обожаю.


  1. vtal007
    14.10.2022 15:32

    Я себя вручную подкармливаю за сделанный урок (питон или эксель) печенькой или шоколадкой