]> git.donarmstrong.com Git - lib.git/blob - emacs_el/google-weather.el
add helm google and zap-to-char
[lib.git] / emacs_el / google-weather.el
1 ;;; google-weather.el --- Fetch Google Weather forecasts.
2
3 ;; Copyright (C) 2010 Julien Danjou
4
5 ;; Author: Julien Danjou <julien@danjou.info>
6 ;; Keywords: comm
7
8 ;; This file is NOT part of GNU Emacs.
9
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.
14
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.
19
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/>.
22
23 ;;; Commentary:
24 ;; This module allows you to fetch Google Weather forecast from the
25 ;; Internet.
26 ;;
27 ;;; Code:
28
29 (require 'url)
30 (require 'url-cache)
31 (require 'xml)
32 (require 'time-date)
33
34 (eval-when-compile
35   (require 'cl))
36
37 (defgroup google-weather nil
38   "Google Weather."
39   :group 'comm)
40
41 (defcustom google-weather-use-https nil
42   "Default protocol to use to access the Google Weather API."
43   :group 'google-weather
44   :type 'boolean)
45
46 (defconst google-weather-url
47   "www.google.com/ig/api"
48   "URL of the Google Weather API.")
49
50 (defconst google-weather-image-url
51   "http://www.google.com"
52   "URL prefix for images.")
53
54 (defcustom google-weather-unit-system-temperature-assoc
55   '(("SI" . "℃")
56     ("US" . "℉"))
57   "Find temperature symbol from unit system."
58   :group 'google-weather)
59
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)))
65              (if cache-time
66                  (time-less-p
67                   (time-add
68                    cache-time
69                    (seconds-to-time expire-time))
70                   (current-time))
71                t)))))
72
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))
77     (current-buffer)))
78
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)
87                     t))
88          (buffer (if expired
89                      (url-retrieve-synchronously url)
90                    (google-weather-cache-fetch url)))
91          data)
92     (when (and expired expire-time)
93       (url-store-in-cache buffer))
94     buffer))
95
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
103                           url expire-time)
104       (goto-char (point-min))
105       (unless (search-forward "\n\n" nil t)
106         (error "Data not found"))
107       (decode-coding-region
108        (point) (point-max)
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))
113         data)))
114
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)
118           (when language
119             (concat "&hl=" language))))
120
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))
126
127 (defun google-weather-data->weather (data)
128   "Return all weather information from DATA."
129   (cddr (assoc 'weather (cdr (assoc 'xml_api_reply data)))))
130
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))))
134
135 (defun google-weather-assoc (key data)
136   "Extract value of field KEY from DATA."
137   (cdr (assoc 'data (cadr (assoc key data)))))
138
139 (defun google-weather-data->city (data)
140   "Return the city where the DATA come from."
141   (google-weather-assoc
142    'city
143    (google-weather-data->forecast-information data)))
144
145 (defun google-weather-data->postal-code (data)
146   "Return the postal code where the DATA come from."
147   (google-weather-assoc
148    'postal_code
149    (google-weather-data->forecast-information data)))
150
151 (defun google-weather-data->unit-system (data)
152   "Return the unit system used for DATA."
153   (google-weather-assoc
154    'unit_system
155    (google-weather-data->forecast-information data)))
156
157 (defun google-weather-data->forecast-date (data)
158   "Return the unit system used for DATA."
159   (google-weather-assoc
160    'forecast_date
161    (google-weather-data->forecast-information data)))
162
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
167                      (parse-time-string
168                       (concat (google-weather-data->forecast-date data) " 00:00:00")))))
169     (mapcar
170      (lambda (forecast)
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)
185            collect entry))))
186
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))))
192
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
196 match."
197   (cdr (assoc (google-weather-data->unit-system data) google-weather-unit-system-temperature-assoc)))
198
199
200 (defun google-weather-data->problem-cause (data)
201   "Return a string if DATA contains a problem cause, `nil' otherwise.
202
203 An error message example:
204
205 ((xml_api_reply
206   ((version . \"1\"))
207   (weather
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
212    'problem_cause
213    (google-weather-data->weather data)))
214
215 (provide 'google-weather)