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
37 (defgroup google-weather nil
41 (defcustom google-weather-use-https nil
42 "Default protocol to use to access the Google Weather API."
43 :group
'google-weather
46 (defconst google-weather-url
47 "www.google.com/ig/api"
48 "URL of the Google Weather API.")
50 (defconst google-weather-image-url
51 "http://www.google.com"
52 "URL prefix for images.")
54 (defcustom google-weather-unit-system-temperature-assoc
57 "Find temperature symbol from unit system."
58 :group
'google-weather
)
60 (defun google-weather-cache-expired (url expire-time
)
61 "Check if URL is cached for more than EXPIRE-TIME."
62 (cond (url-standalone-mode
63 (not (file-exists-p (url-cache-create-filename url
))))
64 (t (let ((cache-time (url-is-cached url
)))
69 (seconds-to-time expire-time
))
73 (defun google-weather-cache-fetch (url)
74 "Fetch URL from the cache."
75 (with-current-buffer (generate-new-buffer " *temp*")
76 (url-cache-extract (url-cache-create-filename url
))
79 (defun google-weather-retrieve-data-raw (url &optional expire-time
)
80 "Retrieve URL and return its data as string.
81 If EXPIRE-TIME is set, the data will be fetched from the cache if
82 their are not older than EXPIRE-TIME seconds. Otherwise, they
83 will be fetched and then cached. Therefore, setting EXPIRE-TIME
84 to 0 force a cache renewal."
85 (let* ((expired (if expire-time
86 (google-weather-cache-expired url expire-time
)
89 (url-retrieve-synchronously url
)
90 (google-weather-cache-fetch url
)))
92 (when (and expired expire-time
)
93 (url-store-in-cache buffer
))
96 (defun google-weather-retrieve-data (url &optional expire-time
)
97 "Retrieve URL and return its data as string.
98 If EXPIRE-TIME is set, the data will be fetched from the cache if
99 their are not older than EXPIRE-TIME seconds. Otherwise, they
100 will be fetched and then cached. Therefore, setting EXPIRE-TIME
101 to 0 force a cache renewal."
102 (with-current-buffer (google-weather-retrieve-data-raw
104 (goto-char (point-min))
105 (unless (search-forward "\n\n" nil t
)
106 (error "Data not found"))
107 (decode-coding-region
109 (detect-coding-region (point) (point-max) t
))
110 (set-buffer-multibyte t
)
111 (let ((data (xml-parse-region (point) (point-max))))
112 (kill-buffer (current-buffer))
115 (defun google-weather-build-url (location &optional language
)
116 "Build URL to retrieve weather for LOCATION in LANGUAGE."
117 (concat "http" (when google-weather-use-https
"s") "://" google-weather-url
"?weather=" (url-hexify-string location
)
119 (concat "&hl=" language
))))
121 (defun google-weather-get-data (location &optional language expire-time
)
122 "Get weather data for LOCATION in LANGUAGE.
123 See `google-weather-retrieve-data' for the use of EXPIRE-TIME."
124 (google-weather-retrieve-data
125 (google-weather-build-url location language
) expire-time
))
127 (defun google-weather-data->weather
(data)
128 "Return all weather information from DATA."
129 (cddr (assoc 'weather
(cdr (assoc 'xml_api_reply data
)))))
131 (defun google-weather-data->forecast-information
(data)
132 "Return the forecast information of DATA."
133 (cddr (assoc 'forecast_information
(google-weather-data->weather data
))))
135 (defun google-weather-assoc (key data
)
136 "Extract value of field KEY from DATA."
137 (cdr (assoc 'data
(cadr (assoc key data
)))))
139 (defun google-weather-data->city
(data)
140 "Return the city where the DATA come from."
141 (google-weather-assoc
143 (google-weather-data->forecast-information data
)))
145 (defun google-weather-data->postal-code
(data)
146 "Return the postal code where the DATA come from."
147 (google-weather-assoc
149 (google-weather-data->forecast-information data
)))
151 (defun google-weather-data->unit-system
(data)
152 "Return the unit system used for DATA."
153 (google-weather-assoc
155 (google-weather-data->forecast-information data
)))
157 (defun google-weather-data->forecast-date
(data)
158 "Return the unit system used for DATA."
159 (google-weather-assoc
161 (google-weather-data->forecast-information data
)))
163 (defun google-weather-data->forecast
(data)
164 "Get forecast list from DATA."
165 ;; Compute date of the forecast in the same format as `current-time'
166 (let ((date (apply 'encode-time
168 (concat (google-weather-data->forecast-date data
) " 00:00:00")))))
171 (let* ((forecast-date (decode-time date
))
172 (forecast-encoded-date (list (nth 4 forecast-date
)
173 (nth 3 forecast-date
)
174 (nth 5 forecast-date
))))
175 ;; Add one day to `date'
176 (setq date
(time-add date
(days-to-time 1)))
177 `(,forecast-encoded-date
178 (low ,(google-weather-assoc 'low forecast
))
179 (high ,(google-weather-assoc 'high forecast
))
180 (icon ,(concat google-weather-image-url
181 (google-weather-assoc 'icon forecast
)))
182 (condition ,(google-weather-assoc 'condition forecast
)))))
183 (loop for entry in
(google-weather-data->weather data
)
184 when
(eq (car entry
) 'forecast_conditions
)
187 (defun google-weather-data->forecast-for-date
(data date
)
188 "Return forecast for DATE from DATA.
189 DATE should be in the same format used by calendar,
190 i.e. (MONTH DAY YEAR)."
191 (cdr (assoc date
(google-weather-data->forecast data
))))
193 (defun google-weather-data->temperature-symbol
(data)
194 "Return the temperature to be used according in DATA.
195 It uses `google-weather-unit-system-temperature-assoc' to find a
197 (cdr (assoc (google-weather-data->unit-system data
) google-weather-unit-system-temperature-assoc
)))
200 (defun google-weather-data->problem-cause
(data)
201 "Return a string if DATA contains a problem cause, `nil' otherwise.
203 An error message example:
208 ((module_id . \"0\") (tab_id . \"0\") (mobile_row . \"0\")
209 (mobile_zipped . \"1\") (row . \"0\") (section . \"0\"))
210 (problem_cause ((data . \"Information is temporarily unavailable.\"))))))))"
211 (google-weather-assoc
213 (google-weather-data->weather data
)))
215 (provide 'google-weather
)