├── .gitignore ├── Battery.alfredworkflow ├── README.md ├── info.png └── src ├── battery.sh ├── icon.png ├── icons ├── age.png ├── charging.png ├── clock.png ├── critical.png ├── cycles.png ├── full.png ├── health.png ├── low.png ├── medium.png ├── power.png ├── serial.png └── temp.png └── info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /Battery.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/Battery.alfredworkflow -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Battery 2 | 3 | > For Apple M1 chip users: https://github.com/BenziAhamed/alfred-battery/issues/11#issue-812562822 4 | 5 | ![Logo](info.png) 6 | 7 | An Alfred workflow to display battery information of your Apple laptop. 8 | -------------------------------------------------------------------------------- /info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/info.png -------------------------------------------------------------------------------- /src/battery.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | INFO=$(ioreg -l -n AppleSmartBattery -r) 4 | 5 | # Charge and time remaining 6 | CURRENT_CAPACITY=$(echo "$INFO" | grep CurrentCapacity | awk '{printf $3; exit}') 7 | MAX_CAPACITY=$(echo "$INFO" | grep MaxCapacity | awk '{printf $3; exit}') 8 | CHARGE=$((CURRENT_CAPACITY * 100 / MAX_CAPACITY)) 9 | CELLS=$(python -c "f='●'*($CHARGE/10) + '○'*(10-$CHARGE/10); print f") 10 | STATUS_INFO=Draining... 11 | 12 | CHARGING=$(echo "$INFO" | grep -i ischarging | awk '{printf("%s", $3)}') 13 | TIME_TO_EMPTY=$(echo "$INFO" | grep -i AvgTimeToEmpty | awk '{printf("%s", $3)}') 14 | TIME_LEFT=Calculating… 15 | 16 | if [ "$TIME_TO_EMPTY" -lt 15000 ]; then 17 | TIME_LEFT=$(echo "$INFO" | grep -i AvgTimeToEmpty | awk '{printf("%i:%.2i", $3/60, $3%60)}') 18 | fi 19 | 20 | if [ "$CHARGING" == Yes ]; then 21 | TIME_FULL=$(echo "$INFO" | grep -i AvgTimeToFull | tr '\n' ' | ' | awk '{printf("%i:%.2i", $3/60, $3%60)}') 22 | TIME_INFO=$(echo "$TIME_FULL" until full) 23 | STATUS_INFO=Charging... 24 | BATT_ICON=charging.png 25 | else 26 | FULLY_CHARGED=$(echo "$INFO" | grep -i FullyCharged | awk '{printf("%s", $3)}') 27 | EXTERNAL=$(echo "$INFO" | grep -i ExternalConnected | awk '{printf("%s", $3)}') 28 | if [ "$FULLY_CHARGED" == Yes ]; then 29 | if [ "$EXTERNAL" == Yes ]; then 30 | TIME_INFO="On AC power" 31 | STATUS_INFO="Fully Charged" 32 | BATT_ICON=power.png 33 | CHARGE="100" 34 | CELLS="●●●●●●●●●●" 35 | else 36 | TIME_INFO=$TIME_LEFT 37 | BATT_ICON=full.png 38 | fi 39 | else 40 | TIME_INFO=$TIME_LEFT 41 | BATT_ICON=critical.png 42 | if [ "$CHARGE" -gt 80 ]; then 43 | BATT_ICON=full.png 44 | elif [ "$CHARGE" -gt 50 ]; then 45 | BATT_ICON=medium.png 46 | elif [ "$CHARGE" -gt 10 ]; then 47 | BATT_ICON=low.png 48 | fi 49 | fi 50 | fi 51 | 52 | # Temperature 53 | TEMPERATURE=$(echo "$INFO" | grep Temperature | awk '{printf ("%.1f", $3/10-273)}') 54 | 55 | # Cycle count 56 | CYCLE_COUNT=$(echo "$INFO" | grep -e '"CycleCount" =' | awk '{printf ("%i", $3)}') 57 | 58 | # Battery health 59 | # ref: https://github.com/BenziAhamed/alfred-battery/issues/10#issuecomment-745541202 60 | DESIGN_CAPACITY=$(echo "$INFO" | grep DesignCapacity | awk '{printf ("%.i", $3)}') 61 | HEALTH=$((MAX_CAPACITY * 100 / DESIGN_CAPACITY)) 62 | 63 | if [ "$HEALTH" -gt 100 ]; then 64 | HEALTH=100 65 | fi 66 | 67 | # Serial 68 | SERIAL=$(echo "$INFO" | grep BatterySerialNumber | awk '{printf ("%s", $3)}' | tr -d '"') 69 | 70 | # Battery age 71 | MANUFACTURE_DATE=$(echo "$INFO" | grep ManufactureDate | awk '{printf ("%i", $3)}') 72 | day=$((MANUFACTURE_DATE&31)) 73 | month=$(((MANUFACTURE_DATE>>5)&15)) 74 | year=$((1980+(MANUFACTURE_DATE>>9))) 75 | AGE=$(python -c "from datetime import date as D; d1=D.today(); d2=D($year, $month, $day); print ( (d1.year - d2.year)*12 + d1.month - d2.month )") 76 | 77 | # Alfred feedback 78 | cat << EOB 79 | 80 | 81 | 82 | $CHARGE% $CELLS 83 | $STATUS_INFO 84 | icons/$BATT_ICON 85 | 86 | 87 | $TIME_INFO 88 | Time Left 89 | icons/clock.png 90 | 91 | 92 | $TEMPERATURE °C 93 | Temperature 94 | icons/temp.png 95 | 96 | 97 | $CYCLE_COUNT 98 | Charge Cycles Completed 99 | icons/cycles.png 100 | 101 | 102 | $HEALTH% 103 | Health 104 | icons/health.png 105 | 106 | 107 | $SERIAL 108 | Serial 109 | icons/serial.png 110 | 111 | 112 | $AGE months 113 | Age 114 | icons/age.png 115 | 116 | 117 | EOB 118 | -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icon.png -------------------------------------------------------------------------------- /src/icons/age.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/age.png -------------------------------------------------------------------------------- /src/icons/charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/charging.png -------------------------------------------------------------------------------- /src/icons/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/clock.png -------------------------------------------------------------------------------- /src/icons/critical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/critical.png -------------------------------------------------------------------------------- /src/icons/cycles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/cycles.png -------------------------------------------------------------------------------- /src/icons/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/full.png -------------------------------------------------------------------------------- /src/icons/health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/health.png -------------------------------------------------------------------------------- /src/icons/low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/low.png -------------------------------------------------------------------------------- /src/icons/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/medium.png -------------------------------------------------------------------------------- /src/icons/power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/power.png -------------------------------------------------------------------------------- /src/icons/serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/serial.png -------------------------------------------------------------------------------- /src/icons/temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/alfred-battery/78fa684b4b05a44b240e9e2b3eb9c8b2ffcf1734/src/icons/temp.png -------------------------------------------------------------------------------- /src/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.benzi.a2w.battery 7 | category 8 | Tools 9 | connections 10 | 11 | 3917FDAB-460E-4C28-A4A9-645B9773F34A 12 | 13 | 14 | destinationuid 15 | FC5EB146-FA11-4E33-8456-F60EA94E7EEE 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | FC5EB146-FA11-4E33-8456-F60EA94E7EEE 25 | 26 | 27 | createdby 28 | Benzi Ahamed 29 | description 30 | Displays information about your laptop battery 31 | disabled 32 | 33 | name 34 | Battery 35 | objects 36 | 37 | 38 | config 39 | 40 | action 41 | 0 42 | argument 43 | 0 44 | focusedappvariable 45 | 46 | focusedappvariablename 47 | 48 | hotkey 49 | 0 50 | hotmod 51 | 0 52 | leftcursor 53 | 54 | modsmode 55 | 0 56 | relatedAppsMode 57 | 0 58 | 59 | type 60 | alfred.workflow.trigger.hotkey 61 | uid 62 | 3917FDAB-460E-4C28-A4A9-645B9773F34A 63 | version 64 | 2 65 | 66 | 67 | config 68 | 69 | alfredfiltersresults 70 | 71 | alfredfiltersresultsmatchmode 72 | 0 73 | argumenttrimmode 74 | 0 75 | argumenttype 76 | 1 77 | escaping 78 | 63 79 | keyword 80 | battery 81 | queuedelaycustom 82 | 3 83 | queuedelayimmediatelyinitially 84 | 85 | queuedelaymode 86 | 0 87 | queuemode 88 | 1 89 | runningsubtext 90 | Displays information about your battery, retrieving... 91 | script 92 | sh ./battery.sh 93 | scriptargtype 94 | 0 95 | scriptfile 96 | 97 | subtext 98 | Displays information about your battery 99 | title 100 | Battery 101 | type 102 | 0 103 | withspace 104 | 105 | 106 | type 107 | alfred.workflow.input.scriptfilter 108 | uid 109 | FC5EB146-FA11-4E33-8456-F60EA94E7EEE 110 | version 111 | 2 112 | 113 | 114 | config 115 | 116 | alfredfiltersresults 117 | 118 | alfredfiltersresultsmatchmode 119 | 0 120 | argumenttrimmode 121 | 0 122 | argumenttype 123 | 2 124 | escaping 125 | 102 126 | keyword 127 | battery 128 | queuedelaycustom 129 | 3 130 | queuedelayimmediatelyinitially 131 | 132 | queuedelaymode 133 | 0 134 | queuemode 135 | 1 136 | runningsubtext 137 | 138 | script 139 | # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH. 140 | readonly remote_info_plist='https://raw.githubusercontent.com/BenziAhamed/alfred-battery/master/src/info.plist' 141 | readonly workflow_url='BenziAhamed/alfred-battery' 142 | readonly download_type='github_release' 143 | readonly frequency_check='4' 144 | 145 | # FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED! 146 | function abort { 147 | echo "${1}" >&2 148 | exit 1 149 | } 150 | 151 | function url_exists { 152 | curl --silent --location --output /dev/null --fail --range 0-0 "${1}" 153 | } 154 | 155 | function notification { 156 | readonly local notificator="$(find . -type d -name 'Notificator.app')" 157 | if [[ -n "${notificator}" ]]; then 158 | "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available' 159 | return 160 | fi 161 | 162 | readonly local terminal_notifier="$(find . -type f -name 'terminal-notifier')" 163 | if [[ -n "${terminal_notifier}" ]]; then 164 | "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}" 165 | return 166 | fi 167 | 168 | osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\"" 169 | } 170 | 171 | # Local sanity checks 172 | readonly local_info_plist='info.plist' 173 | readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")" 174 | 175 | [[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.' 176 | [[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'." 177 | [[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number." 178 | 179 | # Check for updates 180 | if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then 181 | if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check 182 | 183 | readonly tmp_file="$(mktemp)" 184 | curl --silent --location --output "${tmp_file}" "${remote_info_plist}" 185 | readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")" 186 | 187 | if [[ "${local_version}" == "${remote_version}" ]]; then 188 | touch "${local_info_plist}" # Reset timer by touching local file 189 | exit 0 190 | fi 191 | 192 | if [[ "${download_type}" == 'page' ]]; then 193 | notification 'Opening download page…' 194 | open "${workflow_url}" 195 | exit 0 196 | fi 197 | 198 | download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")" 199 | 200 | if url_exists "${download_url}"; then 201 | notification 'Downloading and installing…' 202 | curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}" 203 | open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" 204 | else 205 | abort "'workflow_url' (${download_url}) appears to not be reachable." 206 | fi 207 | fi 208 | scriptargtype 209 | 1 210 | scriptfile 211 | 212 | subtext 213 | 214 | title 215 | 216 | type 217 | 0 218 | withspace 219 | 220 | 221 | type 222 | alfred.workflow.input.scriptfilter 223 | uid 224 | A35F4AB0-47ED-4FBB-8247-9EE1BCCAFFAD 225 | version 226 | 2 227 | 228 | 229 | readme 230 | 231 | uidata 232 | 233 | 3917FDAB-460E-4C28-A4A9-645B9773F34A 234 | 235 | xpos 236 | 30 237 | ypos 238 | 30 239 | 240 | A35F4AB0-47ED-4FBB-8247-9EE1BCCAFFAD 241 | 242 | colorindex 243 | 12 244 | note 245 | OneUpdater 246 | xpos 247 | 220 248 | ypos 249 | 170 250 | 251 | FC5EB146-FA11-4E33-8456-F60EA94E7EEE 252 | 253 | xpos 254 | 220 255 | ypos 256 | 30 257 | 258 | 259 | version 260 | 1.0 261 | webaddress 262 | benzi-ahamed.tumblr.com 263 | 264 | 265 | --------------------------------------------------------------------------------