Air Quality Index in Melbourne is 50 the dominant pollutant is pm25

This commit is contained in:
nik gaffney 2020-02-16 00:34:56 +11:00
parent dff57a346b
commit 265ab11044

138
aqi.el
View file

@ -1,4 +1,4 @@
;;; aqi.el --- Air quality data from the World Air Quality Index. -*- lexical-binding: t; -*- ;;; aqi.el --- Air quality data from the World Air Quality Index -*- lexical-binding: t; -*-
;; Copyright 2020 FoAM ;; Copyright 2020 FoAM
;; ;;
@ -72,14 +72,14 @@
"Clear the cached data, optionally only for a given CITY." "Clear the cached data, optionally only for a given CITY."
(if city (if city
(setq aqi-cached-data (setq aqi-cached-data
(assq-delete-all city aqi-cached-data)) (assq-delete-all city aqi-cached-data))
(setq aqi-cached-data '(("None" . "None"))))) (setq aqi-cached-data '(("None" . "None")))))
(defun aqi--city-cache-update (city) (defun aqi--city-cache-update (city)
"Add or update cached data for a given CITY." "Add or update cached data for a given CITY."
(progn (aqi--city-cache-clear city) (aqi--city-cache-clear city)
(push (cons city (aqi-request city)) (push (cons city (aqi-request city))
aqi-cached-data))) aqi-cached-data))
(defun aqi--city-cache-get (city) (defun aqi--city-cache-get (city)
"Add or update cached data for a given CITY." "Add or update cached data for a given CITY."
@ -97,16 +97,16 @@
(defmacro aqi--make-city-raw-accessor (name aref) (defmacro aqi--make-city-raw-accessor (name aref)
"Macro to create an accesor NAME with a 'let-alist' AREF (or function)." "Macro to create an accesor NAME with a 'let-alist' AREF (or function)."
`(fset ,name `(fset ,name
(lambda (city) (lambda (city)
(aqi-request city) (aqi-request city)
(let-alist (assoc-default city aqi-cached-data) ,aref)))) (let-alist (assoc-default city aqi-cached-data) ,aref))))
(defmacro aqi--make-city-format-accessor (name aref) (defmacro aqi--make-city-format-accessor (name aref)
"Macro to create an accesor NAME with a 'let-alist' AREF (or function)." "Macro to create an accesor NAME with a 'let-alist' AREF (or function)."
`(fset ,name `(fset ,name
(lambda (city) (lambda (city)
(aqi-request city) (aqi-request city)
(format "%s" (let-alist (assoc-default city aqi-cached-data) ,aref))))) (format "%s" (let-alist (assoc-default city aqi-cached-data) ,aref)))))
;; various accessors (added as needed...) ;; various accessors (added as needed...)
@ -115,7 +115,7 @@
;; Function to return the coordinates of a city (by name) as a string ;; Function to return the coordinates of a city (by name) as a string
(aqi--make-city-format-accessor 'aqi-city-lonlat (aqi--make-city-format-accessor 'aqi-city-lonlat
(format "%s, %s" (elt .city.geo 0) (elt .city.geo 1))) (format "%s, %s" (elt .city.geo 0) (elt .city.geo 1)))
;; API requests for AQI info ;; API requests for AQI info
@ -128,15 +128,15 @@
:params `(("token" . ,aqi-api-key)) :params `(("token" . ,aqi-api-key))
:parser 'json-read :parser 'json-read
:success (cl-function :success (cl-function
(lambda (&key data &allow-other-keys) (lambda (&key data &allow-other-keys)
(pcase (assoc-default 'status data) (pcase (assoc-default 'status data)
("ok" (push (cons city (assoc-default 'data data)) ("ok" (push (cons city (assoc-default 'data data))
aqi-cached-data)) aqi-cached-data))
("error" (push (cons city (format "Request error: %s" (assoc-default 'data data))) ("error" (push (cons city (format "Request error: %s" (assoc-default 'data data)))
aqi-cached-data))))) aqi-cached-data)))))
:error (cl-function :error (cl-function
(lambda (&rest args &key error-thrown &allow-other-keys) (lambda (&rest args &key error-thrown &allow-other-keys)
(message "WAQI error: %s" error-thrown))))) (message "WAQI error: %s" error-thrown)))))
(defun aqi-request-geo (latitude longitude) (defun aqi-request-geo (latitude longitude)
"Request details by LATITUDE and LONGITUDE from WAQI." "Request details by LATITUDE and LONGITUDE from WAQI."
@ -146,11 +146,11 @@
:params `(("token" . ,aqi-api-key)) :params `(("token" . ,aqi-api-key))
:parser 'json-read :parser 'json-read
:success (cl-function :success (cl-function
(lambda (&key data &allow-other-keys) (lambda (&key data &allow-other-keys)
(message "200: %s" data))) (message "200: %s" data)))
:error (cl-function :error (cl-function
(lambda (&rest args &key error-thrown &allow-other-keys) (lambda (&rest args &key error-thrown &allow-other-keys)
(message "WAQI error: %s" error-thrown))))) (message "WAQI error: %s" error-thrown)))))
(defun aqi-request-cached (city) (defun aqi-request-cached (city)
"Request details for CITY from cached data or direct from WAQI." "Request details for CITY from cached data or direct from WAQI."
@ -160,19 +160,19 @@
(defun aqi-search (name) (defun aqi-search (name)
"Search for the nearest stations (if any) matching a given NAME." "Search for the nearest stations (if any) matching a given NAME."
(request (request
(format "https://api.waqi.info/search/?keyword=%s&" name) (format "https://api.waqi.info/search/?keyword=%s&" name)
:sync t :sync t
:params `(("token" . ,aqi-api-key)) :params `(("token" . ,aqi-api-key))
:parser 'json-read :parser 'json-read
:success (cl-function :success (cl-function
(lambda (&key data &allow-other-keys) (lambda (&key data &allow-other-keys)
(pcase (assoc-default 'status data) (pcase (assoc-default 'status data)
("ok" (message "Search: %s" (assoc-default 'data data))) ("ok" (message "Search: %s" (assoc-default 'data data)))
("error" (message "Search error: %s" (assoc-default 'data data)))))) ("error" (message "Search error: %s" (assoc-default 'data data))))))
:error (cl-function :error (cl-function
(lambda (&rest args &key error-thrown &allow-other-keys) (lambda (&rest args &key error-thrown &allow-other-keys)
(message "WAQI error: %s" error-thrown))))) (message "WAQI error: %s" error-thrown)))))
;; printing, formatting and presenting. ;; printing, formatting and presenting.
@ -180,28 +180,28 @@
(defun aqi-report-brief (&optional place) (defun aqi-report-brief (&optional place)
"General air quality info from PLACE as a string." "General air quality info from PLACE as a string."
(let ((city (if (and (string< "" place) place) place "here"))) (let ((city (if (and (string< "" place) place) place "here")))
(if aqi-use-cache (if aqi-use-cache
(aqi-request-cached city) (aqi-request-cached city)
(aqi-request city)) (aqi-request city))
(let-alist (aqi--city-cache-get city) (let-alist (aqi--city-cache-get city)
(format "Air Quality Index in %s is %s and the dominant pollutant is %s%s" (format "Air Quality Index in %s is %s and the dominant pollutant is %s%s"
.city.name .aqi .dominentpol .city.name .aqi .dominentpol
(if aqi-use-cache " (cached)" ""))))) (if aqi-use-cache " (cached)" "")))))
;;;###autoload ;;;###autoload
(defun aqi-report-full (&optional place) (defun aqi-report-full (&optional place)
"Detailed air quality info from PLACE as a string." "Detailed air quality info from PLACE as a string."
(let ((city (if (and (string< "" place) place) place "here"))) (let ((city (if (and (string< "" place) place) place "here")))
(if aqi-use-cache (if aqi-use-cache
(aqi-request-cached city) (aqi-request-cached city)
(aqi-request city)) (aqi-request city))
(let ((data (aqi--city-cache-get city))) (let ((data (aqi--city-cache-get city)))
;; simple typecheck -> error handling since semantic errors are cached as strings. ;; simple typecheck -> error handling since semantic errors are cached as strings.
(if (stringp data) (if (stringp data)
(format "%s (%s)" data city) (format "%s (%s)" data city)
(let-alist data (let-alist data
(format (format
"Air Quality index in %s is %s as of %s (UTC%s). "Air Quality index in %s is %s as of %s (UTC%s).
\nDominant pollutant is %s \nDominant pollutant is %s
PM2.5 (fine particulate matter): %s PM2.5 (fine particulate matter): %s
PM10 (respirable particulate matter): %s PM10 (respirable particulate matter): %s
@ -213,38 +213,38 @@ Air pressure: %s
Wind: %s Wind: %s
\nFurther details can be found at %s \nFurther details can be found at %s
\nData provided by %s and %s%s" \nData provided by %s and %s%s"
.city.name .city.name
.aqi .aqi
.time.s .time.s
.time.tz .time.tz
.dominentpol .dominentpol
.iaqi.pm25.v .iaqi.pm25.v
.iaqi.pm10.v .iaqi.pm10.v
.iaqi.no2.v .iaqi.no2.v
.iaqi.co.v .iaqi.co.v
.iaqi.t.v .iaqi.t.v
.iaqi.h.v .iaqi.h.v
.iaqi.p.v .iaqi.p.v
.iaqi.wg.v .iaqi.wg.v
.city.url .city.url
(let-alist (elt .attributions 0) .name) (let-alist (elt .attributions 0) .name)
(let-alist (elt .attributions 1) .name) (let-alist (elt .attributions 1) .name)
(if aqi-use-cache " (cached)" ""))))))) (if aqi-use-cache " (cached)" "")))))))
;;;###autoload ;;;###autoload
(defun aqi-report (&optional place type) (defun aqi-report (&optional place type)
"General air quality info from PLACE (or 'here' if no args are given) report TYPE can be 'brief' or 'full'." "General air quality info from PLACE (or 'here' if no args are given) report TYPE can be 'brief' or 'full'."
(interactive "sName of city or monitoring station (RET for \"here\"): ") (interactive "sName of city or monitoring station (RET for \"here\"): ")
(let* ((city (if (and (string< "" place) place) place "here")) (let* ((city (if (and (string< "" place) place) place "here"))
(aqi-output (get-buffer-create (format "*Air Quality - %s*" city)))) (aqi-output (get-buffer-create (format "*Air Quality - %s*" city))))
(with-current-buffer aqi-output (with-current-buffer aqi-output
(goto-char (point-min)) (goto-char (point-min))
(erase-buffer) (erase-buffer)
(pcase type (pcase type
('brief (insert (aqi-report-brief city))) ('brief (insert (aqi-report-brief city)))
('full (insert (aqi-report-full city))) ('full (insert (aqi-report-full city)))
('nil (insert (aqi-report-full city))) ('nil (insert (aqi-report-full city)))
(other (warn "Unknown report type: '%s. Try using 'full or 'brief" other))) (other (warn "Unknown report type: '%s. Try using 'full or 'brief" other)))
(goto-char (point-max)) (goto-char (point-max))
(insert "")) (insert ""))
(display-buffer aqi-output)) (display-buffer aqi-output))