Как стать автором
Обновить
108.43
InlyIT
Для старательного нет ничего невозможного

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

Время на прочтение5 мин
Количество просмотров9.4K
Автор оригинала: John Partee
Я смеюсь над этой историей все выходные, так что не могу не поделиться. Засидевшись до поздней ночи на 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 (серьезно, он обалденный)
Теги:
Хабы:
+18
Комментарии9

Публикации

Информация

Сайт
inlyit.com
Дата регистрации
Дата основания
Численность
31–50 человек
Местоположение
Россия