Мой блог
Как сделать интерфейс утилиты ИБП доступным для NVDA: опыт разработки дополнения PowerManagerII Access
Приобретение нового оборудования для ПК нередко оборачивается неожиданной проблемой доступности софта. Столкнувшись с тем, что штатная утилита управления источником бесперебойного питания ExeGate LLB-3000 оказалась практически непригодной для работы со скринридером NVDA, я решил исправить эту ситуацию. Без полноценного доступа к софту невозможно контролировать уровень заряда, следить за нагрузкой и управлять параметрами питания.
Принимать подобное положение вещей я не привык. Чтобы восстановить полный контроль над устройством с клавиатуры и слышать все происходящие события, возникла идея написать специализированный аддон для NVDA. В процессе разработки активным помощником выступала языковая модель Codex от OpenAI (GPT-6 Astra). ИИ помог обработать графическое меню, сгенерировал значительный объем кода и помог локализовать ошибки. Результатом этой работы стал модуль PowerManagerII Access, который позволяет полноценно взаимодействовать с фирменным софтом ИБП, включая корректное выполнение команд выключения.
Программа, которую я должен слышать
NVDA представляет собой бесплатный скринридер с открытым исходным кодом для Windows. Программа считывает элементы интерфейса и озвучивает их через голосовой движок или передает на брайлевский дисплей. В моем сценарии ключевой задачей было обеспечить предсказуемую навигацию с клавиатуры и точное голосовое сопровождение.



Для корректного взаимодействия со скринридером приложение должно правильно передавать метаданные своего графического интерфейса. Обычной визуальной подписи рядом с текстовым полем недостаточно — программа экранного доступа должна четко понимать взаимосвязь элементов. Графические кнопки часто лишены названий или стандартных обработчиков событий, а доступный для чтения элемент далеко не всегда корректно принимает фокус ввода.
Фокус клавиатуры — это фундаментальное понятие: именно туда направлены нажатия клавиш. Если NVDA озвучивает одну кнопку, а клавиша Enter срабатывает совершенно на другой, пользоваться софтом становится невозможно. Когда речь заходит о настройке электропитания, подобные ошибки недопустимы.

ИБП необходим для автономного питания техники при авариях в сети. Время работы напрямую зависит от нагрузки и текущей емкости батарей. Но помимо аппаратной части критически важен доступ к управляющей утилите: чтение вольтажа, процента заряда, просмотр журнала событий и конфигурация параметров. В моем случае работа велась со специализированной сборкой Richcomm PowerManagerII версии 4.1 (revision 646) с русскоязычным интерфейсом, где обилие неподписанных графических кнопок и запутанная навигация делали работу на слух практически невозможной.
Как мы с Codex разделили работу
Рабочий процесс строился на четком разделении обязанностей. С моей стороны формулировались требования к поведению интерфейса: где теряется фокус, что именно озвучивает NVDA и какой результат должен быть при нажатии клавиш.
Codex помогал анализировать структуру окон, сопоставлял системные идентификаторы с реальными функциями, генерировал код модуля и вносил правки. Отдельно стоит отметить анализ скриншотов: ИИ разбирал графические меню по изображениям, помог составить понятные названия кнопок и увязать их с логикой программы. По нейросети выполнялся поиск причин сбоев и оптимизация алгоритмов.
После каждого итеративного изменения я тестировал новую сборку аддона вместе с NVDA. Если навигация сбивалась или голосовое сопровождение давало неполную информацию, я корректировал задачу для Codex. Взаимодействие происходило быстро: благодаря GPT-6 Astra прототип превратился в готовый рабочий инструмент за короткий срок. Важно подчеркнуть, что ИИ использовался только на этапе разработки — готовое дополнение работает полностью локально, взаимодействуя лишь с NVDA и PowerManagerII.
Под графическими кнопками нашлись обычные элементы Windows
Архитектурно решение выполнено в виде специального модуля NVDA (appModule) для конкретного исполняемого файла. Такой модуль активируется автоматически при запуске PowerManagerII и переопределяет имена элементов, их роли и реакцию на горячие клавиши.
Многие элементы интерфейса PowerManagerII имеют фиксированные внутренние числовые идентификаторы (ID). В коде аддона (в файле model.py) была создана карта соответствий, сопоставляющая эти ID с понятными текстовыми метками. Например, ID 1012 привязан к «Состоянию ИБП», 1013 — к «Графику измерений», 1014 — к «Текущим измерениям», а 1016 — к «Номинальным параметрам ИБП».
Когда NVDA сталкивается с элементом в окне приложения, аддон подставляет правильное название. Аналогично прописаны команды меню. Для выполнения действия дополнение вызывает штатные механизмы Windows API, исключая необходимость постоянного распознавания экрана по картинке.
Однако простого сопоставления ID недостаточно: одинаковые идентификаторы могут повторно использоваться в разных диалоговых окнах. Чтобы избежать путаницы, модуль использует шаблоны из файла dialog_map.json. Он анализирует структуру текущего окна и применяет подписи только при точном совпадении диалога.

Это позволило исправить и неточные оригинальные названия. К примеру, пункт «Блок схема…» в исходной программе фактически открывает планировщик задач. В дополнении он переименован в «Расписание задач ИБП (Блок-схема ИБП)», что сохраняет связь с оригиналом и точно отражает суть. Для вызыва главного меню используется клавиша F10 (разделы «Система», «Вид», «Управление ИБП», «Помощь»), а за сам низкоуровневый обмен данными с ИБП отвечает оригинальная утилита.
Озвучить вкладку оказалось недостаточно
При проектировании интерфейса доступности выяснилось: простая озвучка переключения вкладок неудобна. Попадание фокуса на заголовок не должно автоматически менять содержимое страницы. Потребовалось четко разделить фокус на элементе управления и процесс активации самой вкладки.
Для удобного чтения аддон формирует специальное текстовое окно с тремя вкладками: «Состояние ИБП», «Текущие измерения» и «График измерений». Порядок работы с ними следующий:
- Перемещение между заголовками вкладок выполняется стрелками или комбинацией Control+Tab.
- Активация выбранной вкладки происходит только по нажатию Enter или Пробела.
- Клавиша Tab переводит фокус непосредственно к списку данных, где строки считываются стрелками вверх и вниз.
- Клавиша F5 запрашивает свежие данные с сохранением текущей позиции курсора.
- Клавиша Escape закрывает окно чтения и возвращает пользователя в PowerManagerII.
На системном уровне Windows такое поведение реализовано через сочетание стиля вкладок TCS_BUTTONS и отправку сообщения TCM_SETCURFOCUS (детали реализации находятся в manual_tabs.py). Это позволяет удерживать фокус на заголовке без немедленного переключения содержимого. Аналогичная логика применена к группам переключателей (radio buttons): переход по Tab попадает на выбранный пункт, а смена варианта производится сознательно стрелками.

Когда программа возвращает тебя назад
В процессе работы всплыла проблема асинхронных задержек. При открытии окон или обработке событий Windows проходит несколько миллисекунд. За это время пользователь успевает нажать Tab или переключить окно. Если отложенный обработчик фокуса сработает без оглядки на действия пользователя, он принудительно вернет фокус назад, сбив навигацию.
Чтобы избежать этой проблемы, обработчики в дополнении предварительно проверяют реальный статус активного окна и текущий системный фокус Windows. NVDA озвучивает исключительно тот элемент, где действительно находится ввод в данный момент. Для проверки алгоритма были написаны автоматические тесты, подтверждающие, что быстрый ввод (например, Tab через 40 миллисекунд) не приводит к ложному возврату фокуса.
Показание должно быть свежим и понятным
Дополнение извлекает текстовые поля и таблицы из интерфейса PowerManagerII, преобразуя их в понятный голосовой поток. Оригинальная таблица производителя разбита на две группы по три столбца (наименование, значение, единица измерения). Аддон объединяет их в единый последовательный список строк, исключая пропуск данных при чтении.
Важно учитывать контекст: когда в PowerManagerII открыт режим графика, скрытые числовые индикаторы на заднем плане могут хранить устаревшие значения. Дополнение отслеживает текущий активный вид. Если отображается таблица — считывается таблица, если экран состояния — берутся его индикаторы. В режиме графика устаревшие числа блокируются.
Кроме того, проверяется факт наличия связи с ИБП. При потерянном или неопределенном статусе соединения данные помечаются как недоступные с указанием времени последнего опроса. Это исключает ситуации, когда устаревшее число выдается за актуальный показатель.

Точки графика пока недоступны
В текущей версии не удалось извлечь точные числовые координаты точек с графиков. Используемый в PowerManagerII элемент ActiveX не отдаёт эти данные через стандартный интерфейс доступности Windows (MSAA).
Вместо этого текстовое окно «График измерений» информирует пользователя об ограничениях и предоставляет две быстрые кнопки: переход к текущим измерениям и переход к сохраненной истории. История считывается порциями по 50 записей на страницу с автоматическим расчетом минимальных и максимальных значений. Переключение страниц выполняется сочетаниями Control+PageDown и Control+PageUp.
Кнопка выключения должна делать ровно то, что я подтвердил
Операции управления питанием (отключение ИБП, запуск самотестирования, запись и удаление расписания) требуют повышенной безопасности. Случайное нажатие может привести к обесточиванию оборудования.
Для всех критических действий дополнение выводит диалог подтверждения, где по умолчанию выбрана кнопка «Нет». Перед отправкой команды код повторно проверяет состояние окна и целевого элемента: если параметры изменились, команда отменяется. Также внедрена защита от дребезга (повторного быстрого нажатия) и блокировка действий без предварительного выбора записи.
Команда выключения была успешно протестирована на реальном оборудовании ExeGate LLB-3000 — ИБП штатно выполнил отключение.
Что именно проверено
Разработка сопровождалась масштабируемым автоматизированным и ручным тестированием. Была зафиксирована следующая эталонная тестовая конфигурация:
- Утилита ИБП: Richcomm PowerManagerII 4.1 revision 646 (PowerManager.exe v1.0.348.273, русский интерфейс).
- Скринридер: NVDA 2025.3.3 (32-битная версия).
- ОС: Windows 11 25H2 (64-битная x64).
Кодовая база прошла 91 модульный тест (unit-тесты), проверяющие парсинг данных, распознавание диалогов, навигацию и генерацию пакетов. В реальной среде NVDA проведено 254 тестовых сценария, включая 159 проверок переходов по Tab, работу со списком журналов, настройку оповещений и отмену команд питания.
Совместимость и оставшиеся ограничения
Аддон гарантированно совместим с NVDA версии 2025.3 и выше. Интерфейс модуля выполнен на русском языке. В текущие испытания не входили глубокие сценарии полного разряда батарей, работа с внешними брайлевскими дисплеями и удаленное управление ИБП по сети.
Проект распространяется с открытым исходным кодом под лицензией GPL-2.0-only без каких-либо гарантий.
Как попробовать
Релиз PowerManagerII Access 1.0.0 доступен для скачивания в виде готового установочного пакета powerManagerAccess-1.0.0.nvda-addon. Установка стороннего Python не требуется.
После установки пакета и перезапуска NVDA доступны следующие горячие клавиши:
F6(в главном окне) — открыть текстовое окно чтения текущей вкладки.Control+Tab— перемещение по заголовкам вкладок.Enter/Пробел— активация выбранной вкладки.Tab, далееСтрелки вверх/вниз— навигация по строкам данных.F5— принудительное обновление показателей.Escape— возврат в окно PowerManagerII.NVDA+Alt+F2— быстрый голосовой отчет о текущем состоянии ИБП.NVDA+Alt+F8— открытие истории измерений.NVDA+Alt+F5— переключение режима периодического мониторинга (опрос каждые 5 секунд с озвучкой изменений статуса).
Исходный код проекта написан на Python с использованием wxPython. Исходники, документация и отслеживание ошибок открыты в репозитории проекта на GitHub.
Источник: habr.com
