Неофициальная приборная панель для Haval H9 с головным устройством на Android 9
и дополнительным экраном Display ID 2 размером 1920×720.
- Package name:
net.adminrunet.h9cluster - Разработчик:
admin.ru.net - Контакт: arkasha18@gmail.com
- Модель распространения: source-available
Проект не связан с Great Wall Motor, Haval или производителем головного устройства и не одобрен ими. Названия и товарные знаки принадлежат их правообладателям.
Доступны восемь тем. Porcelain Fluid и APEX SPORT показаны нативным Canvas-рендером Robolectric,
Sculpted Titanium — контрольным рендером с примерными данными,
ION AURORA — в Demo с имитацией системных индикаторов;
остальные снимки получены на реальном Display ID 2. Разрешение —
1920×720, изображения открываются по клику.
| Classic | Sport |
|---|---|
| Horizon | Simple |
|---|---|
Описание особенностей каждой темы и полноразмерная галерея приведены в документации по скинам.
- независимые темы
Classic,Sport,Horizon,Simple,ION AURORA,Sculpted Titanium,APEX SPORTиPorcelain Fluid; - белая керамика с перламутровыми переливами, жидкостные приборы, бегущий свет
вдоль шкал и компактные вспомогательные показания в
Porcelain Fluid; - круглые спортивные приборы, графические указатели топлива и ОЖ,
широкая карта в
APEX SPORT; - рельефное титановое оформление с янтарной подсветкой, широким центром
для карты и плавным переходом к ней в
Sculpted Titanium; - плавный тахометр со стабилизированными цифрами в
Sculpted Titanium,ION AURORA,APEX SPORTиPorcelain Fluid; - анимированные барабанные шкалы скорости и оборотов в
ION AURORA; - отдельно откалиброванные по печатным делениям стрелки спидометра и
тахометра в темах
ClassicиSport; - общий выбор показаний спидометра: реальная скорость или заводской стиль
+4,5%либо+5 км/ч; режим действует на все темы и меняет указатель и цифровое значение вместе; - скорость, обороты, пробег, топливо и запас хода;
- общий числовой объём бака от
20до200 л,80 лпо умолчанию: литры и шкалы топлива во всех восьми темах учитывают выбранный объём; штатный запас хода продолжает поступать от автомобиля; - температура охлаждающей жидкости и наружного воздуха;
- температура масла автоматической трансмиссии;
- угол рулевого колеса;
- давление в четырёх шинах;
- четыре отдельные скорости колёс;
- момент маховика, средний и мгновенный расход топлива и напряжение бортовой сети;
- зелёный индикатор подключённого Wi-Fi в темах
Classic,Sport,Horizon,Simple,ION AURORA,Sculpted Titanium,APEX SPORTиPorcelain Fluid; - часы под штатной передачей в согласованных рамках в
ION AURORA,ClassicиSport; текущая дата дополнительно отображается вION AURORA; вSculpted Titaniumвремя и дата расположены слева вверху; - выбор темы на основном экране;
- общая галочка
Номер передачи в темево всех восьми скинах: можно скрыть дополнительную цифру, если её уже показывает штатная панель автомобиля; по умолчанию отображение включено; - вариант
Штатная панель: приложение ничего не рисует и наDisplay ID 2остаётся заводская приборная панель; - независимые настройки конкретной темы, если она предоставляет собственный редактор;
- штатный QNX-блок предупреждений появляется поверх пользовательской панели через откалиброванное прозрачное окно;
- встроенная проверка новых GitHub Releases в production-сборке с показом описания, загрузкой APK и установкой только после подтверждения пользователя;
- ручная проверка обновления из общего экрана настроек всех тем с явным сообщением, когда установлена актуальная версия;
- кнопка
Выйти из приложенияна экране настроек: панель наDisplay ID 2закрывается, чтение автомобильных данных прекращается; - автоматический запуск панели на
Display ID 2.
Основные автомобильные данные читаются через штатный Binder-сервис:
com.gwm.android.adapter.server.GwmAdapterService
Приложение использует только операции чтения, регистрацию слушателя и снятие регистрации. Команды управления автомобилем, запись параметров и активные диагностические запросы не выполняются.
Для высокочастотного чтения оборотов и накопительного счётчика топлива
8000
ECM2
используется read-only подписка на svc://fdbus_mcu_ipc. Из изменения
счётчика приложение рассчитывает мгновенный расход: в литрах в час на месте и
в литрах на 100 км в движении. Необходимые ARM64-библиотеки из штатной системы
головного устройства находятся в app/src/main/jniLibs/arm64-v8a/ и
включаются в APK. Это сторонние бинарные компоненты; лицензия проекта на них
не распространяется. Подробности приведены в
THIRD_PARTY_NOTICES.md.
Если совместимые нативные библиотеки отсутствуют или не загружаются, приложение автоматически продолжает работу и получает RPM через Binder; тахометр при этом может обновляться реже, а мгновенный расход может быть недоступен.
Production-сборка при открытии экрана настроек проверяет последний релиз через
GitHub API. Новая версия скачивается только после подтверждения пользователя.
Перед передачей APK системному установщику приложение проверяет ожидаемое имя и
HTTPS-адрес GitHub, имя пакета, увеличение versionCode и совпадение сертификата
подписи с уже установленной версией. В Debug- и Demo-сборках проверка отключена.
Температура масла АКПП и независимые статусы двигателя/Park читаются без
CAN-команд и UDS-запросов из уже обновляемого штатным ПО снимка
/dev/shm/can_data_collect на TBOX. Приложение подключается к TBOX только для
выполнения команды чтения; firewall, файлы и настройки TBOX не изменяются.
Пароль TBOX не хранится в репозитории. Перед production-сборкой передайте его как Gradle property либо переменную окружения:
H9_TBOX_PASSWORD='...' ./gradlew assembleReleaseДля GitHub Actions используйте Repository Secret H9_TBOX_PASSWORD. Во время
сборки пароль преобразуется в две случайно маскированные последовательности,
поэтому открытой строки в DEX нет. Без секрета APK собирается нормально, но
источник температуры АКПП остаётся отключённым.
Маскирование защищает только от простого поиска строки и не является криптографическим хранилищем: автономный APK технически содержит всё необходимое для восстановления секрета во время работы. Не используйте в публичных сборках пароль, применяемый для других систем.
Значение для интерфейса приходит в общем ClusterState:
transmissionTemperatureC— температура в градусах Цельсия;hasTransmissionTemperature()—false, пока первое значение не получено;transmissionTemperatureUpdatedAtMs— время последнего обновления поSystemClock.elapsedRealtime().
Для разовой проверки масштаба одометра и среднего расхода поездки на машине есть отдельный безопасный и ограниченный по частоте тег. По умолчанию он выключен во всех сборках, включая release, и включается только явным opt-in:
adb shell setprop log.tag.H9TripTelemetry DEBUG
adb logcat -s H9TripTelemetry:DОн выводит только числовые значения текущего счётчика поездки, преобразованного
расстояния, среднего расхода cur_journey_avg_fuel_consumption_a с признаками
валидности, оборотов, скорости и monotonic timestamp — не чаще одного раза в
секунду. Этот диагностический лог не выводит мгновенный расход. На голове
car.basic.instant_fuel_consumption отдаёт NaN, поэтому интерфейс получает
мгновенный расход отдельно из read-only счётчика ECM2.
Чтобы снова выключить лог:
adb shell setprop log.tag.H9TripTelemetry ""Перед публикацией фрагментов logcat убедитесь, что в них нет других тегов с VIN, координатами или служебными данными автомобиля.
Production-версия ведёт отдельный структурированный кольцевой журнал для
редких проблем завершения поездки и источников автомобильных данных. В нём
фиксируются жизненный цикл панели, решение детектора двигателя, состояние
FLAG_KEEP_SCREEN_ON, доступность сигналов GWM Adapter, переключение скорости
между Binder и MCU, статистика FDBus, а также подключение, чтение и разбор
температуры, двигателя, Park и EPB из TBOX. Сырые CAN/FDBus-пакеты и пароль
TBOX не записываются. Для диагностики недоступной температуры сохраняются
только метка поколения снимка и десять производных 8-битных температурных
кодов без остальных байтов записей; событие пишется при смене статуса и в
редкой периодической сводке. Чтение ограничено 4097 байтами, чтобы определить
фактический размер альтернативного формата без неограниченного дампа.
На экране настроек показан код вида H9-XXXXX-XXXXX. Приложение пытается
получить VIN только через штатную read-only операцию GET_DATA, строит из него
короткий хеш-код и сразу отбрасывает исходное значение. VIN не сохраняется и не
отправляется. Пока VIN недоступен, интерфейс явно показывает резервный код
установки; после успешного чтения он автоматически заменяется VIN-кодом.
Кнопка Отправить логи вручную передаёт на
https://logs.admin.ru.net/api/v1/uploads только последние 24 часа. Локальное
кольцо ограничено 2 МиБ, gzip-запрос — 1 МиБ. После успешной отправки водитель
сообщает показанный код машины, по которому оператор находит журнал. Debug и
Demo не показывают кнопку и ничего автоматически не отправляют.
Чтобы журнал не создавал постоянную нагрузку, периодические срезы источников и состояния панели записываются не чаще одного раза в минуту, а успешное чтение TBOX — не чаще одного раза в пять минут. Ошибки, восстановления и переходы завершения поездки фиксируются сразу. Фоновой отправки, очереди и повторных сетевых попыток нет.
Карточка ATF выводится во всех зарегистрированных темах справа от защищённой
зоны штатного правого поворотника. Если значение не получено либо не
обновлялось более
15 секунд, интерфейс показывает — °C.
Цвет числа и рамки карточки помогает заметить рост температуры:
- до
99 °C— штатный белый цвет; 100–109 °C— жёлтый;110–119 °C— оранжевый;- от
120 °C— красный.
Для защиты от переключения цветов около границы используется гистерезис:
возврат из красного диапазона происходит ниже 115 °C, из оранжевого — ниже
107 °C, из жёлтого — ниже 97 °C. Это информационные пороги интерфейса, а
не замена штатному индикатору перегрева и требованиям руководства автомобиля.
Карточка GEAR показывает положение селектора: P — паркинг, R — задний
ход, N — нейтраль. В положении движения вперёд и в ручном режиме к букве
добавляется номер включённой ступени, например D3 и M2; пока номер не
получен, остаётся одна буква. Числовые коды положений, которые отдаёт штатный
адаптер, сняты на автомобиле и перечислены в GearSelector.
Исключение — темы Simple, ION AURORA, Classic и Sport: они рисуют
только номер ступени 1–8 в положении D.
В остальных положениях поле пустое, чтобы не дублировать заводскую надпись
рядом с этим местом.
Docker-образ содержит JDK 17, Android SDK 35 и Python-зависимости инструментов создания тем. Исходники, APK и ключи подписи в образ не копируются.
На компьютерах Apple Silicon также указывайте linux/amd64, поскольку Android
Build Tools внутри контейнера рассчитаны на эту архитектуру:
docker build --platform linux/amd64 -t h9-cluster-build .
docker run --rm --platform linux/amd64 \
-v "$PWD:/workspace" -w /workspace \
h9-cluster-buildПо умолчанию контейнер выполняет assembleDebug и lint. Готовый образ из
GitHub Container Registry можно использовать без локальной сборки:
docker pull --platform linux/amd64 \
ghcr.io/arkasha18/h9-cluster-build:android35-jdk17
docker run --rm --platform linux/amd64 \
-v "$PWD:/workspace" -w /workspace \
ghcr.io/arkasha18/h9-cluster-build:android35-jdk17Требуются:
- JDK 17;
- Android SDK Platform 35;
- Android SDK Build Tools 35;
- ARM64-устройство с Android 9 для проверки в автомобиле.
Debug APK собирается без ключей:
./gradlew assembleDebugРезультат:
app/build/outputs/apk/debug/app-debug.apk
Автономный Demo APK с автоматически меняющимися тестовыми значениями:
./gradlew assembleDemoРезультат:
app/build/outputs/apk/demo/app-demo.apk
Demo не подключается к GWM Adapter, FDBus, CAN или TBOX. Для проверки на одном экране эмулятора:
adb shell settings delete global overlay_display_devices
adb shell wm size 1920x720
adb shell wm density 240
./gradlew installDemo
adb shell am start -W \
-n net.adminrunet.h9cluster.demo/net.adminrunet.h9cluster.SettingsActivityКнопка сохранения откроет приборную панель на том же экране. Чтобы проверить экран итогов поездки:
- подождите не менее секунды, пока будет подтверждён запуск двигателя;
- нажмите на левый спидометр;
- подождите 1,5 секунды подтверждения остановки;
- проверьте расстояние, средний расход и длительность;
- нажмите на экран итогов, чтобы вернуться к настройкам.
Клик вне левого спидометра и системная кнопка Back также возвращают настройки. Чтобы вернуть стандартный размер эмулятора:
adb shell wm size reset
adb shell wm density resetДля двухэкранной проверки:
adb shell settings put global overlay_display_devices '1920x720/240'
adb shell am force-stop net.adminrunet.h9cluster.demo
adb shell am start -W \
-n net.adminrunet.h9cluster.demo/net.adminrunet.h9cluster.SettingsActivityПри наличии Display ID 2 приборная панель автоматически откроется на нём.
Для воспроизводимой проверки недоступного расхода можно напрямую запустить Demo preview с тестовым параметром:
adb shell am start -W \
-n net.adminrunet.h9cluster.demo/net.adminrunet.h9cluster.PreviewActivity \
--ez demo_invalid_consumption trueПосле остановки средний расход и израсходованное топливо будут показаны как
—, а расстояние и длительность останутся числовыми.
Итоги поездки считаются только по двум штатным read-only сигналам:
car.basic.cur_journey_odometer для расстояния и
car.basic.cur_journey_avg_fuel_consumption_a для среднего расхода, откуда
получается израсходованное топливо. Показатель car.basic.avg_fuel_consumption
(индикатор B) используется только шкалой приборки и никогда не подменяет A в
итогах: если A недоступен, невалиден или источник перестал публиковать значение,
расход и топливо показываются как —.
Поездка завершается после 1,5 секунды непрерывного подтверждения одним из двух
read-only путей: фактически полученный RPM ≤ 50 с нулевой скоростью либо
свежие состояния TBOX engine stopped + Park. Последняя нулевая скорость
сохраняется до 60 секунд, чтобы принять запаздывающий нулевой RPM после
затихания шины; новое движение сразу отменяет её. Свежий TBOX-статус не-Park
защищает оба пути от ложного завершения при работе start-stop в D/N. Само по
себе отсутствие обновлений RPM остановкой двигателя не считается.
При удалённом запуске неизменившийся ноль может не публиковаться ни GWM, ни
событийным ABS3. Пока измеренная скорость недоступна, все темы показывают 0
только при свежем сочетании TBOX engine running + Park. Это исключительно
визуальная подстановка: внутри источника остаётся speedKph=-1, и детектор
поездки не получает ложного подтверждения остановки.
Даже если H9_TBOX_PASSWORD настроен локально, Demo variant принудительно
оставляет поля секрета пустыми. Проверка unit-контракта, APK и lint:
./gradlew testDemoUnitTest lintDemo assembleDemo
tools/verify_demo_apk_secrets.sh \
app/build/outputs/apk/demo/app-demo.apkДля локальной контрольной release-сборки используйте production-ключ из
пользовательского ~/.gradle/gradle.properties, который находится за
пределами репозитория:
H9_CLUSTER_STORE_FILE=/absolute/path/to/your-release-key.p12
H9_CLUSTER_STORE_TYPE=PKCS12
H9_CLUSTER_STORE_PASSWORD=your-password
H9_CLUSTER_KEY_ALIAS=your-key-alias
H9_CLUSTER_KEY_PASSWORD=your-passwordПосле этого:
./gradlew assembleReleaseФайлы ключей, локальные свойства, APK/AAB и каталоги сборки исключены через
.gitignore. Автоматическая публикация использует отдельный защищённый GitHub
Environment release: ключ выдаётся только signing job после личного
подтверждения владельца, не передаётся Gradle/Docker и не попадает в checkout
или Actions artifacts.
Порядок выпуска подписанной версии приведён в docs/RELEASING_RU.md.
GitHub Actions для каждого Pull Request:
- собирает зафиксированный Docker toolchain;
- проверяет генерацию Forum Kit и обработку графики темы;
- тестирует, проверяет lint и собирает Debug и Demo variants;
- проверяет отсутствие TBOX secret material в Demo APK;
- сохраняет Debug и Demo APK отдельными временными Actions artifacts;
- после попадания проверенного commit в
mainпубликует toolchain в GHCR.
Отдельный workflow Production release никогда не запускается от обычного
коммита в main. Только Arkasha18 может вручную проверить либо опубликовать
точную версию и полный commit SHA. Production-сборка, подпись, сверка
сертификата и создание Immutable Release выполняются раздельными job; перед
доступом к signing secrets требуется подтверждение Environment release.
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -W -n net.adminrunet.h9cluster/.SettingsActivity
adb shell am start --display 2 -W \
-n net.adminrunet.h9cluster/.PreviewActivityПроверка подключения к штатному Adapter и TBOX:
adb logcat -s GwmClusterDataSource FdbusRpmReader TransmissionTempДля успешного Binder- и TBOX-подключения в журнале появляются:
GWM adapter connected; live listener active
Connected to read-only TBOX temperature source
На экране настроек внизу расположена кнопка Выйти из приложения. После
подтверждения закрывается окно панели на Display ID 2, останавливается чтение
автомобильных данных и закрывается сам экран настроек. adb shell am force-stop
для этого больше не нужен.
Выход запоминается: после перезагрузки головного устройства панель не запускается автоматически. Автозапуск восстанавливается, как только приложение открывается вручную.
Сохранённая тема и её настройки при выходе не изменяются. Если нужно убрать
наложение, но оставить приложение работающим, выберите тему Штатная панель.
В репозиторий не входят:
- ключи подписи и пароли;
- собранные APK/AAB;
- дампы автомобиля и сетевые снимки;
- заводские APK, ODEX/VDEX и конфигурации;
- декомпилированный smali-код;
- TBOX-бинарники и диагностические инструменты;
- локальные исследовательские отчёты и исходные диагностические материалы.
Эти материалы не нужны для компиляции рабочего приложения. Соответствующие
локальные каталоги перечислены в .gitignore.
Каталог docs/ содержит:
- инструкцию подготовки JDK, Android SDK, Python и локального Codex;
- процесс «компоновка → оформление и подгонка → нативный Demo → реальные данные», полный состав показаний и готовые задания для разработки темы;
- актуальный шаблон
1920×720с откалиброванной на автомобиле чёрной подложкой и точными полями v2.4: строгие нижние левые зоны отделены от полей, под которыми разрешён декор; есть цветное наложение иsystem_fields.json(каталог, ZIP); - учебный автономный Demo v8.0.7 в
H9_Cluster_Dashboard_Demo_Base_Source_v3.zip; для нового скина используйте build typedemoтекущего репозитория, чтобы сохранить актуальный контракт; H9_Cluster_Codex_Forum_Kit_v3.zipдля передачи полного комплекта другому разработчику.
Каталог tools/ содержит воспроизводимые Python-инструменты:
build_forum_design_template.py— пересобирает актуальный комплект шаблона без изменения публичных имён файлов;package_skin_design_kit.py— синхронизирует три публичных ZIP с документацией и шаблоном;--checkпроверяет содержимое без записи;edit_classic_background.py— формирует подложку и независимый overlay Classic с утверждённой геометрией.
Для запуска инструментов нужны Python 3, Pillow и NumPy:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-tools.txtПодробный процесс описан в docs/FORUM_DESIGN_PROMPTS_RU.md.
- ошибки и предложения: Issues;
- вопросы, идеи и демонстрация тем: Discussions;
- правила участия: CONTRIBUTING.md;
- история версий: CHANGELOG.md;
- уязвимости: SECURITY.md.
Перед отправкой изменений ознакомьтесь с правилами сообщества. В Pull Request автоматически проверяются сборка и Android Lint.
Шрифты, Gradle Wrapper и нативные .so являются сторонними компонентами и не
переводятся под PolyForm. Полный перечень приведён в
THIRD_PARTY_NOTICES.md.
Код и оригинальные ресурсы проекта, права на которые принадлежат
admin.ru.net, доступны по
PolyForm Noncommercial License 1.0.0.
Некоммерческое использование, изменение и распространение разрешены бесплатно на условиях лицензии. Коммерческое использование требует отдельного письменного соглашения с правообладателем: arkasha18@gmail.com.
Это source-available, а не OSI Open Source проект. Сторонние компоненты остаются под собственными лицензиями.
Программное обеспечение предоставляется «как есть». Использование приложения в автомобиле выполняется на риск пользователя. Перед поездкой необходимо убедиться, что панель не мешает штатным предупреждениям и обязательным индикаторам автомобиля.