AppleScript IOBluetooth 问题,但仅当导出为应用程序时

AppleScript IOBluetooth issue, but only when exported as an app

基于 我正在使用 IOBluetooth 框架来检测我的键盘 disconnect/reconnect,因此将其切换到第二个主机会触发将显示输入更改为该主机嗯

当我在脚本编辑器中运行它时,它会在我来回切换时检测连接和断开状态。当我将它导出为应用程序(仅 运行-only)和 运行 时,它开始检测正确的状态。然而,一旦键盘断开并稍后重新连接,它就再也检测不到连接状态。

如果重要的话,我在 2019 MacBook Pro 上使用 Catalina (10.15.7)。

use framework "IOBluetooth"
use scripting additions

property lastStatus : true

set debug to true
set myKeyboard to "Keychron"

repeat
    set kbStatus to isDeviceConnected(myKeyboard)
    if kbStatus is not equal to lastStatus then
        if debug is false then
            if kbStatus is true then
                do shell script "/usr/local/bin/ddcctl -d 1 -i 27"
            else
                do shell script "/usr/local/bin/ddcctl -d 1 -i 17"
            end if
        else
            log "Status changed to " & kbStatus
        end if
        set lastStatus to kbStatus
    end if
    delay 1
end repeat

on isDeviceConnected(substring)
    repeat with device in (current application's IOBluetoothDevice's pairedDevices() as list)
        if (device's nameOrAddress as string) contains substring then
            if device's isConnected then
                return true
            else
                return false
            end if
        end if
    end repeat
    
    return false
end isDeviceConnected

编辑:我在不同的地方插入了很多调试日志消息。当它未能检测到重新连接时,它仍在 pairedDevices() 列表中匹配我的键盘名称,只是未连接。

我怀疑问题是您没有为某些 objC 方法添加括号,特别是:nameOrAddress()isConnected()。我要补充一点,使用空闲循环比使用无休止的重复循环要好得多。看起来像这样:

property lastStatus : true
property debug : true
property myKeyboard : "Keychron"

on idle
    set kbStatus to isDeviceConnected(myKeyboard)
    if kbStatus is not equal to lastStatus then
        if debug is false then
            if kbStatus is true then
                do shell script "/usr/local/bin/ddcctl -d 1 -i 27"
            else
                do shell script "/usr/local/bin/ddcctl -d 1 -i 17"
            end if
        else
            log "Status changed to " & kbStatus
        end if
        set lastStatus to kbStatus
    end if
    return 1
end idle

但是把所有这些放在一边,因为我认为最好的解决方案是使用 IOBluetoothDevice 方法来注册观察者。这样你的应用程序将在后台安静地休眠,直到它收到设备已连接或断开连接的通知。将以下脚本复制到脚本编辑器中:

use AppleScript version "2.4" -- Yosemite 10.10 or later
use framework "IOBluetooth"
use scripting additions

property IOBluetoothDevice : class "IOBluetoothDevice"

property myKeyboard : "Keychron"

property connectionNotifObj : missing value
property disconnectionNotifObj : missing value

on run
    try
        set connectionNotifObj to IOBluetoothDevice's registerForConnectNotifications:me selector:"didConnectNotif:forDevice:"
    on error errstr
        display dialog errstr
    end try
end run

on quit
    try
        connectionNotifObj's unregister()
    end try
    set connectionNotifObj to missing value
    continue quit
end quit

on didConnectNotif:notif forDevice:dev
    if (dev's nameOrAddress() as text) contains myKeyboard then
        set disconnectionNotifObj to (dev's registerForDisconnectNotification:me selector:"didDisconnectNotif:forDevice:")
    end if
end didConnectNotif:forDevice:

on didDisconnectNotif:notif forDevice:dev
    if (dev's nameOrAddress() as string) contains myKeyboard then
        try
            disconnectionNotifObj's unregister()
        end try
        set disconnectionNotifObj to missing value
    end if
end didDisconnectNotif:forDevice:

将脚本另存为应用程序——确保点击 stay open after run handler 复选框,这样脚本应用程序就不会自动退出——这样你就可以开始了。

如果您希望脚本应用对 Dock 和 App Picker 不可见,运行 在终端中执行以下命令:

defaults write '/path/to/$name.app/Contents/Info.plist' LSUIElement -bool yes

当然,这让手动退出有点让人头疼(你必须使用终端或 Activity 监视器才能看到它 运行ning),但它在视觉上更令人愉悦.