Протокол ISCP/eISCP от Onkyo: управление устройствами Onkyo по сети
Я уверен, что многие из читателей Хабра знают, или хотя бы слышали, об аудио-аппаратуре компании Onkyo. Современные сетевые плееры и A/V ресиверы имеют на борту Линукс, а также возможность проводного/беспроводного подключения к сети. Компания Onkyo предоставляет своё фирменное мобильное приложение для удалённого управления подобным устройством — Onkyo Controller. Информации, как это приложение работает, практически нет — есть крохи на форумах, а также несколько проектов на github.
Но можно отыскать в сети описание протокола Integra Serial Communication Protocol over Ethernet (eISCP), который и лежит в основе этого приложения. Протокол интересный. На Хабре ни одной статьи по этому протоколу найти не удалось. С одной стороны, ничего трагичного в этом нет, так как эта проприетарщина нигде, кроме Onkyo, вроде бы и не используется. С другой стороны есть шанс, что найдутся энтузиасты, которые захотят самостоятельно порулить своим плеером или ресивером Onkyo. Также статья может быть интересна тем, кто чисто из теоретического любопытства коллекционирует знания по различным сетевым протоколам. Если заинтересовал, прошу под кат.
Официальной информации по теме статьи мало. Поэтому я буду опираться не только на найденную документацию, так как она описывает исключительно команды протокола, но ничего не говорит об особенностях их использования. Много информации удалось получить как из анализа сетевого трафика с использованием tcpdump/wireshark, так и исследования прошивки устройства. Именно с этого я и начну.
Конкретная модель моего устройства не важна. Скажу только, что это сетевой плеер, похожий на тот, что на картинке для привлечения внимания. Он может проигрывать музыку не только с внешних USB-носителей, но и с музыкального сервера (DLNA), а также поддерживает интернет-радио и потоковые сервисы типа Spotify, Deezer и ещё что-то. Естественно, протокол должен всё это разнообразие поддерживать.
Анализ портов
Для того чтобы начать задавать правильные вопросы в поисковике, пришлось сначала понять, что за протокол вообще используется. То есть первый шаг — анализ портов. Итак, устройство в сети, его адрес — 192.168.1.80. Сканируем весь диапазон портов:
Открыто много чего интересного:
Анализ трафика
Теперь проверим, по какому порту и как именно общается с устройством официальное приложение. Проще всего сделать это на каком-нибудь рутованом Андроиде (но не в эмуляторе, так как официальное приложение требует наличия управляемого устройства в той же локальной подсети). Для этого:
И действительно, общение идёт по 60128 порту. Например, приложение посылает запрос на плеер:
А тот на него отвечает:
Вот мы и подошли к самой сути статьи, а именно: что же это на картинках выше за буквы такие — ISCP? Эта аббревиатура означает Integra Serial Control Protocol, изначально разработанный для управления устройствами Onkyo через порт RS-232 (есть старая интересная статья по этому поводу). Позже его расширили добавлением префикса «е» и получилось eISCP — Integra Serial Communication Protocol over Ethernet. Обе версии протокола описаны в документе «Technical Documentation: Integrated Serial Communication Protocol for AV Receiver». Первая версия документа датирована 31 октября 2012 года, последняя, которую удалось найти — 4 сентября 2017. Все версии, которые я нашёл, собраны в моём репозитории демонстрационного проекта, о котором я расскажу попозже. Дальнейшее изложение будет базироваться как на этом документе, так и на опытах с моим плеером (который, правда, в этом документе явно не упоминается).
Спецификация сообщений
Клиент (например, мобильное приложение) и устройство обмениваются короткими текстовыми сообщениями. Если в нем присутствуют цифры, то они представлены, как правило, в шестнадцатеричном виде.
Формат сообщения от клиента к устройству очень простой:
Сообщение начинается с символа «!», потом идёт код целевого устройства, после чего три буквы команды, и затем строка параметров произвольной длины. Оканчивается символом CR (0x0D) или LF (0x0A), или комбинацией CR+LF.
В зависимости от команды, устройство отвечает либо тем же типом сообщения (если это был запрос какого-либо параметра), либо другим типом или даже комбинацией сообщений (если это была команда на сложное действие типа переключения трека). Формат сообщений, посылаемых устройством на клиента, такой же. Отличие только в последнем байте:
В документе описания протокола содержится больше сотни различных команд, моё устройство поддерживает чуть больше 30 команд из этого документа. То есть как набор команд, так и допустимые параметры зависят от конкретного устройства.
Команды можно сгруппировать в логические группы. В качестве примера, я бы выделил такие:
С точки зрения параметров, все сообщения можно разделить на две группы. В первую группу входит большая часть сообщений. Для этой группы строка параметров содержит данные в буквенном или шестнадцатеричном виде и разбирается побайтно. Например, при переходе в сервис TuneIn Radio плеер высылает сообщение NLT — информацию о заголовке текущего списка с параметром «0E01000000090100FF0E00TuneIn Radio», который, будучи декодирован в соответствии со спецификацией, даёт такую информацию:
Практически все сообщения имеют параметр «QSTN», например «!1NLTQSTN». Этот запрос означает просьбу к плееру вернуть актуальную статусную информацию, соответствующую этому типу сообщений. Работает практически всегда, но есть редкие исключения, когда плеер, в зависимости от своего внутреннего настроения, игнорирует такие запросы.
Вторая группа — это сообщения, где параметром является XML, который нужно разбирать с использованием XML-парсера. Из примера выше, находясь с разделе TuneIn Radio, можно послать запрос NLA, на который ответом придёт информация об активном списке в формате XML:
То есть плеер не только предоставляет текстовую информацию (которая, кстати, отображается в данный момент на дисплее самого плеера), но также и советует адекватную иконку (папка, музыкальный трек, проигрываемый в данный момент трек).
В некоторых случаях плеер хочет показать в клентском приложении текстовое сообщение или запросить дополнительные параметры типа имени пользователя. Для этого плеер посылает универсальное сообщение NCP (универсальный диалог), где в XML описана структура того, что нужно показать пользователю:
В ответ плеер ожидает это же самое сообщение с заполненными полями (или нажатой кнопкой).
Также в XML формате представлено достаточно важное сообщение NRI — общая информация о плеере. Сообщение достаточно большое, поэтому прячу его под спойлер.
Набор команд, который придётся задействовать для управления устройством, во многом зависит от того, что находится с секциях zonelist и controllist этого сообщения.
ISCP over Ethernet (eISCP)
Сообщения в том виде, как я писал выше, предназначены для передачи по кабелю (RS-232). Старые модели ресиверов оснащались для этого 9-контактным разъёмом RS-232. Когда же вместо этого разъёма стали использовать подключение к сети (проводное или беспроводное), то пришлось завернуть эти сообщения в обёртку для передачи по TCP/IP. Так появился протокол eISCP, где ISCP-сообщение завёрнуто в такой пакет:
Под спойлером код процедуры, которая полностью формирует такой пакет для сообщения с заданным кодом (переменная code), сформированной строкой параметров (переменная parameters) и заданной версией протокола (переменная version). Так как процедура достаточно простая, то, мне кажется, код на Джаве скажет много больше, чем тысячи слов.
Если кому интересно, вот здесь находится мой пример реализации протокола для спецификации версии 1.40. Дам также ссылку на этот репозиторий. В нем реализована библиотека сообщений и утилита командной строки на Питоне, а также есть ссылки на другие аналогичные проекты.
Реализация обмена информацией
Сами сообщения, изначально разработанные для передачи по низкоскоростному кабелю, достаточно маленькие. Более того, ещё и сам плеер достаточно скромен — на фоне огромного объёма статистики, отсылаемой куда-то на сервера условного «Амазона», объём информации, которую плеер добровольно отдаёт клиенту по ISCP, просто мизерный. В спецификации протокола нет ни слова о том, когда и при каких условиях плеер посылает ту или иную информацию. Поэтому здесь пришлось достаточно долго экспериментировать самому, чтобы мобильный клиент всегда имел всю необходимую информацию о текущем состоянии устройства.
В целом, общение с плеером строится по схеме запрос/ответ. Причём в определённых ситуациях одним запросом ограничится не получится. Для моего плеера есть несколько ключевых событий, которые нужно обрабатывать:
Пример приложения
В качестве примера дам ссылку на мой репозиторий с Андроид-приложением для дистанционного управления плеером Onkyo NS-6130. Есть шанс, что оно будет также работать с Onkyo NS-6170. Но использовать его с каким-нибудь ресивером Onkyo не получится, так как весь интерфейс приложения заточен именно на воспроизведение и управление фонотекой, чего на ресиверах, как правило, нет. Поэтому у меня нет планов как-нибудь это приложение распространять, здесь я пишу о нём только в качестве примера реализации данного протокола.
Структура приложения простейшая, дизайн минималистический. В наличии всего три вкладки:
В отличие от фирменного приложения, оно в 10 раз меньше, более отзывчивое, поддерживает альбомную ориентацию экрана и различные темы оформления. Весь спектр именно моих задач оно покрывает полностью, хотя есть, и куда расширяться. Однако, также в отличие от фирменного приложения, оно не универсальное.
Если после прочтения этой статьи кто-то из владельцев Onkyo устройств захочет поэкспериментировать со своим экземпляром, я надеюсь, что этот материал и мой пример приложения снизят порог вхождения в тему.
Источник