[WeatherPlugin] Because msn weather-api is dead, I switched from msn to yahoo. Saved...
[enigma2-plugins.git] / weatherplugin / src / setup.py
1 # -*- coding: utf-8 -*-
2 #
3 # WeatherPlugin E2
4 #
5 # Coded by Dr.Best (c) 2012
6 # Support: www.dreambox-tools.info
7 # E-Mail: dr.best@dreambox-tools.info
8 #
9 # This plugin is open source but it is NOT free software.
10 #
11 # This plugin may only be distributed to and executed on hardware which
12 # is licensed by Dream Multimedia GmbH.
13 # In other words:
14 # It's NOT allowed to distribute any parts of this plugin or its source code in ANY way
15 # to hardware which is NOT licensed by Dream Multimedia GmbH.
16 # It's NOT allowed to execute this plugin and its source code or even parts of it in ANY way
17 # on hardware which is NOT licensed by Dream Multimedia GmbH.
18 #
19 # If you want to use or modify the code or parts of it,
20 # you have to keep MY license and inform me about the modifications by mail.
21 #
22 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, \
23         RT_VALIGN_CENTER
24 from Screens.Screen import Screen
25 from Screens.MessageBox import MessageBox
26 from Components.MenuList import MenuList
27 from Components.Sources.StaticText import StaticText
28 from Components.ActionMap import ActionMap
29 from Components.ConfigList import ConfigList, ConfigListScreen
30 from Components.config import ConfigSubsection, ConfigText, ConfigSelection, \
31         getConfigListEntry, config, configfile
32 from twisted.web.client import getPage
33 from urllib import quote as urllib_quote
34 from skin import TemplatedListFonts, componentSizes
35 import json
36
37 def initWeatherPluginEntryConfig():
38         s = ConfigSubsection()
39         s.city = ConfigText(default = "Heidelberg", visible_width = 100, fixed_size = False)
40         s.degreetype = ConfigSelection(choices = [("C", _("metric system")), ("F", _("imperial system"))], default = "C")
41         s.weatherlocationcode = ConfigText(default = "", visible_width = 100, fixed_size = False)
42         config.plugins.WeatherPlugin.Entry.append(s)
43         return s
44
45 def initConfig():
46         count = config.plugins.WeatherPlugin.entrycount.value
47         if count != 0:
48                 i = 0
49                 while i < count:
50                         initWeatherPluginEntryConfig()
51                         i += 1
52
53 class MSNWeatherPluginEntriesListConfigScreen(Screen):
54         skin = """
55                 <screen name="MSNWeatherPluginEntriesListConfigScreen" position="center,120" size="820,520">
56                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on" />
57                 <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40" alphatest="on" />
58                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="410,5" size="200,40" alphatest="on" />
59                 <ePixmap pixmap="skin_default/buttons/blue.png" position="610,5" size="200,40" alphatest="on" />
60                 <widget source="key_red" render="Label" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
61                 <widget source="key_green" render="Label" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
62                 <widget source="key_yellow" render="Label" position="410,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
63                 <widget source="key_blue" render="Label" position="610,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
64                 <eLabel position="10,50" size="800,1" backgroundColor="grey" />
65                 <widget render="Label" source="city" position="15,60" size="500,50" font="Regular;20" halign="left"/>
66                         <widget render="Label" source="degreetype" position="520,60" size="130,50" font="Regular;20" halign="left"/>
67                 <widget name="entrylist" position="10,90" size="800,400" enableWrapAround="1" scrollbarMode="showOnDemand" />
68                 </screen>"""
69
70         def __init__(self, session):
71                 Screen.__init__(self, session)
72                 self.title = _("WeatherPlugin: List of Entries")
73                 self["city"] = StaticText(_("City"))
74                 self["degreetype"] = StaticText(_("System"))
75                 self["key_red"] = StaticText(_("Back"))
76                 self["key_green"] = StaticText(_("Add"))                
77                 self["key_yellow"] = StaticText(_("Edit"))
78                 self["key_blue"] = StaticText(_("Delete"))
79                 self["entrylist"] = WeatherPluginEntryList([])
80                 self["actions"] = ActionMap(["WizardActions","MenuActions","ShortcutActions"],
81                         {
82                          "ok"   :       self.keyOK,
83                          "back" :       self.keyClose,
84                          "red"  :       self.keyClose,
85                          "green":       self.keyGreen,                   
86                          "yellow":      self.keyYellow,
87                          "blue":        self.keyDelete,
88                          }, -1)
89                 self.updateList()
90
91         def updateList(self):
92                 self["entrylist"].buildList()
93
94         def keyClose(self):
95                 self.close(-1, None)
96
97         def keyGreen(self):
98                 self.session.openWithCallback(self.updateList,MSNWeatherPluginEntryConfigScreen,None)
99
100         def keyOK(self):
101                 try:sel = self["entrylist"].l.getCurrentSelection()[0]
102                 except: sel = None
103                 self.close(self["entrylist"].getCurrentIndex(), sel)
104
105         def keyYellow(self):
106                 try:sel = self["entrylist"].l.getCurrentSelection()[0]
107                 except: sel = None
108                 if sel is None:
109                         return
110                 self.session.openWithCallback(self.updateList,MSNWeatherPluginEntryConfigScreen,sel)
111
112         def keyDelete(self):
113                 try:sel = self["entrylist"].l.getCurrentSelection()[0]
114                 except: sel = None
115                 if sel is None:
116                         return
117                 self.session.openWithCallback(self.deleteConfirm, MessageBox, _("Really delete this WeatherPlugin Entry?"))
118
119         def deleteConfirm(self, result):
120                 if not result:
121                         return
122                 sel = self["entrylist"].l.getCurrentSelection()[0]
123                 config.plugins.WeatherPlugin.entrycount.value -= 1
124                 config.plugins.WeatherPlugin.entrycount.save()
125                 config.plugins.WeatherPlugin.Entry.remove(sel)
126                 config.plugins.WeatherPlugin.Entry.save()
127                 config.plugins.WeatherPlugin.save()
128                 configfile.save()
129                 self.updateList()
130
131 class WeatherPluginEntryList(MenuList):
132         SKIN_COMPONENT_KEY = "WeatherPluginList"
133         SKIN_COMPONENT_TEXT_HEIGHT = "textHeight"
134         SKIN_COMPONENT_TEXT_WIDTH = "textWidth"
135         SKIN_COMPONENT_TEXT2_WIDTH = "text2Width"
136         SKIN_COMPONENT_ITEM_MARGIN = "itemMargin"
137
138         def __init__(self, list, enableWrapAround = True):
139                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
140                 tlf = TemplatedListFonts()
141                 self.l.setFont(0, gFont(tlf.face(tlf.SMALL), tlf.size(tlf.SMALL)))
142
143         def postWidgetCreate(self, instance):
144                 MenuList.postWidgetCreate(self, instance)
145                 instance.setItemHeight(componentSizes.itemHeight(self.SKIN_COMPONENT_KEY, 30))
146
147         def getCurrentIndex(self):
148                 return self.instance.getCurrentIndex()
149
150         def buildList(self):
151                 sizes = componentSizes[WeatherPluginEntryList.SKIN_COMPONENT_KEY]
152                 textHeight = sizes.get(WeatherPluginEntryList.SKIN_COMPONENT_TEXT_HEIGHT, 30)
153                 textWidth = sizes.get(WeatherPluginEntryList.SKIN_COMPONENT_TEXT_WIDTH, 500)
154                 text2Width = sizes.get(WeatherPluginEntryList.SKIN_COMPONENT_TEXT2_WIDTH, 80)   
155                 itemMargin = sizes.get(WeatherPluginEntryList.SKIN_COMPONENT_ITEM_MARGIN, 10)   
156                 list = []
157                 for c in config.plugins.WeatherPlugin.Entry:
158                         res = [
159                                 c,
160                                 (eListboxPythonMultiContent.TYPE_TEXT, 5, 0, textWidth, textHeight, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, str(c.city.value)),
161                                 (eListboxPythonMultiContent.TYPE_TEXT, itemMargin+textWidth, 0, text2Width, textHeight, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, str(c.degreetype .value)),
162                         ]
163                         list.append(res)
164                 self.list = list
165                 self.l.setList(list)
166                 self.moveToIndex(0)
167
168 class MSNWeatherPluginEntryConfigScreen(ConfigListScreen, Screen):
169         skin = """
170                 <screen name="MSNWeatherPluginEntryConfigScreen" position="center,120" size="820,520">
171                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on" />
172                 <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40" alphatest="on" />
173                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="410,5" size="200,40" alphatest="on" />
174                 <ePixmap pixmap="skin_default/buttons/blue.png" position="610,5" size="200,40" alphatest="on" />
175                 <widget source="key_red" render="Label" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
176                 <widget source="key_green" render="Label" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
177                 <widget source="key_yellow" render="Label" position="410,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
178                 <widget source="key_blue" render="Label" position="610,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
179                 <eLabel position="10,50" size="800,1" backgroundColor="grey" />
180                 <widget name="config" position="10,60" size="800,450" enableWrapAround="1" scrollbarMode="showOnDemand" />
181                 </screen>"""
182
183         def __init__(self, session, entry):
184                 Screen.__init__(self, session)
185                 self.title = _("WeatherPlugin: Edit Entry")
186                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
187                 {
188                         "green": self.keySave,
189                         "red": self.keyCancel,
190                         "blue": self.keyDelete,
191                         "yellow": self.searchLocation,
192                         "cancel": self.keyCancel
193                 }, -2)
194
195                 self["key_red"] = StaticText(_("Cancel"))
196                 self["key_green"] = StaticText(_("OK"))
197                 self["key_blue"] = StaticText(_("Delete"))
198                 self["key_yellow"] = StaticText(_("Search Code"))
199
200                 if entry is None:
201                         self.newmode = 1
202                         self.current = initWeatherPluginEntryConfig()
203                 else:
204                         self.newmode = 0
205                         self.current = entry
206
207                 cfglist = [
208                         getConfigListEntry(_("City"), self.current.city),
209                         getConfigListEntry(_("Location code"), self.current.weatherlocationcode),
210                         getConfigListEntry(_("System"), self.current.degreetype)
211                 ]
212
213                 ConfigListScreen.__init__(self, cfglist, session)
214                 
215         def searchLocation(self):
216                 if self.current.city.value != "":
217                         url = "http://query.yahooapis.com/v1/public/yql?q=select+*+from+geo.places+where+text='%s'&format=json" % (urllib_quote(self.current.city.value))
218                         getPage(url).addCallback(self.jsonCallback).addErrback(self.error)
219                 else:
220                         self.session.open(MessageBox, _("You need to enter a valid city name before you can search for the location code."), MessageBox.TYPE_ERROR)
221
222         def keySave(self):
223                 if self.current.city.value != "" and self.current.weatherlocationcode.value != "":
224                         if self.newmode == 1:
225                                 config.plugins.WeatherPlugin.entrycount.value = config.plugins.WeatherPlugin.entrycount.value + 1
226                                 config.plugins.WeatherPlugin.entrycount.save()
227                         ConfigListScreen.keySave(self)
228                         config.plugins.WeatherPlugin.save()
229                         configfile.save()
230                         self.close()
231                 else:
232                         if self.current.city.value == "":
233                                 self.session.open(MessageBox, _("Please enter a valid city name."), MessageBox.TYPE_ERROR)
234                         else:
235                                 self.session.open(MessageBox, _("Please enter a valid location code for the city."), MessageBox.TYPE_ERROR)
236
237         def keyCancel(self):
238                 if self.newmode == 1:
239                         config.plugins.WeatherPlugin.Entry.remove(self.current)
240                 ConfigListScreen.cancelConfirm(self, True)
241
242         def keyDelete(self):
243                 if self.newmode == 1:
244                         self.keyCancel()
245                 else:           
246                         self.session.openWithCallback(self.deleteConfirm, MessageBox, _("Really delete this WeatherPlugin Entry?"))
247
248         def deleteConfirm(self, result):
249                 if not result:
250                         return
251                 config.plugins.WeatherPlugin.entrycount.value = config.plugins.WeatherPlugin.entrycount.value - 1
252                 config.plugins.WeatherPlugin.entrycount.save()
253                 config.plugins.WeatherPlugin.Entry.remove(self.current)
254                 config.plugins.WeatherPlugin.Entry.save()
255                 config.plugins.WeatherPlugin.save()
256                 configfile.save()
257                 self.close()
258                 
259                 
260         def jsonCallback(self, jsonstring):
261                 if jsonstring:
262                         response = json.loads(jsonstring)
263                         if 'error' in response:
264                                 data = response['error']
265                                 errormessage = data['description']
266                                 self.session.open(MessageBox, errormessage, MessageBox.TYPE_ERROR)                                      
267                         else:
268                                 self.session.openWithCallback(self.searchCallback, MSNWeatherPluginSearch, response)
269                         
270         def error(self, error = None):
271                 if error is not None:
272                         print error
273                 
274         def searchCallback(self, result):
275                 if result:
276                         self.current.weatherlocationcode.value = result[0]
277                         self.current.city.value = result[1]
278         
279                 
280                 
281 class MSNWeatherPluginSearch(Screen):
282         skin = """
283                 <screen name="MSNWeatherPluginSearch" position="center,120" size="820,520">
284                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on" />
285                 <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40" alphatest="on" />
286                 <widget source="key_red" render="Label" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
287                 <widget source="key_green" render="Label" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
288                 <eLabel position="10,50" size="800,1" backgroundColor="grey" />
289                 <widget name="entrylist" position="10,60" size="800,450" enableWrapAround="1" scrollbarMode="showOnDemand" />
290                 </screen>""" 
291
292         def __init__(self, session, response):
293                 Screen.__init__(self, session)
294                 self.title = _("Yahoo location search result")
295                 self["key_red"] = StaticText(_("Back"))
296                 self["key_green"] = StaticText(_("OK"))         
297                 self["entrylist"] = MSNWeatherPluginSearchResultList([])
298                 self["actions"] = ActionMap(["WizardActions","MenuActions","ShortcutActions"],
299                         {
300                          "ok"   :       self.keyOK,
301                          "green": self.keyOK,
302                          "back" :       self.keyClose,
303                          "red": self.keyClose,
304                          }, -1)
305                 self.updateList(response)
306
307         def updateList(self, response):
308                 self["entrylist"].buildList(response)
309
310         def keyClose(self):
311                 self.close(None)
312
313         def keyOK(self):
314                 pass
315                 try:sel = self["entrylist"].l.getCurrentSelection()[0]
316                 except: sel = None
317                 self.close(sel)
318                 
319
320 class MSNWeatherPluginSearchResultList(MenuList):
321         SKIN_COMPONENT_KEY = "WeatherPluginList"
322         SKIN_COMPONENT_TEXT_HEIGHT = "textHeight"
323         SKIN_COMPONENT_TEXT_WIDTH = "textWidth"
324         SKIN_COMPONENT_LINE_SPACING = "lineSpacing"
325
326         def __init__(self, list, enableWrapAround = True):
327                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
328                 tlf = TemplatedListFonts()
329                 self.l.setFont(0, gFont(tlf.face(tlf.SMALL), tlf.size(tlf.SMALL)))
330
331         def postWidgetCreate(self, instance):
332                 MenuList.postWidgetCreate(self, instance)
333                 instance.setItemHeight(componentSizes.itemHeight(self.SKIN_COMPONENT_KEY, 50))
334
335         def getCurrentIndex(self):
336                 return self.instance.getCurrentIndex()
337
338         def buildList(self, response):
339                 sizes = componentSizes[MSNWeatherPluginSearchResultList.SKIN_COMPONENT_KEY]
340                 textHeight = sizes.get(MSNWeatherPluginSearchResultList.SKIN_COMPONENT_TEXT_HEIGHT, 23)
341                 textWidth = sizes.get(MSNWeatherPluginSearchResultList.SKIN_COMPONENT_TEXT_WIDTH, 500)
342                 lineSpacing = sizes.get(MSNWeatherPluginSearchResultList.SKIN_COMPONENT_LINE_SPACING, 2)
343                 data = []
344                 count = response["query"]["count"]
345                 if count > 1:
346                         data = response["query"]["results"]["place"]
347                 elif count == 1:
348                         data.append(response["query"]["results"]["place"])
349                 searchlocation = ""
350                 searchresult = ""
351                 weatherlocationcode = ""
352                 list = []
353                 for item in data:
354                         searchresult = ""
355                         name = item["name"].encode("utf-8", 'ignore')
356                         if 'admin1' in item:
357                                 admin1 = item['admin1']
358                                 if admin1:
359                                         name = "%s, %s" % (name, admin1["code"].encode("utf-8", 'ignore'))
360                                         searchresult = admin1["content"].encode("utf-8", 'ignore')
361                         if 'admin2' in item:
362                                 admin2 = item['admin2']
363                                 if admin2:
364                                         searchresult = searchresult + " (" + admin2["content"].encode("utf-8", 'ignore') + ")"
365                         searchlocation = name
366                         weatherlocationcode = item["woeid"].encode("utf-8", 'ignore')
367                         res = [
368                                 (weatherlocationcode, searchlocation),
369                                 (eListboxPythonMultiContent.TYPE_TEXT, 5, 0, textWidth, textHeight, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, searchlocation),
370                                 (eListboxPythonMultiContent.TYPE_TEXT, 5, textHeight+lineSpacing, textWidth, textHeight, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, searchresult),
371                         ]
372                         list.append(res)
373                 self.list = list
374                 self.l.setList(list)
375                 self.moveToIndex(0)