예제 #1
0
def test_invalid_data():
    """Test loading and parsing xml file with data that cannot be parsed."""
    file = open('tests/xml/buienradar_invalid.xml', 'r')
    data = file.read()
    file.close()

    latitude = 51.50
    longitude = 6.20
    result = parse_data(data, None, latitude, longitude, usexml=True)
    print(result)
    assert(result[SUCCESS] is False)
    assert(result[MESSAGE] == 'Location data is invalid.')

    file = open('tests/xml/buienradar_invalidfc1.xml', 'r')
    data = file.read()
    file.close()

    latitude = 51.98
    longitude = 4.10
    result = parse_data(data, None, latitude, longitude, usexml=True)
    print(result)
    assert(result[SUCCESS] and                                  # noqa: ignore=W504
           result[MESSAGE] is None)
    # test missing maxtemp:
    assert(len(result[DATA][FORECAST]) == 5 and                 # noqa: ignore=W504
           result[DATA][FORECAST][0][TEMPERATURE] == 0.0)
    # test missing maxgtemp and maxtempmax:
    assert(len(result[DATA][FORECAST]) == 5 and                 # noqa: ignore=W504
           result[DATA][FORECAST][2][TEMPERATURE] == 0.0)

    # read xml with invalid ws coordinates
    file = open('tests/xml/buienradar_invalidws1.xml', 'r')
    data = file.read()
    file.close()

    # 'Meetstation Arcen' contains invalid gps info,
    # 'Meetstation Volkel' will be selected as alternative
    latitude = 51.50
    longitude = 6.20
    result = parse_data(data, None, latitude, longitude, usexml=True)
    print(result)
    assert(result[SUCCESS] and                                  # noqa: ignore=W504
           '(6375)' in result[DATA][STATIONNAME])

    # 'Meetstation Arnhem' contains invalid gps info,
    # 'Meetstation De Bilt' will be selected as alternative
    latitude = 52.07
    longitude = 5.88
    result = parse_data(data, None, latitude, longitude, usexml=True)
    print(result)
    assert(result[SUCCESS] and '(6260)' in result[DATA][STATIONNAME])

    # 'Meetstation Berkhout' contains invalid gps info,
    # 'Meetstation Wijdenes' will be selected as alternative
    latitude = 52.65
    longitude = 4.98
    result = parse_data(data, None, latitude, longitude, usexml=True)
    print(result)
    assert(result[SUCCESS] and '(6248)' in result[DATA][STATIONNAME])
예제 #2
0
def test_nows():
    """Test loading and parsing invalid xml file; no weatherstation."""
    file = open('tests/buienradar_nows.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)
    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'No location selected.')

    file = open('tests/buienradar_nows2.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)
    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'No location selected.')

    file = open('tests/buienradar_nows3.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)
    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'No location selected.')

    file = open('tests/buienradar_nows4.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)
    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'No location selected.')

    file = open('tests/buienradar_nows5.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)
    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'No location selected.')
예제 #3
0
    async def async_update(self, *_):
        """Update the data from buienradar."""
        content = await self.get_data(JSON_FEED_URL)

        if content.get(SUCCESS) is not True:
            # unable to get the data
            self.load_error_count += 1
            threshold_log(
                self.load_error_count,
                "Unable to retrieve json data from Buienradar (Msg: %s, status: %s)",
                content.get(MESSAGE),
                content.get(STATUS_CODE),
            )
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return
        self.load_error_count = 0

        # rounding coordinates prevents unnecessary redirects/calls
        lat = self.coordinates[CONF_LATITUDE]
        lon = self.coordinates[CONF_LONGITUDE]
        rainurl = json_precipitation_forecast_url(lat, lon)
        raincontent = await self.get_data(rainurl)

        if raincontent.get(SUCCESS) is not True:
            self.rain_error_count += 1
            # unable to get the data
            threshold_log(
                self.rain_error_count,
                "Unable to retrieve rain data from Buienradar (Msg: %s, status: %s)",
                raincontent.get(MESSAGE),
                raincontent.get(STATUS_CODE),
            )
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return
        self.rain_error_count = 0

        result = parse_data(
            content.get(CONTENT),
            raincontent.get(CONTENT),
            self.coordinates[CONF_LATITUDE],
            self.coordinates[CONF_LONGITUDE],
            self.timeframe,
            False,
        )

        _LOGGER.debug("Buienradar parsed data: %s", result)
        if result.get(SUCCESS) is not True:
            if int(datetime.now().strftime("%H")) > 0:
                _LOGGER.warning(
                    "Unable to parse data from Buienradar. (Msg: %s)",
                    result.get(MESSAGE),
                )
            await self.schedule_update(SCHEDULE_NOK)
            return

        self.data = result.get(DATA)
        await self.update_devices()
        await self.schedule_update(SCHEDULE_OK)
예제 #4
0
    def async_update(self, *_):
        """Update the data from buienradar."""
        from buienradar.buienradar import (parse_data, CONTENT,
                                           DATA, MESSAGE, STATUS_CODE, SUCCESS)

        result = yield from self.get_data('http://xml.buienradar.nl')
        if result.get(SUCCESS, False) is False:
            result = yield from self.get_data('http://api.buienradar.nl')

        if result.get(SUCCESS):
            result = parse_data(result.get(CONTENT),
                                latitude=self.coordinates[CONF_LATITUDE],
                                longitude=self.coordinates[CONF_LONGITUDE])
            if result.get(SUCCESS):
                self.data = result.get(DATA)

                yield from self.update_devices()

                yield from self.schedule_update(10)
            else:
                yield from self.schedule_update(2)
        else:
            # unable to get the data
            _LOGGER.warning("Unable to retrieve data from Buienradar."
                            "(Msg: %s, status: %s,)",
                            result.get(MESSAGE),
                            result.get(STATUS_CODE),)
            # schedule new call
            yield from self.schedule_update(2)
예제 #5
0
    def async_update(self, *_):
        """Update the data from buienradar."""
        from buienradar.buienradar import (parse_data, CONTENT, DATA, MESSAGE,
                                           STATUS_CODE, SUCCESS)

        result = yield from self.get_data('http://xml.buienradar.nl')
        if result.get(SUCCESS, False) is False:
            result = yield from self.get_data('http://api.buienradar.nl')

        if result.get(SUCCESS):
            result = parse_data(result.get(CONTENT),
                                latitude=self.coordinates[CONF_LATITUDE],
                                longitude=self.coordinates[CONF_LONGITUDE])
            if result.get(SUCCESS):
                self.data = result.get(DATA)

                yield from self.update_devices()

                yield from self.schedule_update(10)
            else:
                yield from self.schedule_update(2)
        else:
            # unable to get the data
            _LOGGER.warning(
                "Unable to retrieve data from Buienradar."
                "(Msg: %s, status: %s,)",
                result.get(MESSAGE),
                result.get(STATUS_CODE),
            )
            # schedule new call
            yield from self.schedule_update(2)
예제 #6
0
    async def async_update(self, *_):
        """Update the data from buienradar."""
        from buienradar.buienradar import (parse_data, CONTENT, DATA, MESSAGE,
                                           STATUS_CODE, SUCCESS)

        content = await self.get_data('http://xml.buienradar.nl')
        if not content.get(SUCCESS, False):
            content = await self.get_data('http://api.buienradar.nl')

        if content.get(SUCCESS) is not True:
            # unable to get the data
            _LOGGER.warning(
                "Unable to retrieve xml data from Buienradar."
                "(Msg: %s, status: %s,)",
                content.get(MESSAGE),
                content.get(STATUS_CODE),
            )
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return

        # rounding coordinates prevents unnecessary redirects/calls
        rainurl = 'http://gadgets.buienradar.nl/data/raintext/?lat={}&lon={}'
        rainurl = rainurl.format(round(self.coordinates[CONF_LATITUDE], 2),
                                 round(self.coordinates[CONF_LONGITUDE], 2))
        raincontent = await self.get_data(rainurl)

        if raincontent.get(SUCCESS) is not True:
            # unable to get the data
            _LOGGER.warning(
                "Unable to retrieve raindata from Buienradar."
                "(Msg: %s, status: %s,)",
                raincontent.get(MESSAGE),
                raincontent.get(STATUS_CODE),
            )
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return

        result = parse_data(content.get(CONTENT), raincontent.get(CONTENT),
                            self.coordinates[CONF_LATITUDE],
                            self.coordinates[CONF_LONGITUDE], self.timeframe)

        _LOGGER.debug("Buienradar parsed data: %s", result)
        if result.get(SUCCESS) is not True:
            if int(datetime.now().strftime('%H')) > 0:
                _LOGGER.warning(
                    "Unable to parse data from Buienradar."
                    "(Msg: %s)",
                    result.get(MESSAGE),
                )
            await self.schedule_update(SCHEDULE_NOK)
            return

        self.data = result.get(DATA)
        await self.update_devices()
        await self.schedule_update(SCHEDULE_OK)
예제 #7
0
    async def async_update(self, *_):
        """Update the data from buienradar."""
        from buienradar.buienradar import (parse_data, CONTENT,
                                           DATA, MESSAGE, STATUS_CODE, SUCCESS)

        content = await self.get_data('http://xml.buienradar.nl')
        if not content.get(SUCCESS, False):
            content = await self.get_data('http://api.buienradar.nl')

        if content.get(SUCCESS) is not True:
            # unable to get the data
            _LOGGER.warning("Unable to retrieve xml data from Buienradar."
                            "(Msg: %s, status: %s,)",
                            content.get(MESSAGE),
                            content.get(STATUS_CODE),)
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return

        # rounding coordinates prevents unnecessary redirects/calls
        rainurl = 'http://gadgets.buienradar.nl/data/raintext/?lat={}&lon={}'
        rainurl = rainurl.format(
            round(self.coordinates[CONF_LATITUDE], 2),
            round(self.coordinates[CONF_LONGITUDE], 2)
            )
        raincontent = await self.get_data(rainurl)

        if raincontent.get(SUCCESS) is not True:
            # unable to get the data
            _LOGGER.warning("Unable to retrieve raindata from Buienradar."
                            "(Msg: %s, status: %s,)",
                            raincontent.get(MESSAGE),
                            raincontent.get(STATUS_CODE),)
            # schedule new call
            await self.schedule_update(SCHEDULE_NOK)
            return

        result = parse_data(content.get(CONTENT),
                            raincontent.get(CONTENT),
                            self.coordinates[CONF_LATITUDE],
                            self.coordinates[CONF_LONGITUDE],
                            self.timeframe)

        _LOGGER.debug("Buienradar parsed data: %s", result)
        if result.get(SUCCESS) is not True:
            if int(datetime.now().strftime('%H')) > 0:
                _LOGGER.warning("Unable to parse data from Buienradar."
                                "(Msg: %s)",
                                result.get(MESSAGE),)
            await self.schedule_update(SCHEDULE_NOK)
            return

        self.data = result.get(DATA)
        await self.update_devices()
        await self.schedule_update(SCHEDULE_OK)
예제 #8
0
def test_parse_timeframe():
    """Test loading and parsing xml file."""
    data = None
    raindata = None

    latitude = 51.50
    longitude = 6.20
    try:
        result = parse_data(data, raindata,
                            latitude, longitude, 4, usexml=True)
        # timeframe=4 should raise a ValueError, so:
        assert(False)
    except ValueError:
        # timeframe=4 should raise a ValueError, so:
        assert(True)

    try:
        result = parse_data(data, raindata,
                            latitude, longitude, 5, usexml=True)
        # timeframe=5 should NOT raise a ValueError, so:
        assert(True and result[SUCCESS] is False)
    except ValueError:
        # timeframe=5 should NOT raise a ValueError, so:
        assert(False)

    try:
        result = parse_data(data, raindata,
                            latitude, longitude, 121, usexml=True)
        # timeframe=121 should raise a ValueError, so:
        assert(False)
    except ValueError:
        # timeframe=121 should raise a ValueError, so:
        assert(True)

    try:
        result = parse_data(data, raindata,
                            latitude, longitude, 120, usexml=True)
        # timeframe=120 should NOT raise a ValueError, so:
        assert(True and result[SUCCESS] is False)
    except ValueError:
        # timeframe=120 should NOT raise a ValueError, so:
        assert(False)
예제 #9
0
def test_nofc2():
    """Test loading and parsing invalid xml file; no forecast."""
    file = open('tests/buienradar_nofc2.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)

    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] and
            len(result[DATA][FORECAST]) == 0)
예제 #10
0
def test_nofc():
    """Test loading and parsing invalid xml file: no forecast data."""
    file = open('tests/buienradar_nofc.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)

    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == 'Unable to extract forecast data.')
예제 #11
0
def test_noroot():
    """Test loading and parsing invalid xml file."""
    # load noxml_file
    file = open('tests/buienradar_noroot.xml', 'r')
    data = file.read()
    file.close()

    result = parse_data(data, None)

    # test calling results in the loop close cleanly
    print(result)
    assert (result[SUCCESS] is False and
            result[MESSAGE] == 'Unable to parse content as xml.')
예제 #12
0
    def get_data(self, lat: Optional[float] = None, long: Optional[float] = None, time_frame: Optional[int] = None) \
            -> Dict[str, Any]:
        # noinspection PyPackageRequirements
        from buienradar.buienradar import get_data, parse_data
        # noinspection PyPackageRequirements
        from buienradar.constants import SUCCESS, CONTENT, RAINCONTENT, DATA

        lat = lat or self.lat
        long = long or self.long
        time_frame = time_frame or self.time_frame

        result = get_data(latitude=lat, longitude=long)
        if not result.get(SUCCESS):
            raise RuntimeError('Error while retrieving data')

        data = result.get(CONTENT)
        rain_data = result.get(RAINCONTENT)
        result = parse_data(data, rain_data, lat, long, time_frame)
        return result.get(DATA, {})
예제 #13
0
    def async_update(self, *_):
        """Update the data from buienradar."""
        from buienradar.buienradar import (parse_data, CONTENT, DATA, MESSAGE,
                                           STATUS_CODE, SUCCESS)

        content = yield from self.get_data('http://xml.buienradar.nl')
        if not content.get(SUCCESS, False):
            content = yield from self.get_data('http://api.buienradar.nl')

        # rounding coordinates prevents unnecessary redirects/calls
        rainurl = 'http://gadgets.buienradar.nl/data/raintext/?lat={}&lon={}'
        rainurl = rainurl.format(round(self.coordinates[CONF_LATITUDE], 2),
                                 round(self.coordinates[CONF_LONGITUDE], 2))
        raincontent = yield from self.get_data(rainurl)

        if content.get(SUCCESS) and raincontent.get(SUCCESS):
            result = parse_data(content.get(CONTENT), raincontent.get(CONTENT),
                                self.coordinates[CONF_LATITUDE],
                                self.coordinates[CONF_LONGITUDE],
                                self.timeframe)
            if result.get(SUCCESS):
                self.data = result.get(DATA)

                yield from self.update_devices()

                yield from self.schedule_update(10)
            else:
                yield from self.schedule_update(2)
        else:
            # unable to get the data
            _LOGGER.warning(
                "Unable to retrieve data from Buienradar."
                "(Msg: %s, status: %s,)",
                result.get(MESSAGE),
                result.get(STATUS_CODE),
            )
            # schedule new call
            yield from self.schedule_update(2)
예제 #14
0
def forecast():
    timeframe = 60
    raindata = 0
    # gps-coordinates for the weather data
    latitude = float(request.args.get('latitude'))
    longitude = float(request.args.get('longitude'))

    result = get_data(
        latitude=latitude,
        longitude=longitude,
    )

    if result.get(SUCCESS):
        data = result[CONTENT]
        raindata = result[RAINCONTENT]

        result = parse_data(data, raindata, latitude, longitude, timeframe)
    response = jsonify({
        "forecast": result,
        "rainfall": parse_rainfall(raindata)
    })
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response
예제 #15
0
"""Example usage."""
from buienradar.buienradar import (CONTENT, RAINCONTENT, SUCCESS, get_data,
                                   parse_data)

# minutes to look ahead for precipitation forecast
# (5..120)
timeframe = 60

# gps-coordinates for the weather data
latitude = 51.50
longitude = 6.20

result = get_data(
    latitude=latitude,
    longitude=longitude,
)

if result.get(SUCCESS):
    data = result[CONTENT]
    raindata = result[RAINCONTENT]

    result = parse_data(data, raindata, latitude, longitude, timeframe)

print(result)
예제 #16
0
def test_missing_data():
    """Test loading and parsing invalid xml file; missing data fields."""
    file = open('tests/buienradar_missing.xml', 'r')
    data = file.read()
    file.close()

    latitude = 51.50
    longitude = 6.20
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: stationnaam ")

    latitude = 52.07
    longitude = 5.88
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: icoonactueel ")

    latitude = 52.65
    longitude = 4.98
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: luchtvochtigheid ")

    latitude = 52.10
    longitude = 5.18
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: temperatuurGC ")

    latitude = 52.92
    longitude = 4.78
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: temperatuur10cm ")

    latitude = 51.45
    longitude = 5.42
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: windsnelheidMS ")

    latitude = 51.20
    longitude = 5.77
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: windsnelheidBF ")

    latitude = 52.00
    longitude = 3.28
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: windrichtingGR ")

    latitude = 51.57
    longitude = 4.93
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: windrichting ")

    latitude = 52.07
    longitude = 6.65
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: luchtdruk ")

    latitude = 52.43
    longitude = 6.27
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: windstotenMS ")

    latitude = 51.87
    longitude = 5.15
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: regenMMPU ")

    latitude = 51.98
    longitude = 4.10
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert (result[SUCCESS] and
            result[MESSAGE] == "Missing key(s) in br data: zonintensiteitWM2 ")
예제 #17
0
def test_readdata3():
    """Test loading and parsing xml file."""
    # load buienradar.xml
    file = open('tests/buienradar.xml', 'r')
    data = file.read()
    file.close()

    # select last weather stationnaam
    # gps coordinates not exact, so non-zero distance
    # Meetstation Zeeplatform K13 (6252)
    latitude = 53.23
    longitude = 3.23
    result = parse_data(data, None, latitude, longitude)
    print(result)
    assert(result[SUCCESS] and
           result[MESSAGE] is None)

    # check the selected weatherstation:
    assert(result[SUCCESS] and
           '(6252)' in result[DATA][STATIONNAME])

    # check the data:
    fc1 = (datetime.now(pytz.timezone(__TIMEZONE)) + timedelta(days=1))
    fc2 = (datetime.now(pytz.timezone(__TIMEZONE)) + timedelta(days=2))
    fc3 = (datetime.now(pytz.timezone(__TIMEZONE)) + timedelta(days=3))
    fc4 = (datetime.now(pytz.timezone(__TIMEZONE)) + timedelta(days=4))
    fc5 = (datetime.now(pytz.timezone(__TIMEZONE)) + timedelta(days=5))

    fc1 = fc1.replace(hour=12, minute=0, second=0, microsecond=0)
    fc2 = fc2.replace(hour=12, minute=0, second=0, microsecond=0)
    fc3 = fc3.replace(hour=12, minute=0, second=0, microsecond=0)
    fc4 = fc4.replace(hour=12, minute=0, second=0, microsecond=0)
    fc5 = fc5.replace(hour=12, minute=0, second=0, microsecond=0)

    # '05/19/2017 00:20:00'
    loc_dt = datetime(2017, 5, 19, hour=0, minute=20, second=0, microsecond=0)
    measured = pytz.timezone(__TIMEZONE).localize(loc_dt)

    # Expected result:
    expect = {
        'msg': None,
        'success': True,
        'distance': 1.297928,
        'data': {
            'attribution': 'Data provided by buienradar.nl',
            'windspeed': 8.16,
            'windazimuth': 59,
            'groundtemperature': 0.0,
            'windforce': 5,
            'precipitation': 0.0,
            'precipitation_forecast': None,
            'humidity': 47,
            'pressure': 1004.95,
            'condition': {'condcode': 'c', 'condition': 'cloudy',
                          'detailed': 'cloudy',
                          'exact': 'Heavily clouded',
                          'exact_nl': 'Zwaar bewolkt',
                          'image': get_imageurl('cc')},
            'measured': measured,
            'winddirection': 'O',
            'stationname': 'Zeeplatform K13 (6252)',
            'temperature': 16.8,
            'visibility': 6200,
            'irradiance': 614,
            'windgust': 14.0,
            'forecast': [
                    {'datetime': fc1, 'temperature': 16.0, 'maxtemp': 16.0,
                     'mintemp': 8.0, 'rainchance': 15, 'sunchance': 0,
                     'rain': 0.0, 'snow': 0.0, 'windforce': 3,
                     'condition': {'condcode': 'j', 'condition': 'cloudy',
                                   'detailed': 'partlycloudy',
                                   'exact': 'Mix of clear and high clouds',
                                   'exact_nl': ('Mix van opklaringen en hoge '
                                                'bewolking'),
                                   'image': get_imageurl('j')}},
                    {'datetime': fc2, 'temperature': 17.0, 'maxtemp': 17.0,
                     'mintemp': 8.0, 'rainchance': 1, 'sunchance': 43,
                     'rain': 0.0, 'snow': 0.0, 'windforce': 3,
                     'condition': {'condcode': 'b', 'condition': 'cloudy',
                                   'detailed': 'partlycloudy',
                                   'exact': ('Mix of clear and medium or low '
                                             'clouds'),
                                   'exact_nl': ('Mix van opklaringen en '
                                                'middelbare of lage '
                                                'bewolking'),
                                   'image': get_imageurl('b')}},
                    {'datetime': fc3, 'temperature': 22.0, 'maxtemp': 22.0,
                     'mintemp': 10.0, 'rainchance': 3, 'sunchance': 0,
                     'rain': 0.0, 'snow': 0.0, 'windforce': 4,
                     'condition': {'condcode': 'r', 'condition': 'cloudy',
                                   'detailed': 'partlycloudy',
                                   'exact': '?? Partly cloudy ??',
                                   'exact_nl': '?? Partly cloudy ??',
                                   'image': get_imageurl('r')}},
                    {'datetime': fc4, 'temperature': 18.0, 'maxtemp': 18.0,
                     'mintemp': 11.0, 'rainchance': 43, 'sunchance': 0,
                     'rain': 1.8, 'snow': 0.0, 'windforce': 4,
                     'condition': {'condcode': 'm', 'condition': 'rainy',
                                   'detailed': 'light-rain',
                                   'exact': ('Heavily clouded with some '
                                             'light rain'),
                                   'exact_nl': ('Zwaar bewolkt met wat lichte'
                                                ' regen'),
                                   'image': get_imageurl('m')}},
                    {'datetime': fc5, 'temperature': 15.0, 'maxtemp': 15.0,
                     'mintemp': 9.0, 'rainchance': 76, 'sunchance': 0,
                     'rain': 4.4, 'snow': 0.0, 'windforce': 4,
                     'condition': {'condcode': 'f', 'condition': 'rainy',
                                   'detailed': 'partlycloudy-light-rain',
                                   'exact': ('Alternatingly cloudy with some '
                                             'light rain'),
                                   'exact_nl': ('Afwisselend bewolkt met '
                                                '(mogelijk) wat lichte '
                                                'regen'),
                                   'image': get_imageurl('f')}}
                ]
            },
        }
    assert(expect == result)