]> git.donarmstrong.com Git - lib.git/blob - emacs_el/google-weather.el
add google weather mode
[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 (defgroup google-weather nil
35   "Google Weather."
36   :group 'comm)
37
38 (defconst google-weather-url
39   "http://www.google.com/ig/api"
40   "URL used to access the Google Weather API.")
41
42 (defconst google-weather-image-url
43   "http://www.google.com"
44   "URL prefix for images.")
45
46 (defcustom google-weather-unit-system-temperature-assoc
47   '(("SI" . "℃")
48     ("US" . "℉"))
49   "Find temperature symbol from unit system."
50   :group 'google-weather)
51
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)))
57              (if cache-time
58                  (time-less-p
59                   (time-add
60                    (url-is-cached url)
61                    (seconds-to-time expire-time))
62                   (current-time))
63                t)))))
64
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))
69     (current-buffer)))
70
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)
79                     t))
80          (buffer (if expired
81                      (url-retrieve-synchronously url)
82                    (google-weather-cache-fetch url)))
83          data)
84     (with-current-buffer buffer
85       (goto-char (point-min))
86       (search-forward "\n\n")
87       (decode-coding-region
88        (point) (point-max)
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))
95       data)))
96
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)
100           (when language
101             (concat "&hl=" language))))
102
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))
108
109 (defun google-weather-data->weather (data)
110   "Return all weather information from DATA."
111   (cddr (assoc 'weather (cdr (assoc 'xml_api_reply data)))))
112
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))))
116
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)))))
120
121 (defun google-weather-data->city (data)
122   "Return the city where the DATA come from."
123   (google-weather-assoc
124    'city
125    (google-weather-data->forecast-information data)))
126
127 (defun google-weather-data->postal-code (data)
128   "Return the postal code where the data come from."
129   (google-weather-assoc
130    'postal_code
131    (google-weather-data->forecast-information data)))
132
133 (defun google-weather-data->unit-system (data)
134   "Return the unit system used for data."
135   (google-weather-assoc
136    'unit_system
137    (google-weather-data->forecast-information data)))
138
139 (defun google-weather-data->forecast-date (data)
140   "Return the unit system used for data."
141   (google-weather-assoc
142    'forecast_date
143    (google-weather-data->forecast-information data)))
144
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
149                      (parse-time-string
150                       (concat (google-weather-data->forecast-date data) " 00:00:00")))))
151     (mapcar
152      (lambda (forecast)
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)
167            collect entry))))
168
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)."
172   (cdr (assoc date
173               (google-weather-data->forecast data))))
174
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)))
178
179 (provide 'google-weather)