1 ;;; google-weather.el --- Fetch Google Weather forecasts.
3 ;; Copyright (C) 2010 Julien Danjou
5 ;; Author: Julien Danjou <julien@danjou.info>
8 ;; This file is NOT part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;; This module allows you to fetch Google Weather forecast from the
34 (defgroup google-weather nil
38 (defconst google-weather-url
39 "http://www.google.com/ig/api"
40 "URL used to access the Google Weather API.")
42 (defconst google-weather-image-url
43 "http://www.google.com"
44 "URL prefix for images.")
46 (defcustom google-weather-unit-system-temperature-assoc
49 "Find temperature symbol from unit system."
50 :group 'google-weather)
52 (defun google-weather-cache-expired (url expire-time)
53 "Check if URL is cached for more than EXPIRE-TIME."
54 (cond (url-standalone-mode
55 (not (file-exists-p (url-cache-create-filename url))))
56 (t (let ((cache-time (url-is-cached url)))
61 (seconds-to-time expire-time))
65 (defun google-weather-cache-fetch (url)
66 "Fetch URL from the cache."
67 (with-current-buffer (generate-new-buffer " *temp*")
68 (url-cache-extract (url-cache-create-filename url))
71 (defun google-weather-retrieve-data (url &optional expire-time)
72 "Retrieve URL and return its data as string.
73 If EXPIRE-TIME is set, the data will be fetched from the cache if
74 their are not older than EXPIRE-TIME seconds. Otherwise, they
75 will be fetched and then cached. Therefore, setting EXPIRE-TIME
76 to 0 force a cache renewal."
77 (let* ((expired (if expire-time
78 (google-weather-cache-expired url expire-time)
81 (url-retrieve-synchronously url)
82 (google-weather-cache-fetch url)))
84 (with-current-buffer buffer
85 (goto-char (point-min))
86 (search-forward "\n\n")
89 (detect-coding-region (point) (point-max) t))
90 (set-buffer-multibyte t)
91 (setq data (xml-parse-region (point) (point-max)))
92 (when (and expired expire-time)
93 (url-store-in-cache (current-buffer)))
94 (kill-buffer (current-buffer))
97 (defun google-weather-build-url (location &optional language)
98 "Build URL to retrieve weather for LOCATION in LANGUAGE."
99 (concat google-weather-url "?weather=" (url-hexify-string location)
101 (concat "&hl=" language))))
103 (defun google-weather-get-data (location &optional language expire-time)
104 "Get weather data for LOCATION in LANGUAGE.
105 See `google-weather-retrieve-data' for the use of EXPIRE-TIME."
106 (google-weather-retrieve-data
107 (google-weather-build-url location language) expire-time))
109 (defun google-weather-data->weather (data)
110 "Return all weather information from DATA."
111 (cddr (assoc 'weather (cdr (assoc 'xml_api_reply data)))))
113 (defun google-weather-data->forecast-information (data)
114 "Return the forecast information of DATA."
115 (cddr (assoc 'forecast_information (google-weather-data->weather data))))
117 (defun google-weather-assoc (key data)
118 "Do some sort of magic 'assoc to find fields in DATA."
119 (cdr (assoc 'data (cadr (assoc key data)))))
121 (defun google-weather-data->city (data)
122 "Return the city where the DATA come from."
123 (google-weather-assoc
125 (google-weather-data->forecast-information data)))
127 (defun google-weather-data->postal-code (data)
128 "Return the postal code where the data come from."
129 (google-weather-assoc
131 (google-weather-data->forecast-information data)))
133 (defun google-weather-data->unit-system (data)
134 "Return the unit system used for data."
135 (google-weather-assoc
137 (google-weather-data->forecast-information data)))
139 (defun google-weather-data->forecast-date (data)
140 "Return the unit system used for data."
141 (google-weather-assoc
143 (google-weather-data->forecast-information data)))
145 (defun google-weather-data->forecast (data)
146 "Get forecast list from DATA."
147 ;; Compute date of the forecast in the same format as `current-time'
148 (let ((date (apply 'encode-time
150 (concat (google-weather-data->forecast-date data) " 00:00:00")))))
153 (let* ((forecast-date (decode-time date))
154 (forecast-encoded-date (list (nth 4 forecast-date)
155 (nth 3 forecast-date)
156 (nth 5 forecast-date))))
157 ;; Add one day to `date'
158 (setq date (time-add date (days-to-time 1)))
159 `(,forecast-encoded-date
160 (low ,(google-weather-assoc 'low forecast))
161 (high ,(google-weather-assoc 'high forecast))
162 (icon ,(concat google-weather-image-url
163 (google-weather-assoc 'icon forecast)))
164 (condition ,(google-weather-assoc 'condition forecast)))))
165 (loop for entry in (google-weather-data->weather data)
166 when (eq (car entry) 'forecast_conditions)
169 (defun google-weather-data->forecast-for-date (data date)
170 "Return forecast for DATE from DATA.
171 DATE should be in the same format used by calendar i.e. (MONTH DAY YEAR)."
173 (google-weather-data->forecast data))))
175 (defun google-weather-data->temperature-symbol (data)
176 "Return the temperature to be used according to `google-weather-unit-system-temperature-assoc' in DATA."
177 (cdr (assoc (google-weather-data->unit-system data) google-weather-unit-system-temperature-assoc)))
179 (provide 'google-weather)