-
-
Save thisdougb/51c61703e285541704562af5e7955752 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash | |
# | |
# Pops up a window notification to let you know when your Magic devices are below THRESHOLD. OSX | |
# gives you a warning at about 2% battery (mouse and keyboard), which means you have to stop work | |
# when the battery dies. Threshold at 20% gives you a few days of power to fit charging in. | |
# | |
# eg: | |
# --------------------------------------- | |
# | | | |
# | Get a coffee and charge: | | |
# | | | |
# | Magic Mouse 2 at 18% | | |
# | Magic Keyboard at 5%. | | |
# | | | |
# | | | |
# | OK Cancel | | |
# --------------------------------------- | |
# | |
# | |
# | |
# 1. Save locally as AppleMagicPower.sh, chmod +x <file> | |
# | |
# 2. Add a cron entry to run it, for example: | |
# 30 9 * * * /Users/dougb/dev/scripts/AppleMagicPower.sh | |
# | |
# (OSX asks permission to run this the first time) | |
# | |
# @thisdougb, 25/04/2020 | |
# You can change the threshold | |
THRESHOLD=20 | |
# You can change the message, if coffee is not your thing | |
MESSAGE="Get a coffee and charge:" | |
# Probably best leave this as is | |
DEVICES=("Magic Mouse 2" "Magic Keyboard") | |
# --------------- Change nothing below here --------------- | |
messages=() | |
for index in ${!DEVICES[*]} | |
do | |
device=${DEVICES[$index]} | |
# trying to make it readable | |
powerValue=$(/usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice -a \ | |
| awk -v dev="$device" \ | |
'BEGIN { battery_value = 0 } \ | |
/\<key\>BatteryPercent\<\/key\>/ \ | |
{ \ | |
getline; \ | |
match($0, "[0-9]{1,3}"); \ | |
battery_value = substr($0, RSTART, RLENGTH) \ | |
} \ | |
/\<key\>Product\<\/key\>/ \ | |
{ \ | |
getline; \ | |
if ($0 ~ dev) { \ | |
print battery_value; \ | |
exit 0; \ | |
} else { \ | |
battery_value = 0 \ | |
} \ | |
} \ | |
') | |
int_re='^[0-9]+$' | |
if [[ $powerValue =~ $int_re ]] ; then | |
if [ $powerValue -le $THRESHOLD ]; then | |
messages[$index]="$device at $powerValue%" | |
fi | |
fi | |
done | |
len=${#messages[@]} | |
if (( "$len" > 0 )); then | |
if [[ -z $MESSAGE ]]; then | |
message="Get a coffee and charge:" | |
else | |
message=$MESSAGE | |
fi | |
for index in ${!messages[*]} | |
do | |
message="$message\n\t${messages[$index]}" | |
done | |
/usr/bin/osascript <<-EOF | |
tell application "System Events" | |
activate | |
display dialog "$message" | |
end tell | |
EOF | |
fi |
One pipe?
/usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice | awk '/BatteryPercent/ { print $4 }'
Nice one!
I was thinking the other day that I should add the keyboard as well. But then I realised I don't have a wireless keyboard... Would be pretty simple, just need to get the (-n) name.
Hmm, it fits under the same -n by the looks of it:
iMac:~$ /usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice | awk '/BatteryPercent/'
| "BatteryPercent" = 60
| "BatteryPercent" = 36
Let me try and isolate it…
I started to mess with a perlre to snaffle the details, but I'm beyond rusty 😆 So, here's the tree output, which I'm betting you can parse …
iMac:~$ /usr/sbin/ioreg -rln AppleDeviceManagementHIDEventService
+-o AppleDeviceManagementHIDEventService <class AppleDeviceManagementHIDEventService, id 0x100000725, registered, mat$
{
"LowBatteryNotificationPercentage" = 2
"PrimaryUsagePage" = 65280
"BatteryFaultNotificationType" = "MOBatteryFault"
"VersionNumber" = 0
"VendorID" = 76
"LastCriticalError" = 32
"Built-In" = No
"DeviceAddress" = "94-b0-1f-03-35-0a"
"IOUserClientClass" = "IOHIDEventServiceUserClient"
"WakeReason" = "Button (0x03)"
"Product" = "Magic Mouse 2"
"SerialNumber" = "94-b0-1f-03-35-0a"
"Transport" = "Bluetooth"
"BatteryLowNotificationType" = "MOLowBattery"
"ProductID" = 617
"DeviceUsagePairs" = ({"DeviceUsagePage"=65280,"DeviceUsage"=11},{"DeviceUsagePage"=65280,"DeviceUsage"=20})
"BatteryPercent" = 60
"BD_ADDR" = <94b01f03350a>
"CriticallyLowBatteryNotificationPercentage" = 1
"BatteryStatusNotificationType" = "BatteryStatusChanged"
"ReportInterval" = 11250
"VendorIDSource" = 1
"STFW Version" = 2133
"CFBundleIdentifier" = "com.apple.driver.AppleTopCaseHIDEventDriver"
"IOCFPlugInTypes" = {"7DDEECA8-A7B4-11DA-8A0E-0014519758EF"="IOHIDFamily.kext/Contents/PlugIns/IOHIDLib.plugin",$
"IOProviderClass" = "IOHIDInterface"
"LocationID" = 520303882
"IOClass" = "AppleDeviceManagementHIDEventService"
"HIDServiceSupport" = No
"CFBundleIdentifierKernel" = "com.apple.driver.AppleTopCaseHIDEventDriver"
"ProductIDArray" = (617)
"BatteryStatusFlags" = 0
"ColorID" = 0
"IOMatchCategory" = "IODefaultMatchCategory"
"CountryCode" = 0
"IOProbeScore" = 7175
"PrimaryUsage" = 11
"IOGeneralInterest" = "IOCommand is not serializable"
"BTFW Version" = 258
}
+-o AppleDeviceManagementHIDEventService <class AppleDeviceManagementHIDEventService, id 0x10003a084, registered, mat$
{
"LowBatteryNotificationPercentage" = 2
"PrimaryUsagePage" = 65280
"BatteryFaultNotificationType" = "KBBatteryFault"
"VersionNumber" = 0
"VendorID" = 76
"IOUserClientClass" = "IOHIDEventServiceUserClient"
"Built-In" = No
"DeviceAddress" = "68-fe-f7-73-44-2f"
"WakeReason" = "Keyboard (0x02)"
"Product" = "Magic Keyboard"
"SerialNumber" = "68-fe-f7-73-44-2f"
"Transport" = "Bluetooth"
"BatteryLowNotificationType" = "KBLowBattery"
"ProductID" = 615
"DeviceUsagePairs" = ({"DeviceUsagePage"=65280,"DeviceUsage"=11},{"DeviceUsagePage"=65280,"DeviceUsage"=20})
"BatteryPercent" = 36
"BD_ADDR" = <68fef773442f>
"CriticallyLowBatteryNotificationPercentage" = 1
"BatteryStatusNotificationType" = "BatteryStatusChanged"
"ReportInterval" = 11250
"VendorIDSource" = 1
"STFW Version" = 2129
"CFBundleIdentifier" = "com.apple.driver.AppleTopCaseHIDEventDriver"
"IOCFPlugInTypes" = {"7DDEECA8-A7B4-11DA-8A0E-0014519758EF"="IOHIDFamily.kext/Contents/PlugIns/IOHIDLib.plugin",$
"IOProviderClass" = "IOHIDInterface"
"LocationID" = 2004042799
"IOClass" = "AppleDeviceManagementHIDEventService"
"HIDServiceSupport" = No
"CFBundleIdentifierKernel" = "com.apple.driver.AppleTopCaseHIDEventDriver"
"ProductIDArray" = (615)
"BatteryStatusFlags" = 0
"ColorID" = 0
"IOMatchCategory" = "IODefaultMatchCategory"
"CountryCode" = 0
"IOProbeScore" = 7175
"PrimaryUsage" = 11
"IOGeneralInterest" = "IOCommand is not serializable"
"BTFW Version" = 256
}
hacktastic... 😬
Ha! I've realised it's a whole new blob of Python 😆 I'll test it out shortly …
yeah, there's no way to distinguish the two dumps of text you have above. So I had to dump it out as xml, then write a searcher for the xml... Apple don't make it easy.
Catalina, sigh …
iMac:~$ python -V
Python 2.7.16
ahh, yeah, I forgot. It's not like Apple haven't had warning.
The End Of Life date (EOL, sunset date) for Python 2.7 has been moved five years into the future, to 2020.
On the plus side. I've always wanted to do xml parsing in bash...
Ha! Quite!
This is working for me. Back to bash...
Hmm, not sure it's picked the keyboard battery level up…
iMac:scripts$ sh -x !$
sh -x ./magic
+ THRESHOLD=20
+ MESSAGE='Get a coffee and charge:\n'
+ DEVICES=("Magic Mouse 2" "Magic Keyboard")
+ messages=()
+ for index in '${!DEVICES[*]}'
+ device='Magic Mouse 2'
++ /usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice -a
++ awk -v 'dev=Magic Mouse 2' 'BEGIN { battery_key=0; battery_value=0 } /\<key\>BatteryPercent\<\/key\>/ { battery_key=NR; getline; match($0, "[0-9]{1,3}"); battery_value = substr($0, RSTART, RLENGTH) } /\<key\>Product\<\/key\>/ { getline; if ($0 ~ dev) print battery_value; exit 0 }'
+ powerValue=57
+ [[ -n 57 ]]
+ [[ 57 -le 20 ]]
+ for index in '${!DEVICES[*]}'
+ device='Magic Keyboard'
++ /usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice -a
++ awk -v 'dev=Magic Keyboard' 'BEGIN { battery_key=0; battery_value=0 } /\<key\>BatteryPercent\<\/key\>/ { battery_key=NR; getline; match($0, "[0-9]{1,3}"); battery_value = substr($0, RSTART, RLENGTH) } /\<key\>Product\<\/key\>/ { getline; if ($0 ~ dev) print battery_value; exit 0 }'
+ powerValue=
+ [[ -n '' ]]
+ len=0
+ (( 0 > 0 ))
can you post the output of, it's probably just the wrong name I've used.
/usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice -a
or via email/sms
I've pruned the output (full dump is 83,000 lines; really Apple, WTF?)
iMac:~$ ioreg -r -l -n AppleHSBluetoothDevice -a | grep -iA2 product
<key>Bluetooth Product Name</key>
<string>Magic Mouse 2</string>
<key>CFBundleIdentifier</key>
--
<key>ProductString</key>
<string>Magic Mouse 2</string>
</dict>
--
<key>Bluetooth Product Name</key>
<string>Magic Mouse 2</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ProductIDArray</key>
<array>
<integer>617</integer>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>617</integer>
<key>idVendor</key>
--
<key>Bluetooth Product Name</key>
<string>Magic Mouse 2</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportInterval</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ProductIDArray</key>
<array>
<integer>617</integer>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>617</integer>
<key>idVendor</key>
--
<key>Bluetooth Product Name</key>
<string>Magic Mouse 2</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>617</integer>
<key>idVendor</key>
--
<key>Product</key>
<string>Mouse</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Mouse 2</string>
<key>ProductID</key>
<integer>617</integer>
<key>ReportDescriptor</key>
--
<key>Bluetooth Product Name</key>
<string>Magic Keyboard</string>
<key>CFBundleIdentifier</key>
--
<key>ProductString</key>
<string>Magic Keyboard</string>
</dict>
--
<key>Bluetooth Product Name</key>
<string>Magic Keyboard</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ProductIDArray</key>
<array>
<integer>615</integer>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>615</integer>
<key>idVendor</key>
--
<key>Bluetooth Product Name</key>
<string>Magic Keyboard</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ProductIDArray</key>
<array>
<integer>615</integer>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>615</integer>
<key>idVendor</key>
--
<key>Bluetooth Product Name</key>
<string>Magic Keyboard</string>
<key>IOObjectClass</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>idProductArray</key>
<array>
<integer>613</integer>
--
<key>idProduct</key>
<integer>615</integer>
<key>idVendor</key>
--
<key>Product</key>
<string>Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
--
<key>Product</key>
<string>Magic Keyboard</string>
<key>ProductID</key>
<integer>615</integer>
<key>ReportDescriptor</key>
bugfix 😁
One pipe?
/usr/sbin/ioreg -r -l -n AppleHSBluetoothDevice | awk '/BatteryPercent/ { print $4 }'