Site icon AppTractor

Реверс инжиниринг кормушки для кошек для повышения производительности разработки

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

Я не смог найти USB-раздатчик конфет (я даже проверил thinkgeek, ничего близкого!), поэтому я и решил попробовать кормушку для кошек. Эта штука потрясающая, и я покажу вам, как повторить то, что я сделал.

Тизер

Обзор

REST API — это, по сути, язык Интернета. Большинство продуктов используют их для передачи данных с устройств на серверы. Я видел какой-то старый код Python, в котором уже был отработан процесс аутентификации, так что мне было с чего начать.

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

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

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

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

А это просто мило:

Они положили таблицу размеров продуктов, в которой указано, что кусочки должны быть менее 12 мм:

Но он фильтрует и порционирует такие продукты так:

via GIPHY

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

На момент написания кормушка стоит около 70 долларов, но она отлично работает и хорошо собрана.

Приложение

Приложение довольно хорошее. Хотя немного странно, если у вас нет питомца… Мне пришлось добавить себя в качестве собаки и выбрать свою «породу»:

Но после этого я смог использовать API. Несколько раз протестировав размер порции с помощью приложения я определил, что 1/10 чашки — это половина ротора, что в среднем составляет около пяти миндалин.

Слежка

Я использовал Packet Capture для Android, пока выполнял ручную подачу:

И нашел этот запрос:

Настоящей серебряной пули здесь нет, пришлось просмотреть несколько групп запросов, чтобы найти нужный. Но мы видим, что он достигает конечной точки /latest/d4/saveDailyFeed, что дает нам большой намек! Раздел URLENCODED сообщает нам, что количество, время и идентификатор устройства включены в 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()

Это выглядит сложно, но заголовки были скопированы кем-то, кто делал то же самое, что и я, с iPhone. Он использует запросы (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}
    )
)

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

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

И короткий звуковой сигнал, сопровождаемый прекрасным звуком удара вознаграждения по вашей миске (не думайте об этом слишком много, это лишь слегка унизительно):

Деплой

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

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

Наверняка вы могли бы сделать это лучше, но нет необходимости в облачном хостинге или чем-то еще для глупого маленького проекта. Это просто часть удовольствия!

Вывод

Огромное спасибо всем, кто реверсинженерил  это до меня. Оставалось только найти конечную точку и сделать запрос, что было слишком просто! Кто знает, станет ли это настоящим подспорьем в написании кода, но я не видел, чтобы кто-то делал это раньше, и я рад, что попробовал. Устранение неполадок в CI стало намного приятнее.

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

Вы увидите, что данные для этих продуктов отправляются в виде открытого текста на их серверы и в них… нет HTTPS. Это просто слишком поверхностно. Они также отправляют ваше полное местоположение, что еще более нелепо. Я бы старался избегать использования приложения настолько часто, насколько это возможно, и использовать одноразовый пароль на случай его взлома… Потому что это действительно может произойти в какой-то момент.

Источник

Exit mobile version