Exemple #1
0
def extract_included_region(config: configparser.SectionProxy) -> tuple:
    """
    This functions extracts the region for which primer shall be generated
    from the config file.
    :param config: contains the values.
    :return: extracted values formatted as int
    """
    if config is not None:
        seq_begin = config.getint('SEQUENCE_INCLUDED_BEGIN', -1)
        seq_end = config.getint('SEQUENCE_INCLUDED_END', -1)
    else:
        seq_begin = 0
        seq_end = 0
    # is one of the values invalid?
    if seq_begin < 0 or seq_end < 0:
        logging.error(
            ('Found negative or invalid values for the region'
             'for which the primer will be generated or have been generated if '
             'custom primers are used via --keep-primer.'
             '(SEQUENCE_INCLUDED_BEGIN,SEQUENCE_INCLUDED_END)'
             ' = {}. If you want to generate primers for the '
             'whole sequence set both values to 0. Aborting').format(
                (config['SEQUENCE_INCLUDED_BEGIN'],
                 config['SEQUENCE_INCLUDED_END']))
        )
        sys.exit(1)
    return seq_begin, seq_end
Exemple #2
0
    def setup(self, config: SectionProxy) -> None:
        """Set up the counters for config."""
        client_limit = config.getint("limit_client_tests", fallback=0)
        if client_limit:
            client_period = config.getfloat("limit_client_period", fallback=1) * 3600
            self._setup("client_id", client_limit, client_period)

        origin_limit = config.getint("limit_origin_tests", fallback=0)
        if origin_limit:
            origin_period = config.getfloat("limit_origin_period", fallback=1) * 3600
            self._setup("origin", origin_limit, origin_period)

        slack_user_limit = config.getint("limit_slack_user_tests", fallback=0)
        if slack_user_limit:
            slack_user_period = (
                config.getfloat("limit_slack_user_period", fallback=1) * 3600
            )
            self._setup("slack_user", slack_user_limit, slack_user_period)

        slack_team_limit = config.getint("limit_slack_team_tests", fallback=0)
        if slack_team_limit:
            slack_team_period = (
                config.getfloat("limit_slack_team_period", fallback=1) * 3600
            )
            self._setup("slack_team", slack_team_limit, slack_team_period)

        self.running = True
Exemple #3
0
def main_loop(panel_size: tuple[int, int], fonts: Fonts, images: Icons, config: configparser.SectionProxy, epd_so: Optional[ctypes.CDLL]) -> None:
  logger = logging.getLogger(__name__)
  logger.info("main_loop() started")
  wakeup_time = datetime.datetime.now()
  if((wakeup_time.minute - 5) % config.getint('REFRESH_FULL_INTERVAL') == 0):
    refresh.refresh(panel_size, fonts, images, config, epd_so, True)
  elif(wakeup_time.minute % config.getint('REFRESH_PARTIAL_INTERVAL') == 0):
    refresh.refresh(panel_size, fonts, images, config, epd_so, False)
Exemple #4
0
 def create(cls, name: str, config: configparser.SectionProxy) -> "Mpd":
     try:
         host = config.get("host", fallback="localhost")
         port = config.getint("port", fallback=6600)
         timeout = config.getint("timeout", fallback=5)
         return cls(name, host, port, timeout)
     except ValueError as error:
         raise ConfigurationError(
             "Host port or timeout configuration wrong: {}".format(
                 error)) from error
Exemple #5
0
    def load(section: SectionProxy) -> CacheConfig:
        """Load overridden variables from a section within a config file."""
        config = CacheConfig()

        config.path = os.path.expanduser(section.get("path", fallback=config.path))

        config.max_entries = section.getint("max_entries", fallback=config.max_entries)
        config.max_size = section.getint("max_size", fallback=config.max_size)

        return config
Exemple #6
0
def show_temperatur_warning_icon(temperature: float, time: Datetime,
                                 config: SectionProxy) -> bool:
    if temperature >= config.getint('HIGH_TEMPERATURE_WARNING_THRESHOLD'):
        return True

    if temperature <= config.getint('LOW_TEMPERATURE_WARNING_THRESHOLD'):
        return True

    if temperature >= config.getint(
            'TROPICAL_NIGHT_TEMPERATURE_WARNING_THRESHOLD') and (
                time.hour > 21 or time.hour < 8):
        return True

    return False
Exemple #7
0
def build_feature_extractor(cfg_section: SectionProxy):
    name = cfg_section.get('Type').lower()

    if name == "dlib":
        return DlibFeatureExtractor(cfg_section.get('ShapePredictor'),
                                    cfg_section.get('Extractor'))
    elif name == "senet":
        return Senet50FeatureExtractor(cfg_section.get("Detections"),
                                       cfg_section.get('Snapshot'))
    elif name == "vgg":
        return VggFeatureExtractor(cfg_section.getint("MaxpoolIdx"),
                                   cfg_section.getint("NoActivation"))
    else:
        raise Exception("Wrong feature extractor type.")
def get_sensor_panel(sensor_mac: str, sensor_name: str, images: Icons, fonts: Fonts, config: SectionProxy, draw_title:bool = True) -> Image.Image:
  logger = logging.getLogger(__name__)
  logger.info('Generating sensor panel')

  x_size = 550
  y_size = 330

  image = Image.new('L', (x_size, y_size), 0xff) 
  draw = ImageDraw.Draw(image)
  
  if (draw_title):
    utils.draw_title(draw, fonts['font_sm'], 'SENSOR', sensor_name, fonts['font_xs'])

  try:
    if (not config.getboolean('USE_FAKE_SENSOR_DATA')):
      timeout = config.getint('SENSOR_POLL_TIMEOUT')
      logger.info(f'Fetching sensor data (timeout: {timeout})')
      ruuvi_reactive = RuuviTagReactive([sensor_mac])
      sensor_data = ruuvi_reactive\
        .get_subject()\
        .map(lambda x: {x[0]: x[1]})\
        .merge(rx.Observable.timer(timeout).map(lambda x: {}))\
        .to_blocking()\
        .first()
      ruuvi_reactive.stop() 
      logger.info('Received data: %s', repr(sensor_data))
    else:
      sensor_data = {sensor_mac: {"temperature": random.uniform(18, 30), "humidity": random.uniform(20, 80), "battery": random.uniform(2000, 3000), "rssi": random.uniform(-120, -10)}}
      logger.info('Using fake data: %s', repr(sensor_data))
  except Exception as e:
    logger.error('get_data_for_sensors() failed: %s', repr(e))
    sensor_data = {}

  if (sensor_mac in sensor_data):
    data_y_base = 100 if (draw_title) else 0 
    state_in = sensor_data[sensor_mac]
    utils.draw_quantity(draw, (x_size//2 + 160, data_y_base + 120), str(round(state_in['temperature'], 1)), '°C', fonts, 'font_lg', 'font_sm')
    utils.draw_quantity(draw, (x_size//2 + 160, data_y_base + 210), str(round(state_in['humidity'])), '%', fonts)

    # Battery level
    battery_icon = icons.get_scaled_image(get_battery_icon(state_in['battery'], images), 120)
    image.paste(battery_icon, (30, data_y_base - 30), battery_icon)
    utils.draw_quantity(draw, (130, data_y_base + 125), str(round(state_in['battery']/1000, 2)), 'V', fonts, 'font_xs', 'font_xxs')

    # RSSI - not yet part of ruuvitag-sensor
    # Adding is trivial by editing ruuvitag-sensor package's decoder.py
    # See: https://github.com/ttu/ruuvitag-sensor/issues/52
    if ('rssi' in state_in):
      utils.draw_quantity(draw, (130, data_y_base + 190), str(round(state_in['rssi'])), 'dBm', fonts, 'font_xs', 'font_xxs')

  else: 
    logger.info(f'Could not find mac {sensor_mac} in sensor data')
    no_wifi_image = icons.get_scaled_image(images['misc']['no_wifi'], 200)
    image.paste(no_wifi_image, (x_size//2 - no_wifi_image.width//2, y_size//2 - no_wifi_image.height//2), no_wifi_image)
  
  # Borders
  if (config.getboolean('DRAW_PANEL_BORDERS')):
    draw.polygon([(0, 0), (x_size-1, 0), (x_size-1, y_size-1), (0, y_size-1), (0, 0)])
  
  return image
Exemple #9
0
 def from_config_section(cls, section: SectionProxy) -> Config:
     """Creates a credentials tuple from
     the respective config section.
     """
     host = section['host']
     port = section.getint('port')
     return cls(host, port)
Exemple #10
0
 def read_general_settings(self,
                           general_section: configparser.SectionProxy):
     """
     read settings from given sections, if no value found, no value would change
     better be called after the reset so the variables won't be None
     :param general_section: the general section in config
     :return: None
     """
     self.type = general_section.get('type', self.type)
     self.size = general_section.getint('size', self.size)
     self.timeout = general_section.getint('timeout', self.timeout)
     self.paste_format = general_section.get('paste_format',
                                             self.paste_format).replace(
                                                 '<&br />', '\n')
     self.log_save_path = general_section.get('log_path',
                                              self.log_save_path)
Exemple #11
0
 def __get_option_with_default_value(section: configparser.SectionProxy,
                                     option,
                                     default_value,
                                     data_type: str = ""):
     if section is not None and option in section:
         try:
             if data_type == "bool":
                 temp = section.getboolean(option)
             elif data_type == "int":
                 temp = section.getint(option)
             elif data_type == "float":
                 temp = section.getfloat(option)
             else:
                 temp = section.get(option)
             if option is None:
                 log.warning(
                     f"Setting '{option}' is present but has no value. Please refer to 'config.default' for an example config."
                 )
                 return default_value
             else:
                 return temp
         except ValueError:
             return default_value
     log.error(
         f"Setting '{option}' is missing from config. Please refer to 'config.default' for an example config."
     )
     return default_value
Exemple #12
0
 def create(cls, name: str, config: configparser.SectionProxy) -> "PlayersOnline":
     try:
         treshold = config.getint("treshold", fallback=0)
     except ValueError as error:
         raise ConfigurationError("Treshold must be integer") from error
     
     return cls(name, treshold, **cls.collect_init_args(config))
Exemple #13
0
 def loadFont(self, section: SectionProxy, name: str, fontNameDef: str = "Helvetica", fontSizeDef: int = 32, isBoldDef: bool = False, isItalicDef: bool = False) -> Tuple[str, int, bool, bool]:
     if not isinstance(section, SectionProxy):
         raise TypeError("section")
     fontName = section.get(name + "Name", fontNameDef)
     fontSize = section.getint(name + "Size", fontSizeDef)
     isBold = section.getboolean(name + "Bold", isBoldDef)
     isItalic = section.getboolean(name + "Italic", isItalicDef)
     return (fontName, fontSize, isBold, isItalic)
Exemple #14
0
    def from_config(cls, section: configparser.SectionProxy, option_handler_id: str = None) -> OptionHandler:
        """
        Create a handler of this class based on the configuration in the config section.

        :param section: The configuration section
        :param option_handler_id: Optional extra identifier
        :return: A handler object
        :rtype: OptionHandler
        """
        # The option handler ID is our primary link prefix
        responsible_for_links = []
        try:
            prefix = IPv6Network(option_handler_id)
            responsible_for_links.append(prefix)
        except ValueError:
            raise configparser.ParsingError("The ID of [{}] must be the primary link prefix".format(section.name))

        # Add any extra prefixes
        additional_prefixes = section.get("additional-prefixes", "").split(" ")
        for additional_prefix in additional_prefixes:
            if not additional_prefix:
                continue

            try:
                prefix = IPv6Network(additional_prefix)
                responsible_for_links.append(prefix)
            except ValueError:
                raise configparser.ParsingError("'{}' is not a valid IPv6 prefix".format(additional_prefix))

        # Get the lifetimes
        address_preferred_lifetime = section.getint("address-preferred-lifetime", 3600)
        address_valid_lifetime = section.getint("address-valid-lifetime", 7200)
        prefix_preferred_lifetime = section.getint("prefix-preferred-lifetime", 43200)
        prefix_valid_lifetime = section.getint("prefix-valid-lifetime", 86400)

        sqlite_filename = section.get("assignments-file")

        return cls(
            sqlite_filename,
            responsible_for_links,
            address_preferred_lifetime,
            address_valid_lifetime,
            prefix_preferred_lifetime,
            prefix_valid_lifetime,
        )
Exemple #15
0
 def __init__(self, config: SectionProxy, experiment):
     super().__init__(
         filename=config.get("name"),
         directory=config.get("path"),
         activation_level=config.getint("level"),
         experiment=experiment,
         name=config.get("name"),
         encrypt=config.getboolean("encrypt", fallback=False),
     )
 def __init__(self, section: SectionProxy, items_type: str):
     for key in section:
         if items_type == "string":
             self.__setattr__(key, section[key])
         elif items_type == "int":
             try:
                 self.__setattr__(key, section.getint(key))
             except ValueError:
                 print(f'Could not convert option [{section.name}] -> "{key}" to int')
                 raise
Exemple #17
0
 def collect_init_args(cls,
                       config: configparser.SectionProxy) -> Dict[str, Any]:
     try:
         _add_default_kodi_url(config)
         args = NetworkMixin.collect_init_args(config)
         args["idle_time"] = config.getint("idle_time", fallback=120)
         return args
     except ValueError as error:
         raise ConfigurationError("Configuration error " +
                                  str(error)) from error
Exemple #18
0
    def _check_kit_section(self, section_name: str,
                           section_params: SectionProxy) -> None:
        splitted_section_name = section_name.split(" ")
        valid_section = True
        if len(splitted_section_name) != 3:
            valid_section = False

        if valid_section:
            try:
                kit_index = int(splitted_section_name[1])
                analyte = Analyte.__members__[splitted_section_name[2]]
            except Exception:
                try:
                    kit_index = int(splitted_section_name[2])
                    analyte = Analyte.__members__[splitted_section_name[1]]
                except Exception:
                    valid_section = False

        if valid_section:
            current_kit = self.kits.setdefault((kit_index, analyte), KitData())
        else:
            analytes_str = "|".join(Analyte.__members__.keys())
            sys.stderr.write("WARNING: '{}' kit section is invalid. "
                             "The format is as following:\n"
                             "KIT n [{}]\nor\nKIT [{}] n\n".format(
                                 section_name, analytes_str, analytes_str))
            return

        for param_name in (
                "name",
                "cancer_site",
                "adapter_r1",
                "adapter_r2",
                "target_list",
                "bait_list",
                "indels_hg19",
                "indels_hg38",
                "amplicons",
        ):
            if param_name in section_params:
                param_value = section_params[param_name]
                if param_name.startswith("indels_"):
                    param_value = [
                        filename.strip() for filename in param_value.split(",")
                    ]
                setattr(current_kit, param_name, param_value)

        for param_name in ("mean_len_library", "sd_len_library"):
            if param_name in section_params:
                try:
                    setattr(current_kit, param_name,
                            section_params.getint(param_name))
                except Exception:
                    setattr(current_kit, param_name, None)
Exemple #19
0
    def param_validation(params: configparser.SectionProxy) -> None:
        """
        Validation of the params definitions from config.cfg. validates relations between objects, as the agent or the targets are in the Area
        If not valid, raise InvalidParamsException
        """
        N = params.getint('N')
        num_cells = N * N
        target_locations = eval(params['TARGET_LOCATIONS'])
        agent_position = eval(params['AGENT_POSITION'])

        if target_locations is None:
            assert params.getint('NUM_TARGETS') <= num_cells
        else:
            assert len(target_locations) <= num_cells
            assert all(
                [target_location[0] <= N - 1 and target_location[1] <= N - 1 for target_location in target_locations])
        if isinstance(agent_position, tuple):
            assert agent_position[0] <= N - 1
            assert agent_position[1] <= N - 1
        assert 0 < params.getfloat('pta') <= 1
        assert 0 < params.getfloat('alpha') <= 1
        assert 0 < params.getfloat('INITIAL_PRIOR_P') < 1
Exemple #20
0
 def _init_client(self, config: SectionProxy):
     ca_file = config.get("ca_file_path") if config.getboolean(
         "use_ssl") else None
     client = pymongo.MongoClient(
         host=config.get("host"),
         port=config.getint("port"),
         username=config.get("user"),
         password=config.get("password"),
         tls=config.getboolean("use_ssl"),
         tlsCAFile=ca_file,
         authSource=config.get("auth_source"),
     )
     return client
Exemple #21
0
    def from_config(cls, section: configparser.SectionProxy, option_handler_id: str = None) -> OptionHandler:
        """
        Create a handler of this class based on the configuration in the config section.

        :param section: The configuration section
        :param option_handler_id: Optional extra identifier
        :return: A handler object
        :rtype: OptionHandler
        """
        preference = section.getint('preference')
        if preference is None:
            raise configparser.NoOptionError('preference', section.name)

        return cls(preference)
    def range_from_parsed_config(cls, parsed: SectionProxy):
        """Create KarmaRange from parsed section"""
        blog = logging.getLogger('botlog')
        blog.info(f'Parsing section {parsed.name}')

        timeout_v = int(parsed['timeout'][:-1])
        timeout_s = parsed['timeout'][-1]

        if timeout_s == 's':
            timeout = datetime.timedelta(seconds=timeout_v)
        elif timeout_s == 'm':
            timeout = datetime.timedelta(minutes=timeout_v)
        elif timeout_s == 'h':
            timeout = datetime.timedelta(hours=timeout_v)
        elif timeout_s == 'd':
            timeout = datetime.timedelta(days=timeout_v)
        elif timeout_s == 'w':
            timeout = datetime.timedelta(weeks=timeout_v)
        else:
            raise ConfigParseError('Invalid timeout symbol: ' + timeout_s)

        try:
            obj = cls(name=parsed['name'],
                      min_range=cls._read_int_or_inf(parsed['range_min']),
                      max_range=cls._read_int_or_inf(parsed['range_max']),
                      enable_plus=parsed.getboolean('enable_plus'),
                      enable_minus=parsed.getboolean('enable_minus'),
                      plus_value=parsed.getint('plus_value'),
                      minus_value=parsed.getint('minus_value'),
                      day_max=cls._read_int_or_inf(parsed['day_max']),
                      timeout=timeout)
        except KeyError as e:
            msg = f'Value of {str(e)} not found for section {parsed.name}'
            blog.fatal(msg)
            raise ConfigParseError(msg)
        return obj
def parse_value(section: configparser.SectionProxy, name: str, value_type: ConfigValueType, default_value: TypeOfConfigValue) -> TypeOfConfigValue:
	try:
		if value_type == ConfigValueType.Bool:
			return parse_bool(section[name])
		if value_type in (ConfigValueType.FilePath, ConfigValueType.FolderPath):
			return Path(section[name]).expanduser()
		if value_type == ConfigValueType.StringList:
			return parse_string_list(section[name])
		if value_type in (ConfigValueType.FilePathList, ConfigValueType.FolderPathList):
			return parse_path_list(section[name])
		if value_type == ConfigValueType.Integer:
			return section.getint(name, default_value)
		return section[name]
	except KeyError:
		return default_value
Exemple #24
0
    def collect_init_args(cls, config: configparser.SectionProxy) -> Dict[str, Any]:
        args = {} # type: Dict[str, Any]
        try:
            address = config.get("server", fallback="localhost")
            args["server"] = MinecraftServer.lookup(address)
        except ValueError as error:
            raise ConfigurationError(
                "Unable to look up server: {}".format(error)
            ) from error
        
        try:
            args["retries"] = config.getint("retries", fallback=3)
        except ValueError as error:
            raise ConfigurationError("Retries must be integer") from error

        return args
Exemple #25
0
 def collect_init_args(cls,
                       config: configparser.SectionProxy) -> Dict[str, Any]:
     try:
         args = {}  # type: Dict[str, Any]
         args['timeout'] = config.getint('timeout', fallback=5)
         args['url'] = config['url']
         args['username'] = config.get('username')
         args['password'] = config.get('password')
         if (args['username'] is None) != (args['password'] is None):
             raise ConfigurationError('Username and password must be set')
         return args
     except ValueError as error:
         raise ConfigurationError('Configuration error ' +
                                  str(error)) from error
     except KeyError as error:
         raise ConfigurationError('Lacks ' + str(error) +
                                  ' config entry') from error
Exemple #26
0
 def __init__(self, config: SectionProxy, **kwargs):
     host = config.get("host")
     port = config.getint("port")
     username = config.get("user")
     password = config.get("password")
     tls = config.getboolean("use_ssl")
     tlsCAFile = config.get("ca_file_path") if tls else None
     authSource = config.get("auth_source")
     super().__init__(
         host=host,
         port=port,
         username=username,
         password=password,
         authSource=authSource,
         tls=tls,
         tlsCAFile=tlsCAFile,
         **kwargs,
     )
Exemple #27
0
 def create(cls, name: str,
            config: configparser.SectionProxy) -> "XIdleTime":
     with warnings.catch_warnings():
         warnings.simplefilter("ignore", FutureWarning)
         try:
             return cls(
                 name,
                 config.getint("timeout", fallback=600),
                 config.get("method", fallback="sockets"),
                 re.compile(config.get("ignore_if_process",
                                       fallback=r"a^")),
                 re.compile(config.get("ignore_users", fallback=r"a^")),
             )
         except re.error as error:
             raise ConfigurationError(
                 "Regular expression is invalid: {}".format(error),
             ) from error
         except ValueError as error:
             raise ConfigurationError(
                 "Unable to parse configuration: {}".format(error),
             ) from error
Exemple #28
0
    def __init__(
        self,
        config: SectionProxy,
        client: pymongo.MongoClient = None,
        experiment=None,
    ):

        if not client:
            client = AutoMongoClient(config=config)

        if not self.validate_client(client, config):
            raise ValueError(
                "The client and configuration contain different values for 'host'."
            )

        super().__init__(client=client,
                         database=config.get("database"),
                         collection=config.get("collection"),
                         activation_level=config.getint("level"),
                         experiment=experiment,
                         name=config.get("name"),
                         encrypt=config.getboolean("encrypt", fallback=False),
                         misc_collection=config.get("misc_collection"))
 def __init__(self, record_conf: configparser.SectionProxy):
     self.A = record_conf.getboolean('A', False)
     self.AAAA = record_conf.getboolean('AAAA', False)
     self.name = record_conf.name
     self.ttl = record_conf.getint('ttl', 120)
 def configure(self, config: configparser.SectionProxy) -> None:
     """See base class method."""
     super().configure(config)
     self._line = config.getint(
         "line{suffix}".format(suffix=self._option_suffix),
         fallback=self._line)
 def configure(self, config: configparser.SectionProxy) -> None:
     """Configure the amount of characters to skip."""
     self._prefix_length = config.getint(
         "skip{suffix}".format(suffix=self._option_suffix),
         fallback=self._prefix_length,
     )
Exemple #32
0
 def __init__(self, config: SectionProxy, config_path=""):
     # minimal distance between clusters = page_width / width_denom
     self.width_denom = config.getint('ImageWidthDenominator', fallback=10)
Exemple #33
0
 def __init__(self, section: SectionProxy):
     # noinspection PyUnresolvedReferences
     self.request_timeout = section.getint('request_timeout')
Exemple #34
0
def get_forecasts_panel(
        images: Icons, fonts: Fonts,
        config: SectionProxy) -> tuple[Image.Image, tuple[str, str]]:
    logger = logging.getLogger(__name__)
    logger.info('Generating forecast panel')
    icon_width = config.getint('ICON_WIDTH')
    count = 7
    x_size = 1872
    y_size = 800
    (forecasts, first_position,
     first_position_name) = get_forecasts(config.get('FMI_LOCATION'), count, 6)
    logger.info('Received data: %s', repr(forecasts))

    dates = sorted(forecasts.keys())
    image = Image.new('L', (x_size, y_size), 0xff)
    draw = ImageDraw.Draw(image)

    utils.draw_title(draw, fonts['font_sm'], 'FORECAST', first_position_name,
                     fonts['font_xxs'])

    data_y_base = 100

    for date, i in zip(dates, range(len(dates))):
        is_daylight = get_is_daylight(first_position, date)
        data = forecasts[date]
        date_local = utils.utc_datetime_string_to_local_datetime(date)
        date_formatted = date_local.strftime('%-H:%M')
        x_step = x_size // count
        x_base = x_step // 2

        # Time
        draw.text((x_base + i * x_step, data_y_base + 10),
                  date_formatted,
                  font=(fonts['font_sm'] if date_formatted != "15:00" else
                        fonts['font_sm_bold']),
                  fill=0,
                  anchor='mt')

        # Weather icon
        icon_position = (x_base + i * x_step -
                         config.getint('ICON_WIDTH') // 2, data_y_base + 80)
        weather_icon = icons.get_scaled_image(
            get_forecats_weather_icon(data['WeatherSymbol3'], is_daylight,
                                      images, fonts, config), icon_width)
        image.paste(weather_icon, icon_position, weather_icon)

        # Warning icon
        if (utils.show_temperatur_warning_icon(data["Temperature"], date_local,
                                               config)):
            warning_icon = icons.get_scaled_image(images['misc']['warning'],
                                                  50)
            image.paste(warning_icon,
                        (icon_position[0] + weather_icon.width -
                         2 * warning_icon.width // 3, icon_position[1] +
                         weather_icon.height - 2 * warning_icon.height // 3),
                        warning_icon)

        # Temperature
        utils.draw_quantity(draw, (x_base + i * x_step, data_y_base + 350),
                            str(round(data["Temperature"])), '°C', fonts)
        # Wind speed
        utils.draw_quantity(draw, (x_base + i * x_step, data_y_base + 420),
                            str(round(data["WindSpeedMS"])), 'm/s', fonts)

        # Cloud cover
        cloud_cover_raw = data["TotalCloudCover"]
        cloud_cover = math.nan if math.isnan(
            cloud_cover_raw) else cloud_cover_raw / 100 * 8
        cloud_cover_icon = icons.get_scaled_image(
            utils.get_cloud_cover_icon(cloud_cover, images, fonts, config),
            160)
        image.paste(cloud_cover_icon,
                    (x_base + i * x_step - cloud_cover_icon.width // 2,
                     data_y_base + 440), cloud_cover_icon)

        # Wind direction
        wind_image = icons.get_scaled_image(images['misc']['wind_icon'], 160)
        wind_image_rot = wind_image.rotate(-data['WindDirection'] + 180,
                                           fillcolor=0xff,
                                           resample=Image.BICUBIC)
        image.paste(wind_image_rot,
                    (x_base + i * x_step - wind_image_rot.width // 2,
                     data_y_base + 440), wind_image_rot)

    # Borders
    if (config.getboolean('DRAW_PANEL_BORDERS')):
        draw.polygon([(0, 0), (x_size - 1, 0), (x_size - 1, y_size - 1),
                      (0, y_size - 1), (0, 0)])

    return (image, first_position)