Bluetooth経由でMacに接続しているデバイスのうち、バッテリー残量を取得できるものの情報を取得するAppleScriptです。
# as anythingの解釈違いから「as list of string or string」と展開される部分をmacOS 10.13以降のOSでは誤解されなくなったので、v4として追記してあります
現在Bluetooth経由で接続中のデバイス名一覧を取得し、各名称のデバイスのバッテリー残量情報をSystem Profiler経由で取得します。
ただし、バッテリー残量を取得できているのはApple純正のキーボードとマウスだけで、同じく純正品といってもペアリングしているiPhoneのバッテリー残量は取得できませんし、Bluetoothメニューにバッテリー残量が表示されるAirPodsもこの方法では取得できていません。
サードパーティのデバイス(PlayStation 3のゲームコントローラー)についても確認しましたが、メニューにも残量は出ていませんし、同様に本AppleScriptでもバッテリー残量は取得できません。
あと、プログラムはもっと短くできるような気がとてもします。
AppleScript名:Bluetoothデバイスのバッテリー残量を取得 v3 |
— Created 2018-03-18 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "IOBluetooth" property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSPredicate : a reference to current application’s NSPredicate property NSMutableArray : a reference to current application’s NSMutableArray property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSPropertyListFormat : a reference to current application’s NSPropertyListFormat property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSPropertyListImmutable : a reference to current application’s NSPropertyListImmutable property NSPropertyListSerialization : a reference to current application’s NSPropertyListSerialization set devList to getBTBatteryLifeByNameList() of me return devList –> {{devName:"Takaaki Naganoya のマウス", battPercentage:79}, {devName:"Takaaki Naganoya のキーボード #1", battPercentage:93}} on getBTBatteryLifeByNameList() –Get name of connected devices set aDevNameList to getBTPeripheralsConnected() of me –Get System Profiler information set sRes to do shell script "/usr/sbin/system_profiler SPBluetoothDataType -detailLevel full -xml" set aSource to (readPlistFromStr(sRes) of me) as list set aaList to contents of first item of aSource –Filter System Profiler output set outList to {} set aList to _items of aaList repeat with aDevName in aDevNameList set aName to contents of aDevName repeat with i in aList set aDict to (NSMutableDictionary’s dictionaryWithDictionary:(contents of i)) set aKeyPath to "device_title." & aName set aVal to (aDict’s valueForKeyPath:aKeyPath) as list of string or string set tmpOut to filterDevListByName(aVal) of me as list of string or string if (tmpOut ≠ missing value) and (tmpOut ≠ "") then set the end of outList to {devName:aName, battPercentage:retIntNumFromPercentageString(tmpOut) of me} exit repeat end if end repeat end repeat return outList end getBTBatteryLifeByNameList on retIntNumFromPercentageString(aStr) set aStr to current application’s NSString’s stringWithString:aStr set aNum to aStr’s intValue() return aNum end retIntNumFromPercentageString on filterDevListByName(aVal) set outList to {} repeat with ii in aVal set jj to contents of ii if jj is not equal to missing value then set tmpRec to (current application’s NSDictionary’s dictionaryWithDictionary:jj) set tmpRes to (tmpRec’s valueForKeyPath:"device_batteryPercent") if tmpRes ≠ missing value then return (tmpRes as list of string or string) end if end if end repeat return "" end filterDevListByName –Bluetooth経由で接続されているデバイス名をリストで返す on getBTPeripheralsConnected() set pRes to getBluetoothPowerState() of me if pRes = false then return false set dArray to current application’s IOBluetoothDevice’s pairedDevices() set aRes to my filterRecListByLabel1(dArray, "mIONotification != 0") set dNames to (aRes’s mName) as list return dNames end getBTPeripheralsConnected –stringのplistを読み込んでRecordに on readPlistFromStr(theString) set aSource to NSString’s stringWithString:theString set pListData to aSource’s dataUsingEncoding:(NSUTF8StringEncoding) set aPlist to NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(NSPropertyListImmutable) |format|:(NSPropertyListFormat) errorDescription:(missing value) return aPlist end readPlistFromStr –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel(aRecList as list, aPredicate as string) set aArray to NSArray’s arrayWithArray:aRecList set aPredicate to NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate set bList to filteredArray as list return bList end filterRecListByLabel –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel1(aRecList, aPredicate as string) set aArray to current application’s NSArray’s arrayWithArray:aRecList set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate return filteredArray end filterRecListByLabel1 –Mac本体のBluetoothのパワー状態を取得 on getBluetoothPowerState() set aCon to current application’s IOBluetoothHostController’s alloc()’s init() set pRes to (aCon’s powerState()) as boolean end getBluetoothPowerState |
「as anything」の部分がmacOS 10.13より前のバージョンでは「as list of string or string」と解釈されてしまいますが、macOS 10.14以降のスクリプトエディタでは「as anyhing」、Script Debuggerでは「as any」と解釈されるため(用語がそれぞれ整備されたため)、「as anything」で書き換えておきました。
「as anything」については、言語仕様の重箱の隅のさらに隅をつついている状態なので、「as {list, string, missing value}」などと、想定できるデータ型を列挙しておいたほうが安全です。でも、このany型のcastはCocoa ObjectからAppleScript Objectの型指定しない変換ができて便利なので、言語仕様上ないと不便です。
AppleScript名:Bluetoothデバイスのバッテリー残量を取得 v4.scpt |
— Created 2018-03-18 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "IOBluetooth" property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSPredicate : a reference to current application’s NSPredicate property NSMutableArray : a reference to current application’s NSMutableArray property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSPropertyListFormat : a reference to current application’s NSPropertyListFormat property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSPropertyListImmutable : a reference to current application’s NSPropertyListImmutable property NSPropertyListSerialization : a reference to current application’s NSPropertyListSerialization set devList to getBTBatteryLifeByNameList() of me return devList –> {{devName:"Takaaki Naganoya のマウス", battPercentage:79}, {devName:"Takaaki Naganoya のキーボード #1", battPercentage:93}} on getBTBatteryLifeByNameList() –Get name of connected devices set aDevNameList to getBTPeripheralsConnected() of me –Get System Profiler information set sRes to do shell script "/usr/sbin/system_profiler SPBluetoothDataType -detailLevel full -xml" set aSource to (readPlistFromStr(sRes) of me) as list set aaList to contents of first item of aSource –Filter System Profiler output set outList to {} set aList to _items of aaList repeat with aDevName in aDevNameList set aName to contents of aDevName repeat with i in aList set aDict to (NSMutableDictionary’s dictionaryWithDictionary:(contents of i)) set aKeyPath to "device_title." & aName set aVal to (aDict’s valueForKeyPath:aKeyPath) as anything set tmpOut to filterDevListByName(aVal) of me as anything if (tmpOut ≠ missing value) and (tmpOut ≠ "") then set the end of outList to {devName:aName, battPercentage:retIntNumFromPercentageString(tmpOut) of me} exit repeat end if end repeat end repeat return outList end getBTBatteryLifeByNameList on retIntNumFromPercentageString(aStr) set aStr to current application’s NSString’s stringWithString:aStr set aNum to aStr’s intValue() return aNum end retIntNumFromPercentageString on filterDevListByName(aVal) set outList to {} repeat with ii in aVal set jj to contents of ii if jj is not equal to missing value then set tmpRec to (current application’s NSDictionary’s dictionaryWithDictionary:jj) set tmpRes to (tmpRec’s valueForKeyPath:"device_batteryPercent") if tmpRes ≠ missing value then return (tmpRes as anything) end if end if end repeat return "" end filterDevListByName –Bluetooth経由で接続されているデバイス名をリストで返す on getBTPeripheralsConnected() set pRes to getBluetoothPowerState() of me if pRes = false then return false set dArray to current application’s IOBluetoothDevice’s pairedDevices() set aRes to my filterRecListByLabel1(dArray, "mIONotification != 0") set dNames to (aRes’s mName) as list return dNames end getBTPeripheralsConnected –stringのplistを読み込んでRecordに on readPlistFromStr(theString) set aSource to NSString’s stringWithString:theString set pListData to aSource’s dataUsingEncoding:(NSUTF8StringEncoding) set aPlist to NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(NSPropertyListImmutable) format:(NSPropertyListFormat) errorDescription:(missing value) return aPlist end readPlistFromStr –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel(aRecList as list, aPredicate as string) set aArray to NSArray’s arrayWithArray:aRecList set aPredicate to NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate set bList to filteredArray as list return bList end filterRecListByLabel –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel1(aRecList, aPredicate as string) set aArray to current application’s NSArray’s arrayWithArray:aRecList set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate return filteredArray end filterRecListByLabel1 –Mac本体のBluetoothのパワー状態を取得 on getBluetoothPowerState() set aCon to current application’s IOBluetoothHostController’s alloc()’s init() set pRes to (aCon’s powerState()) as boolean end getBluetoothPowerState |