├── README.md ├── aqi-on-screensaver.sh ├── preview.notification.png └── preview.screensaver.png /README.md: -------------------------------------------------------------------------------- 1 | # AQI on ScreenSaver 2 | 3 | Air pollution has become a major issue in China, damaging thousands of people's 4 | health every single day. While government and related industrial enterprises is 5 | taking actions on pollution control, you also should pay attention to the air 6 | quality periodically in your living place to decide whether to enable the 7 | protective measures like air purifier. 8 | 9 | There are many ways we can get 10 | [AQI](https://en.wikipedia.org/wiki/Air_quality_index) in our life, it sounds 11 | better we have a yet another way to get AQI when the screen saver turns on. 12 | 13 | ## Features 14 | 15 | * Display AQI on your screen saver 16 | * Update AQI from aqicn.org periodically 17 | * Push notifications when AQI exceed the specified threshold level 18 | * Get local AQI based on IP address (thank [@feix](https://github.com/feix)) 19 | 20 | ## Previews 21 | 22 | ![AQI on screen saver](preview.screensaver.png) 23 | 24 | ![AQI notification](preview.notification.png) 25 | 26 | ## Requirements 27 | 28 | This script is designed for macOS, cannot be run in other platform. 29 | 30 | There are no external dependencies. 31 | 32 | Tested on macOS 10.12. 33 | 34 | ## Installation 35 | 36 | First of all, apply for a API token from [this site](http://aqicn.org/data-platform/token/). 37 | 38 | ```shell 39 | $ git clone https://github.com/aheadlead/aqi-on-screensaver 40 | $ cd aqi-on-screensaver 41 | $ ./aqi-on-screensaver.sh install 42 | ``` 43 | 44 | Installation script will create a service on your launchd daemon. Script 45 | aqi-on-screensaver.sh will be called by launchd periodically to fetch AQI from 46 | aqicn.org, therefore you should keep this script here so that launchd can find 47 | it. 48 | 49 | If you want to remove this script, simply run following lines in terminal. 50 | 51 | ```shell 52 | $ ./aqi-on-screensaver.sh uninstall 53 | ``` 54 | 55 | ## Known issues 56 | 57 | * The AQI value will not be updated on screen, when the screen saver appear, 58 | though this script did fetch the AQI value in background. 59 | 60 | ## Todos 61 | 62 | ## Author 63 | 64 | Wei Yulan 65 | 66 | -------------------------------------------------------------------------------- /aqi-on-screensaver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # --- config 4 | 5 | update_interval=1800 # seconds 6 | 7 | aqicn_api_token=_PLACEHOLDER_ 8 | 9 | # city name in pinyin 10 | location=nanjing 11 | # "y"=true; "n"=false 12 | get_location_by_ip_address=y 13 | 14 | # script trigger a notification when AQI exceeds the threshold value 15 | notification_threshold=50 16 | 17 | # the minimum interval between two notifications in seconds 18 | notification_cooldown=7200 19 | 20 | notification_title="AQI on ScreenSaver" 21 | 22 | launchd_service_name=com.aheadlead.aqi_on_screensaver 23 | 24 | 25 | # --- End of config part. Do not modify below this line. 26 | 27 | AQI_API_URL=http://api.waqi.info/feed/$location/?token=$aqicn_api_token 28 | LOCATION_API_URL=https://api.waqi.info/mapq/nearest/ 29 | LAUNCHAGENTS_PLIST_PATH=~/Library/LaunchAgents/$launchd_service_name.plist 30 | SCREENSAVE_PLIST_PATH=~/Library/Preferences/ByHost/com.apple.ScreenSaver.Basic.plist 31 | LAST_REFRESH_TIME=/tmp/aqi_on_screensaver.last_refresh_time 32 | LAST_NOTIFICATION_TIME=/tmp/aqi_on_screensaver.last_notification_time 33 | LAST_AQI_VALUE=/tmp/aqi_on_screensaver.last_aql_value 34 | LOG=/tmp/aqi_on_screensaver.log 35 | LAST_REFRESH_LOCATION=/tmp/aqi_on_screensaver.last_refresh_location 36 | 37 | function _write_config { 38 | sed -e "s/^$1=.*/$1=$2/" -i "" $0 39 | } 40 | 41 | function _install_config_input { 42 | local _TMP 43 | prompt=$1 44 | var_to_set=$2 45 | 46 | echo $prompt 47 | echo Now: $var_to_set=${!var_to_set} 48 | read -p "(leaving blank with no change) New: $var_to_set=" _TMP 49 | 50 | if [ ${#_TMP} -ne 0 ]; 51 | then 52 | _write_config $var_to_set $_TMP 53 | fi 54 | echo 55 | } 56 | 57 | function _install_config_branch { 58 | local _TMP 59 | prompt=$1 60 | 61 | read -p "$prompt (y/n, default: n)" _TMP 62 | echo 63 | 64 | if [ $_TMP != "y" ]; 65 | then 66 | return 0 67 | else 68 | return 1 69 | fi 70 | } 71 | 72 | function _get_location_by_ip_address { 73 | payload=$(curl -is $LOCATION_API_URL) 74 | echo $payload | grep "\"city\":\"" > /dev/null 75 | if [ $? -eq 0 ]; 76 | then 77 | location=$(echo $payload | grep -Eo -e '"city":"[^"]+' | cut -d '"' -f4 | tr '[A-Z]' '[a-z]') 78 | _write_config location $location 79 | echo $location > $LAST_REFRESH_LOCATION 80 | else 81 | echo $(date) Failed to fetch location >> $LOG 82 | fi 83 | } 84 | 85 | function _install_launchagents_plist { 86 | # get absolute path 87 | pushd $(dirname $0) > /dev/null 88 | ABSOLUTE_PATH=$(pwd -P)/$(basename $0) 89 | popd > /dev/null 90 | 91 | PLIST=$(cat < 93 | 95 | 96 | 97 | Label 98 | $launchd_service_name 99 | Program 100 | $ABSOLUTE_PATH 101 | StartInterval 102 | $update_interval 103 | RunAtLoad 104 | 105 | WorkingDirectory 106 | /tmp 107 | 108 | 109 | EOF 110 | ) 111 | echo + Checking environment 112 | if [ $(uname -s) != "Darwin" ]; 113 | then 114 | echo This script cannot be run in your platform, excepted "Darwin" but $(uname -s). 115 | exit 1 116 | fi 117 | echo 118 | 119 | _install_config_input "+ Set your API token you applied from aqicn.org." aqicn_api_token 120 | _install_config_branch "+ Do you want this script detect your city by IP address?" 121 | if [ $? -eq 1 ]; 122 | then 123 | _write_config get_location_by_ip_address y 124 | _get_location_by_ip_address 125 | _install_config_branch "+ Here is your city: $location, Right?" 126 | if [ $? -eq 0 ]; 127 | then 128 | _write_config get_location_by_ip_address n 129 | _install_config_input "+ Set the city name which you want aqi-on-screensaver notice you." location 130 | fi 131 | else 132 | _write_config get_location_by_ip_address n 133 | _install_config_input "+ Set the city name which you want aqi-on-screensaver notice you." location 134 | fi 135 | _install_config_input "+ Set the threshold value. If AQI exceeds the value, aqi-screensaver will push a notification." notification_threshold 136 | 137 | echo + Creating launchd services $launchd_service_name 138 | echo $PLIST > $LAUNCHAGENTS_PLIST_PATH 139 | echo 140 | 141 | echo + Starting service $launchd_service_name 142 | pushd $(dirname $LAUNCHAGENTS_PLIST_PATH) > /dev/null 143 | launchctl load $(basename $LAUNCHAGENTS_PLIST_PATH) 144 | popd > /dev/null 145 | echo 146 | } 147 | 148 | function _uninstall_launchagents_plist { 149 | echo + Stopping service $launchd_service_name 150 | pushd $(dirname $LAUNCHAGENTS_PLIST_PATH) > /dev/null 151 | launchctl unload $(basename $LAUNCHAGENTS_PLIST_PATH) 152 | popd > /dev/null 153 | echo 154 | 155 | echo + Removing launchd service $launchd_service_name 156 | rm -i $LAUNCHAGENTS_PLIST_PATH 157 | echo 158 | 159 | echo + Removing temporary files 160 | rm -i /tmp/aqi_on_screensaver.* 161 | echo 162 | } 163 | 164 | 165 | 166 | # Set the title of screensaver 167 | # e.g. _write_screensaver_plist "meiyoubaage" 168 | function _write_screensaver_plist { 169 | defaults write $SCREENSAVE_PLIST_PATH MESSAGE "$1" 170 | } 171 | 172 | # Pop a notification at the right top corner on the screen 173 | # e.g. _pop_notification "Text" 174 | function _pop_notification { 175 | osascript -e "display notification \"$1\" with title \"$notification_title\"" 176 | } 177 | 178 | 179 | 180 | function refresh { 181 | echo $(date) > $LAST_REFRESH_TIME 182 | 183 | if [ $get_location_by_ip_address = "y" ]; 184 | then 185 | _TMP=$(cat $LAST_REFRESH_LOCATION) 186 | _get_location_by_ip_address 187 | if [ "$_TMP"x != ""x -a "$location" != "$_TMP" ]; 188 | then 189 | _pop_notification "位置由 $_TMP 变化为 $location" 190 | fi 191 | fi 192 | 193 | payload=$(curl -is $AQI_API_URL) 194 | 195 | echo $payload | grep "\"status\":\"ok\"" > /dev/null 196 | if [ $? -ne 0 ]; 197 | then 198 | echo $(date) Failed to fetch API: $payload >> $LOG 199 | exit 1 200 | fi 201 | 202 | aqi_value=$(echo $payload | grep -Eo -e "\"aqi\":[0-9]+" | tail -c +7) 203 | echo $aqi_value > $LAST_AQI_VALUE 204 | echo $(date) AQI: $aqi_value >> $LOG 205 | 206 | if [ $aqi_value -gt `expr 2 \* $notification_threshold` ]; 207 | then 208 | _write_screensaver_plist "AQI: $aqi_value 😫" ; 209 | elif [ $aqi_value -gt $notification_threshold ]; 210 | then 211 | _write_screensaver_plist "AQI: $aqi_value 😣" ; 212 | elif [ $aqi_value -gt 10 ]; 213 | then 214 | _write_screensaver_plist "AQI: $aqi_value 😁" ; 215 | else 216 | _write_screensaver_plist "AQI: $aqi_value 😆🎉" ; 217 | fi 218 | 219 | if [ $aqi_value -gt $notification_threshold ]; 220 | then 221 | if [ ! -f $LAST_NOTIFICATION_TIME ]; 222 | then 223 | echo 0 > $LAST_NOTIFICATION_TIME 224 | fi 225 | last_notification_time=$(cat $LAST_NOTIFICATION_TIME) 226 | if [ $(date +%s) -gt `expr $last_notification_time + $notification_cooldown` ]; 227 | then 228 | _pop_notification "空气质量恶化,当前 AQI 为 $aqi_value" 229 | echo $(date +%s) > $LAST_NOTIFICATION_TIME 230 | echo $(date) Triggered a notification >> $LOG 231 | else 232 | echo $(date) The AQI exceeded the threshold level but still waiting for cooldown >> $LOG 233 | fi 234 | fi 235 | 236 | } 237 | 238 | 239 | case $1 in 240 | install) _install_launchagents_plist ;; 241 | uninstall) _uninstall_launchagents_plist ;; 242 | *) refresh ;; 243 | esac 244 | 245 | -------------------------------------------------------------------------------- /preview.notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aheadlead/aqi-on-screensaver/276d290b9c37f71514c2ef7a4b6fb95610c04191/preview.notification.png -------------------------------------------------------------------------------- /preview.screensaver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aheadlead/aqi-on-screensaver/276d290b9c37f71514c2ef7a4b6fb95610c04191/preview.screensaver.png --------------------------------------------------------------------------------