Если вы хотите автоматизировать рутинные задачи на Mac, AppleScript и Automator – первые инструменты, к которым стоит обратиться. Сегодня разберем частый сценарий: напоминание о выключении Bluetooth через заданное время. Кажется, всё просто, но даже опытные пользователи сталкиваются с подводными камнями, особенно при работе с системными командами и таймерами.
Одна из типичных ошибок – использование shell-скриптов для получения времени там, где достаточно встроенных функций AppleScript. Например, команда date +%s (которая возвращает текущее время в секундах с начала эпохи Unix) часто вызывает проблемы с преобразованием типов данных. Вместо этого лучше использовать time of (current date) – это упрощает вычисления и снижает риск ошибок.
В качестве примера, возьмем такой вариант скрипта:
global Cnt
global Cnt2
global Cnt3
global diff
global curTime
global btOnTime
set Cnt to 0
set Cnt2 to 0
set Cnt3 to 0
set btOnTime to do shell script "date +%s"
set curTime to do shell script "date +%s"
set diff to (curTime as integer) - (btOnTime as integer)
set btStatus to 0
repeat
repeat until btStatus = 0
try
do shell script "system_Profiler SPBluetoothDataType | grep -i \"State: On\""
set btStatus to 1
set btOnTime to do shell script "date +%s"
on error
set btStatus to 0
end try
-- delay 110
set Cnt2 to (Cnt2 + 1)
end repeat
return display alert "BT ON. OUT OF CICLE 1. Cnt2: " & Cnt2
set curTime to do shell script "date +%s"
repeat while diff < 120
set curTime to do shell script "date +%s"
set diff to (curTime as integer) - (btOnTime as integer)
set Cnt3 to (Cnt3 + 1)
-- delay 110
end repeat
return display alert "BT ON. OUT OF CICLE 2. Cnt3: " & Cnt3
if diff > 120 and Cnt = 0 then
-- set recentRestart to (curTime - btOnTime) < 10
-- if not recentRestart then
return display notification "Stupendo, SPEGNI IL BLUETOOTH!!" with title "Bluetooth acceso da un BOTTO"
set Cnt to (Cnt + 1)
-- end if
end if
end repeat
Почему исходный скрипт может не работать?
Давайте посмотрим на ключевые моменты:
1. Глобальные переменные. В оригинальном коде объявлены global Cnt, global Cnt2 и другие. Хотя AppleScript позволяет это, избыточные глобальные переменные усложняют отладку. В большинстве случаев достаточно локальных переменных, объявленных через local (как в исправленной версии).
2. Проверка статуса Bluetooth. Использование system_Profiler SPBluetoothDataType | grep -i “State: On” работает, но не оптимально. Команда system_Profiler может выполняться до 5-10 секунд, что замедляет скрипт. Лучше анализировать вывод напрямую в AppleScript через contains “Bluetooth Power: On”.
3. Тайминги и задержки. В оригинале есть циклы с repeat until и repeat while, которые могут блокировать выполнение скрипта. Добавление delay 2 (как в исправленном коде) снижает нагрузку на процессор и делает проверки более предсказуемыми.
4. Обработка ошибок. Конструкция try…on error в первом скрипте корректно перехватывает отсутствие включенного Bluetooth, но не учитывает возможные таймауты выполнения команды. В исправленной версии добавлен параметр -timeout 4 для system_Profiler, что предотвращает зависания.
Исправленный код с комментариями
Вот оптимизированная версия скрипта, которая решает описанные проблемы:
use scripting additions
local t1, t2, t3, bton, conn
-- Команда для получения статуса Bluetooth (вывод содержит "Bluetooth Power: On" или "Off")
set cmd to "system_Profiler SPBluetoothDataType -timeout 4"
set t1 to time of (current date) -- Время начала (секунды с полуночи)
set t2 to t1 -- Инициализация переменной времени окончания
set bton to 0 -- Счетчик циклов с включенным Bluetooth
repeat until (t2 - t1 ≥ 30) -- Проверяем статус каждые 2 секунды в течение 30 секунд
set conn to do shell script cmd -- Запуск команды
if conn contains "Bluetooth Power: On" then
set bton to 1 + bton
delay 2 -- Пауза между проверками
set t2 to time of (current date)
else
set bton to 0
exit repeat -- Выход, если Bluetooth выключен
end if
end repeat
set t3 to (t2 - t1) -- Общее время работы цикла
if bton ≥ 1 then
display alert "Bluetooth включен уже " & t3 & " секунд" giving up after 2
else
display notification "Bluetooth выключен через " & t3 & " секунд"
end if
Как интегрировать скрипт в Automator
1. Откройте Automator (Программы → Automator).
2. Создайте новый документ типа Быстрое действие.
3. В поиске найдите действие Запустить AppleScript и перетащите его в рабочую область.
4. Вставьте исправленный код в текстовое поле.
5. Сохраните действие через Файл → Сохранить (например, под именем «Проверка Bluetooth»).
Чтобы скрипт запускался автоматически, настройте его выполнение через Настройки → Защита и безопасность → Конфиденциальность → Службы автоматизации. Разрешите Automator управлять Bluetooth.
Возможные ошибки и как их исправить
– Скрипт не реагирует на выключение Bluetooth. Убедитесь, что в системе нет других процессов, блокирующих Bluetooth (например, подключенные устройства или фоновые приложения вроде AirDrop).
– Automator не сохраняет изменения. Попробуйте перезапустить Automator и сохранить скрипт с другим именем.
– Нет уведомлений. Проверьте настройки уведомлений macOS (Системные настройки → Уведомления и фокус) – для Automator должны быть разрешены звуковые и визуальные оповещения.
Дополнительные советы
- Используйте LaunchAgents для периодического запуска. Если нужно проверять Bluetooth каждые 5 минут, создайте plist-файл в ~/Library/LaunchAgents с параметром StartInterval (инструкции есть на сайте Apple Developer).
- Оптимизируйте запросы к системе. Замените system_Profiler на blueutil (утилита командной строки для управления Bluetooth), если часто сталкиваетесь с таймаутами. Установите через Homebrew:
brew install blueutil
Затем используйте команду blueutil –power on/off` для управления.
Если вы столкнулись с неочевидной ошибкой, попробуйте добавить log “Текст” в код – это выведет сообщения в консоль (Программы → Утилиты → Консоль), где можно отслеживать выполнение скрипта в реальном времени.
Надеюсь, эти решения помогут сделать ваш Mac чуть более автоматизированным. Если остались вопросы – смело пишите в комментарии, постараюсь помочь!