Skip to content

Instantly share code, notes, and snippets.

@thisdougb
Last active April 26, 2020 16:51
Show Gist options
  • Save thisdougb/51c61703e285541704562af5e7955752 to your computer and use it in GitHub Desktop.
Save thisdougb/51c61703e285541704562af5e7955752 to your computer and use it in GitHub Desktop.
Simple alert to let you know when the device battery is low.
#!/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
@phips
Copy link

phips commented Apr 25, 2020

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  ))

@thisdougb
Copy link
Author

thisdougb commented Apr 25, 2020

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

@phips
Copy link

phips commented Apr 25, 2020

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>

@thisdougb
Copy link
Author

bugfix 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment