Merge remote-tracking branch 'origin/master'
[enigma2-plugins.git] / weatherplugin / src / plugin.py
1 # -*- coding: utf-8 -*-
2 #
3 #  Weather Plugin E2
4 #
5 #  $Id$
6 #
7 #  Coded by Dr.Best (c) 2009
8 #  Support: www.dreambox-tools.info
9 #
10 #  This program is free software; you can redistribute it and/or
11 #  modify it under the terms of the GNU General Public License
12 #  as published by the Free Software Foundation; either version 2
13 #  of the License, or (at your option) any later version.
14 #
15 #  This program 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
21 # for localized messages
22 from . import _
23
24 from Plugins.Plugin import PluginDescriptor
25 from Screens.Screen import Screen
26 from Components.ActionMap import ActionMap
27 from Components.Sources.StaticText import StaticText
28 from xml.etree.cElementTree import fromstring as cet_fromstring
29 from twisted.internet import defer
30 from twisted.web.client import getPage, downloadPage
31 from urllib import quote
32 from Components.Pixmap import Pixmap
33 from enigma import ePicLoad
34 from os import path as os_path, mkdir as os_mkdir
35 from Components.AVSwitch import AVSwitch
36 from Components.config import ConfigSubsection, ConfigSubList, ConfigInteger, config
37 from setup import initConfig, WeatherPluginEntriesListConfigScreen
38
39 config.plugins.WeatherPlugin = ConfigSubsection()
40 config.plugins.WeatherPlugin.entriescount =  ConfigInteger(0)
41 config.plugins.WeatherPlugin.Entries = ConfigSubList()
42 initConfig()
43
44 UserAgent = "Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.15) Gecko/2009102815 Ubuntu/9.04 (jaunty) Firefox/3."
45
46 class WeatherIconItem:
47         def __init__(self, url = "", filename = "", index = -1, error = False):
48                 self.url = url
49                 self.filename = filename
50                 self.index = index
51                 self.error = error
52
53 def getXML(url):
54         return getPage(url, agent=UserAgent)
55
56 def download(item):
57         return downloadPage(item.url, file(item.filename, 'wb'), agent=UserAgent)
58
59
60 def main(session,**kwargs):
61         session.open(WeatherPlugin)
62
63 def Plugins(**kwargs):
64         list = [PluginDescriptor(name=_("Weather Plugin"), description=_("Show Weather Forecast"), where = [PluginDescriptor.WHERE_PLUGINMENU, PluginDescriptor.WHERE_EXTENSIONSMENU], fnc=main)]
65         return list
66
67
68 class WeatherPlugin(Screen):
69
70         skin = """
71                 <screen name="WeatherPlugin" position="center,center" size="664,190" title="%s">
72                         <widget render="Label" source="caption" position="10,20" zPosition="1" size="600,23" font="Regular;22" transparent="1"/>
73                         <ePixmap position="620,22" zPosition="1" size="36,20" pixmap="skin_default/buttons/key_menu.png" alphatest="on" />
74                         <widget render="Label" source="currentTemp" position="10,45" zPosition="1" size="300,23" font="Regular;22" transparent="1"/>
75                         <widget render="Label" source="condition" position="10,100" zPosition="1" size="300,20" font="Regular;18" transparent="1"/>
76                         <widget render="Label" source="wind_condition" position="10,125" zPosition="1" size="300,20" font="Regular;18" transparent="1"/>
77                         <widget render="Label" source="humidity" position="10,150" zPosition="1" size="300,20" font="Regular;18" valign="bottom" transparent="1"/>
78                         <widget render="Label" source="weekday1" position="255,50" zPosition="1" size="72,20" halign="center" valign="center" font="Regular;18" transparent="1"/>
79                         <widget name="weekday1_icon" position="255,70" zPosition="1" size="72,72" alphatest="blend"/>
80                         <widget render="Label" source="weekday1_temp" position="241,150" zPosition="1" size="100,20" halign="center" valign="bottom" font="Regular;16" transparent="1"/>
81                         <widget render="Label" source="weekday2" position="358,50" zPosition="1" size="72,20" halign="center" valign="center" font="Regular;18" transparent="1"/>
82                         <widget name="weekday2_icon" position="358,70" zPosition="1" size="72,72" alphatest="blend"/>
83                         <widget render="Label" source="weekday2_temp" position="344,150" zPosition="1" size="100,20" halign="center" valign="bottom" font="Regular;16" transparent="1"/>
84                         <widget render="Label" source="weekday3" position="461,50" zPosition="1" size="72,20" halign="center" valign="center" font="Regular;18" transparent="1"/>
85                         <widget name="weekday3_icon" position="461,70" zPosition="1" size="72,72" alphatest="blend"/>
86                         <widget render="Label" source="weekday3_temp" position="448,150" zPosition="1" size="100,20" halign="center" valign="bottom" font="Regular;16" transparent="1"/>
87                         <widget render="Label" source="weekday4" position="564,50" zPosition="1" size="72,20" halign="center" valign="center" font="Regular;18" transparent="1"/>
88                         <widget name="weekday4_icon" position="564,70" zPosition="1" size="72,72" alphatest="blend"/>
89                         <widget render="Label" source="weekday4_temp" position="550,150" zPosition="1" size="100,20" halign="center" valign="bottom" font="Regular;16" transparent="1"/>
90                         <widget render="Label" source="statustext" position="0,0" zPosition="1" size="664,190" font="Regular;20" halign="center" valign="center" transparent="1"/>
91                 </screen>"""
92         
93         def __init__(self, session):
94                 Screen.__init__(self, session)
95                 self.title = _("Weather Plugin")
96                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
97                 {
98                         "back": self.close,
99                         "input_date_time": self.config,
100                         "right": self.nextItem,
101                         "left": self.previousItem
102                 }, -1)
103
104                 self["statustext"] = StaticText()
105                 self["caption"] = StaticText()
106                 self["currentTemp"] = StaticText()
107                 self["condition"] = StaticText()
108                 self["wind_condition"] = StaticText()
109                 self["humidity"] = StaticText()
110
111                 i = 1
112                 while i < 5:
113                         self["weekday%s" % i] = StaticText()
114                         self["weekday%s_icon" %i] = WeatherIcon()
115                         self["weekday%s_temp" % i] = StaticText()
116                         i += 1
117                 del i
118
119                 self.appdir = "/usr/lib/enigma2/python/Plugins/Extensions/WeatherPlugin/icons/" 
120                 if not os_path.exists(self.appdir):
121                         os_mkdir(self.appdir)
122
123                 self.weatherPluginEntryIndex = -1
124                 self.weatherPluginEntryCount = config.plugins.WeatherPlugin.entriescount.value
125                 if self.weatherPluginEntryCount >= 1:
126                         self.weatherPluginEntry = config.plugins.WeatherPlugin.Entries[0]
127                         self.weatherPluginEntryIndex = 1
128                 else:
129                         self.weatherPluginEntry = None
130
131                 self.onLayoutFinish.append(self.startRun)
132
133         def startRun(self):
134                 if self.weatherPluginEntry is not None:
135                         self["statustext"].text = _("Getting weather information...")
136                         url = ("http://www.google.com/ig/api?weather=%s&hl=%s" % (quote(self.weatherPluginEntry.city.value), self.weatherPluginEntry.language.value))
137                         getXML(url).addCallback(self.xmlCallback).addErrback(self.error)
138                 else:
139                         self["statustext"].text = _("No locations defined...\nPress 'Menu' to do that.")
140
141         def nextItem(self):
142                 if self.weatherPluginEntryCount != 0:
143                         if self.weatherPluginEntryIndex < self.weatherPluginEntryCount:
144                                 self.weatherPluginEntryIndex = self.weatherPluginEntryIndex + 1
145                         else:
146                                 self.weatherPluginEntryIndex = 1
147                         self.setItem()
148
149         def previousItem(self):
150                 if self.weatherPluginEntryCount != 0:
151                         if self.weatherPluginEntryIndex >= 2:
152                                 self.weatherPluginEntryIndex = self.weatherPluginEntryIndex - 1
153                         else:
154                                 self.weatherPluginEntryIndex = self.weatherPluginEntryCount
155                         self.setItem()
156
157         def setItem(self):
158                 self.weatherPluginEntry = config.plugins.WeatherPlugin.Entries[self.weatherPluginEntryIndex-1]
159                 self.clearFields()
160                 self.startRun()
161
162         def clearFields(self):
163                 self["caption"].text = ""
164                 self["currentTemp"].text = ""
165                 self["condition"].text = ""
166                 self["wind_condition"].text = ""
167                 self["humidity"].text = ""
168                 i = 1
169                 while i < 5:
170                         self["weekday%s" % i].text = ""
171                         self["weekday%s_icon" %i].hide()
172                         self["weekday%s_temp" % i].text = ""
173                         i += 1
174
175         def errorIconDownload(self, error = None, item = None):
176                 item.error = True
177
178         def finishedIconDownload(self, result, item):
179                 if not item.error:
180                         self.showIcon(item.index,item.filename)
181
182         def showIcon(self,index, filename):
183                 self["weekday%s_icon" % index].updateIcon(filename)
184                 self["weekday%s_icon" % index].show()
185
186         def xmlCallback(self, xmlstring):
187                 self["statustext"].text = ""
188                 metric = 0
189                 index = 0
190                 UnitSystemText = "F"
191                 IconDownloadList = []
192                 root = cet_fromstring(xmlstring)
193                 for childs in root.findall("weather"):
194                         for items in childs:
195                                 if items.tag == "problem_cause":
196                                         self["statustext"].text = items.attrib.get("data").encode("utf-8", 'ignore')
197                                 elif items.tag == "forecast_information":
198                                         for items2 in items:
199                                                 if items2.tag == "city":
200                                                         self["caption"].text = items2.attrib.get("data").encode("utf-8", 'ignore')
201                                                 elif items2.tag == "unit_system":
202                                                         if items2.attrib.get("data").encode("utf-8", 'ignore') == "SI":
203                                                                 metric = 1
204                                                                 UnitSystemText = "C"
205                                 elif items.tag == "current_conditions":
206                                         for items2 in items:
207                                                 if items2.tag == "condition":
208                                                         self["condition"].text = _("Current: %s") % items2.attrib.get("data").encode("utf-8", 'ignore')
209                                                 elif items2.tag == "temp_f" and metric == 0:
210                                                         self["currentTemp"].text =  ("%s F" % items2.attrib.get("data").encode("utf-8", 'ignore')) 
211                                                 elif items2.tag == "temp_c" and metric == 1:
212                                                         self["currentTemp"].text =  ("%s C" % items2.attrib.get("data").encode("utf-8", 'ignore')) 
213                                                 elif items2.tag == "humidity":
214                                                         self["humidity"].text = items2.attrib.get("data").encode("utf-8", 'ignore')
215                                                 elif items2.tag == "wind_condition":
216                                                         self["wind_condition"].text = items2.attrib.get("data").encode("utf-8", 'ignore')
217                                 elif items.tag == "forecast_conditions":
218                                         index = index + 1
219                                         lowTemp = ""
220                                         highTemp = ""
221                                         icon = ""
222                                         for items2 in items:
223                                                 if items2.tag == "day_of_week":
224                                                         self["weekday%s" % index].text = items2.attrib.get("data").encode("utf-8", 'ignore')
225                                                 elif items2.tag == "low":
226                                                         lowTemp = items2.attrib.get("data").encode("utf-8", 'ignore')
227                                                 elif items2.tag == "high":
228                                                         highTemp = items2.attrib.get("data").encode("utf-8", 'ignore')
229                                                         self["weekday%s_temp" % index].text = "%s %s | %s %s" % (highTemp, UnitSystemText, lowTemp, UnitSystemText)
230                                                 elif items2.tag == "icon":
231                                                         url = items2.attrib.get("data").encode("utf-8", 'ignore')
232                                                         parts = url.split("/")
233                                                         filename = self.appdir + parts[-1]
234                                                         if not os_path.exists(filename):
235                                                                 IconDownloadList.append(WeatherIconItem(url = url,filename = filename, index = index))
236                                                         else:
237                                                                 self.showIcon(index,filename)
238                 if len(IconDownloadList) != 0:
239                         ds = defer.DeferredSemaphore(tokens=len(IconDownloadList))
240                         downloads = [ds.run(download,item ).addErrback(self.errorIconDownload, item).addCallback(self.finishedIconDownload,item) for item in IconDownloadList]
241                         finished = defer.DeferredList(downloads).addErrback(self.error)
242
243         def config(self):
244                 self.session.openWithCallback(self.setupFinished, WeatherPluginEntriesListConfigScreen)
245
246         def setupFinished(self, index, entry = None):
247                 self.weatherPluginEntryCount = config.plugins.WeatherPlugin.entriescount.value
248                 if self.weatherPluginEntryCount >= 1:
249                         if entry is not None:
250                                 self.weatherPluginEntry = entry
251                                 self.weatherPluginEntryIndex = index + 1
252                         if self.weatherPluginEntry is None:
253                                 self.weatherPluginEntry = config.plugins.WeatherPlugin.Entries[0]
254                                 self.weatherPluginEntryIndex = 1
255                 else:
256                         self.weatherPluginEntry = None
257                         self.weatherPluginEntryIndex = -1
258
259                 self.clearFields()
260                 self.startRun()
261
262         def error(self, error = None):
263                 if error is not None:
264                         self.clearFields()
265                         self["statustext"].text = str(error.getErrorMessage())
266
267
268 class WeatherIcon(Pixmap):
269         def __init__(self):
270                 Pixmap.__init__(self)
271                 self.IconFileName = ""
272                 self.picload = ePicLoad()
273                 self.picload.PictureData.get().append(self.paintIconPixmapCB)
274
275         def onShow(self):
276                 Pixmap.onShow(self)
277                 sc = AVSwitch().getFramebufferScale()
278                 self.picload.setPara((self.instance.size().width(), self.instance.size().height(), sc[0], sc[1], 0, 0, '#00000000'))
279
280         def paintIconPixmapCB(self, picInfo=None):
281                 ptr = self.picload.getData()
282                 if ptr != None:
283                         self.instance.setPixmap(ptr.__deref__())
284
285         def updateIcon(self, filename):
286                 new_IconFileName = filename
287                 if (self.IconFileName != new_IconFileName):
288                         self.IconFileName = new_IconFileName
289                         self.picload.startDecode(self.IconFileName)
290