diff --git a/modules/meteofrance/browser.py b/modules/meteofrance/browser.py
index 0e0c2b99b05795bae02bea963784162c75201e0a..b3d8d2712089bf8cd463524a5849524130749d63 100644
--- a/modules/meteofrance/browser.py
+++ b/modules/meteofrance/browser.py
@@ -18,21 +18,46 @@
# along with this weboob module. If not, see .
from weboob.browser import PagesBrowser, URL
-from .pages import WeatherPage, SearchCitiesPage
+from .pages import WeatherPage, SearchCitiesPage, HomePage
__all__ = ['MeteofranceBrowser']
class MeteofranceBrowser(PagesBrowser):
- BASEURL = 'http://www.meteofrance.com'
- cities = URL('mf3-rpc-portlet/rest/lieu/facet/previsions/search/(?P.*)', SearchCitiesPage)
- weather = URL('previsions-meteo-france/(?P.*)/(?P.*)', WeatherPage)
+ BASEURL = 'https://meteofrance.com'
+
+ cities = URL(r'/search/all\?term=(?P.*)',
+ SearchCitiesPage)
+ weather = URL(r'https://rpcache-aa.meteofrance.com/internet2018client/2.0/forecast\?lat=(?P.*)&lon=(?P.*)&id=&instants=&day=2',
+ WeatherPage)
+ home = URL('', HomePage)
+
+ def _fill_header(self):
+ self.home.go()
+ mfessions = self.session.cookies.get('mfsession')
+ token = ''
+ for c in mfessions:
+ if c.isalpha():
+ t = 97 if c.islower() else 65
+ token += chr(t + (ord(c) - t + 13) % 26)
+ else:
+ token += c
+
+ self.session.headers['Authorization'] = 'Bearer %s' % token
+ self.session.headers['Sec-Fetch-Site'] = 'same-site'
+ self.session.headers['Sec-Fetch-Mode'] = 'cors'
def iter_city_search(self, pattern):
return self.cities.go(pattern=pattern).iter_cities()
def iter_forecast(self, city):
- return self.weather.go(city_id=city.id, city_name=city.name).iter_forecast()
+ if not self.session.headers.get('Authorization', None):
+ self._fill_header()
+
+ return self.weather.go(lng=city._lng, lat=city._lat).iter_forecast()
def get_current(self, city):
- return self.weather.go(city_id=city.id, city_name=city.name).get_current()
+ if not self.session.headers.get('Authorization', None):
+ self._fill_header()
+
+ return self.weather.go(lng=city._lng, lat=city._lat).get_current()
diff --git a/modules/meteofrance/pages.py b/modules/meteofrance/pages.py
index 2dac0730d4ab19ec9b87a366e3ddcd96fa53b5a4..a51b7957fd1679a8b61affe2103732e60650533b 100644
--- a/modules/meteofrance/pages.py
+++ b/modules/meteofrance/pages.py
@@ -18,20 +18,19 @@
# You should have received a copy of the GNU Affero General Public License
# along with this weboob module. If not, see .
-from datetime import date
+from datetime import date, datetime
from weboob.browser.pages import JsonPage, HTMLPage
-from weboob.browser.elements import ItemElement, ListElement, DictElement, method
-from weboob.capabilities.base import NotAvailable
-from weboob.capabilities.weather import Forecast, Current, City, Temperature
+from weboob.browser.elements import ItemElement, DictElement, method
+from weboob.capabilities.weather import Forecast, Current, City, Temperature, Precipitation, Direction
from weboob.browser.filters.json import Dict
-from weboob.browser.filters.standard import CleanText, CleanDecimal, Regexp, Format, Eval
+from weboob.browser.filters.standard import CleanText, Format, Field
class SearchCitiesPage(JsonPage):
@method
class iter_cities(DictElement):
- ignore_duplicate = True
+ # ignore_duplicate = True
class item(ItemElement):
klass = City
@@ -39,68 +38,89 @@ class item(ItemElement):
def condition(self):
return Dict('type')(self) == "VILLE_FRANCE"
- obj_id = Dict('codePostal')
- obj_name = Dict('slug')
+ obj_id = Dict('cp')
+ obj_name = Dict('name')
+ obj__lng = Dict('lng')
+ obj__lat = Dict('lat')
-class WeatherPage(HTMLPage):
+class HomePage(HTMLPage):
+ pass
+
+
+class WeatherPage(JsonPage):
@method
- class iter_forecast(ListElement):
- item_xpath = '//div[@class="liste-jours"]/ul/li'
+ class get_current(ItemElement):
+ klass = Current
- class item(ItemElement):
- klass = Forecast
+ def parse(self, el):
+ now = datetime.now()
+ self.cpt = 0
+ for item in Dict('properties/forecast')(el):
+ if datetime.strptime(item['time'], '%Y-%m-%dT%H:%M:%S.%fZ') < now:
+ self.cpt = self.cpt + 1
+ else:
+ break
+
+ obj_date = date.today()
- obj_id = CleanText('./dl/dt')
-
- def obj_date(self):
- actual_day_number = Eval(int,
- Regexp(CleanText('./dl/dt'),
- '\w{3} (\d+)'))(self)
- base_date = date.today()
- if base_date.day > actual_day_number:
- base_date = base_date.replace(
- month=(
- (base_date.month % 12) + 1
- )
- )
- base_date = base_date.replace(day=actual_day_number)
- return base_date
+ def obj_id(self):
+ return Dict('properties/forecast/{}/time'.format(self.cpt))(self)
- def obj_low(self):
- temp = Regexp(CleanText('./dl/dd/span[@class="min-temp"]'),
- u'(\d*)\xb0\w Minimale.*')
- if temp != "-": # Sometimes website does not return low
- temp = CleanDecimal(temp)(self)
- unit = Regexp(CleanText('./dl/dd/span[@class="min-temp"]'), u'.*\xb0(\w) Minimale.*')(self)
- return Temperature(float(temp), unit)
- return NotAvailable
+ def obj_text(self):
+ return Format(u'%s - %s probability %s%% - Cloud coverage %s%% - Wind %s km/h %s° %s - Humidity %s%% - Pressure %s hPa',
+ Dict('properties/forecast/{}/weather_description'.format(self.cpt)),
+ Field('precipitation'),
+ Field('precipitation_probability'),
+ Field('wind_speed'),
+ Dict('properties/forecast/{}/total_cloud_cover'.format(self.cpt)),
+ Dict('properties/forecast/{}/wind_direction'.format(self.cpt)),
+ Field('wind_direction'),
+ Field('humidity'),
+ Field('pressure'))(self)
- def obj_high(self):
- temp = Regexp(CleanText('./dl/dd/span[@class="max-temp"]'),
- u'(.*)\xb0\w Maximale.*')(self)
- if temp != "-": # Sometimes website does not return high
- temp = CleanDecimal(temp)(self)
- unit = Regexp(CleanText('./dl/dd/span[@class="max-temp"]'), u'.*\xb0(\w) Maximale.*')(self)
- return Temperature(float(temp), unit)
- return NotAvailable
+ def obj_precipitation_probability(self):
+ return float(Dict('properties/forecast/{}/rain_1h'.format(self.cpt), default=0)(self))
- obj_text = CleanText('./@title')
+ def obj_precipitation(self):
+ return Precipitation.RA
- @method
- class get_current(ItemElement):
- klass = Current
+ def obj_wind_direction(self):
+ return Direction[CleanText(Dict('properties/forecast/{}/wind_icon'.format(self.cpt)),
+ replace=[('O', 'W')])(self)]
- obj_id = date.today()
- obj_date = date.today()
- obj_text = Format('%s - %s - %s - Vent %s',
- CleanText('//ul[@class="prevision-horaire "]/li[@class=" active "]/div/ul/li[@class="day-summary-tress-start"]'),
- CleanText('//ul[@class="prevision-horaire "]/li[@class=" active "]/div/ul/li[@class="day-summary-image"]'),
- CleanText('//ul[@class="prevision-horaire "]/li[@class=" active "]/div/ul/li[@class="day-summary-uv"]'),
- CleanText('//ul[@class="prevision-horaire "]/li[@class=" active "]/div/ul/li[@class="day-summary-wind"]'))
+ def obj_wind_speed(self):
+ return float(Dict('properties/forecast/{}/wind_speed'.format(self.cpt), default=0)(self))
+
+ def obj_humidity(self):
+ return float(Dict('properties/forecast/{}/relative_humidity'.format(self.cpt), default=0)(self))
+
+ def obj_pressure(self):
+ return float(Dict('properties/forecast/{}/P_sea'.format(self.cpt), default=0)(self))
def obj_temp(self):
- temp = CleanDecimal('//ul[@class="prevision-horaire "]/li[@class=" active "]/ul/li[@class="day-summary-temperature"]')(self)
- unit = Regexp(CleanText('//ul[@class="prevision-horaire "]/li[@class=" active "]/ul/li[@class="day-summary-temperature"]'),
- u'.*\xb0(\w)')(self)
- return Temperature(float(temp), unit)
+ return Temperature(float(Dict('properties/forecast/{}/T'.format(self.cpt), default=50)(self)), 'C')
+
+ @method
+ class iter_forecast(DictElement):
+ item_xpath = 'properties/daily_forecast'
+
+ class item(ItemElement):
+ klass = Forecast
+
+ obj_id = Dict('time')
+
+ obj_date = Field('id')
+
+ def obj_low(self):
+ return Temperature(float(Dict('T_min', default=-50)(self)), 'C')
+
+ def obj_high(self):
+ return Temperature(float(Dict('T_max', default=50)(self)), 'C')
+
+ def obj_text(self):
+ return Format(u'%s - humidity %s%% / %s%% - UV index %s',
+ Dict('daily_weather_description'),
+ Dict('relative_humidity_min'),
+ Dict('relative_humidity_max'),
+ Dict('uv_index'))(self)
diff --git a/modules/meteofrance/test.py b/modules/meteofrance/test.py
index 1c83423b42824dd74171987b87fa43260c41c1a2..7a6dd7bfeee5364988983acaebdda601c7b80e20 100644
--- a/modules/meteofrance/test.py
+++ b/modules/meteofrance/test.py
@@ -25,13 +25,16 @@ class MeteoFranceTest(BackendTest):
MODULE = 'meteofrance'
def test_meteofrance(self):
- l = list(self.backend.iter_city_search('paris'))
- self.assertTrue(len(l) > 0)
+ cities = list(self.backend.iter_city_search('paris'))
+ self.assertTrue(len(cities) > 0)
- city = l[0]
+ city = cities[0]
current = self.backend.get_current(city.id)
self.assertTrue(current.temp.value > -20 and current.temp.value < 50)
+ current2 = self.backend.get_current('béthune')
+ self.assertTrue(current2.temp.value > -20 and current2.temp.value < 50)
+
forecasts = list(self.backend.iter_forecast(city.id))
self.assertTrue(len(forecasts) > 0)