示例#1
0
def _get_site_dict(config_dict, service, *args):
    """Obtain the site options, with defaults from the StdRESTful section.
    If the service is not enabled, or if one or more required parameters is
    not specified, then return None."""

    try:
        site_dict = accumulateLeaves(config_dict['StdRESTful'][service],
                                     max_level=1)
    except KeyError:
        log_err("restx: %s: no config info. Skipped." % service)

        return None

    try:
        if not to_bool(site_dict['enabled']):
            log_inf("restx: %s: service not enabled." % service)
    except KeyError:
        pass

    try:
        for option in args:
            if site_dict[option] == 'replace_me':
                raise KeyError(option)
    except KeyError, e:
        log_dbg("restx: %s. Data will not be posted: missing option %s" %
                (service, e))

        return None
示例#2
0
文件: owm.py 项目: hes19073/hesweewx
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        username: OpenWeatherMap username

        password: OpenWeatherMap password

        station_name: station name

        latitude: Station latitude in decimal degrees
        Default is station latitude

        longitude: Station longitude in decimal degrees
        Default is station longitude

        altitude: Station altitude in meters
        Default is station altitude
        """
        super(OpenWeatherMap, self).__init__(engine, config_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = config_dict["StdRESTful"]["OpenWeatherMap"]
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict["username"]
            site_dict["password"]
            site_dict["station_name"]
        except KeyError, e:
            logerr("Data will not be posted: Missing option %s" % e)
            return
    def __init__(self, engine, config_dict):

        super(OpsGenieHeartbeat, self).__init__(engine, config_dict)

        # Extract the required parameters. If one of them is missing,
        # a KeyError exception will occur. Be prepared to catch it.
        try:
            # Extract a copy of the dictionary with the registry options:
            _opsgenie_dict = accumulateLeaves(
                config_dict['OpsGenie']['Heartbeat'], max_level=1)

            # Should the service be run?
            if not to_bool(_opsgenie_dict.pop('send_heartbeat', False)):
                syslog.syslog(
                    syslog.LOG_INFO, "restx: OpsGenieHeartbeat: "
                    "Send Heartbeat not requested.")
                return

            if _opsgenie_dict['apiKey'] is None:
                raise KeyError("apiKey")
            if _opsgenie_dict['heartbeatName'] is None:
                raise KeyError("heartbeatName")
        except KeyError, e:
            syslog.syslog(
                syslog.LOG_DEBUG, "restx: OpsGenieHeartbeat: "
                "Heartbeats will not be sent. Missing option {}".format(e))
            return
示例#4
0
    def __init__(self, engine, cfg_dict):
        """This service recognizes standard restful options plus the following:

        Required parameters:

        database: name of the database at the server

        Optional parameters:

        host: server hostname
        Default is localhost

        port: server port
        Default is 8086

        server_url: full restful endpoint of the server
        Default is None

        measurement: name of the measurement
        Default is 'record'

        tags: comma-delimited list of name=value pairs to identify the
        measurement.  tags cannot contain spaces.
        Default is None

        create_database: should the upload attempt to create database first
        Default is True

        line_format: which line protocol format to use.  Possible values are
        single-line or multi-line.
        Default is single-line

        append_units_label: should units label be appended to name
        Default is True

        obs_to_upload: Which observations to upload.  Possible values are
        most, none, or all.  When none is specified, only items in the inputs
        list will be uploaded.  When all is specified, all observations will be
        uploaded, subject to overrides in the inputs list.
        Default is most

        inputs: dictionary of weewx observation names with optional upload
        name, format, and units
        Default is None

        binding: options include "loop", "archive", or "loop,archive"
        Default is archive
        """
        super(Influx, self).__init__(engine, cfg_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = cfg_dict['StdRESTful']['Influx']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['database']
        except KeyError, e:
            logerr("Data will not be uploaded: Missing option %s" % e)
            return
示例#5
0
 def __init__(self, engine, config_dict):
     super(EveryAware, self).__init__(engine, config_dict)
     loginf("service version is %s" % VERSION)
     try:
         site_dict = config_dict['StdRESTful']['EveryAware']
         site_dict = accumulateLeaves(site_dict, max_level=1)
         site_dict['feeds']
         site_dict['geoLatitude'] = config_dict['Station']['latitude']
         site_dict['geoLongitude'] = config_dict['Station']['longitude']
         site_dict['location'] = config_dict['Station']['location']
         site_dict['altitude'] = config_dict['Station']['altitude']
         site_dict['stationType'] = config_dict['Station']['station_type']
     except KeyError, e:
         logerr("Data will not be posted: Missing option %s" % e)
         return
示例#6
0
    def __init__(self, engine, config_dict):
        super(StdRainlog, self).__init__(engine, config_dict)

        self.protocol_name = 'Rainlog'

        try:
            site_dict = accumulateLeaves(config_dict['StdRESTful']['Rainlog'],
                                         max_level=1)
            site_dict['username']
            site_dict['password']
            site_dict['WEEWX_ROOT'] = config_dict['WEEWX_ROOT']
        except KeyError, e:
            logdbg("rainlog: %s: "
                   "Data will not be posted: Missing option %s" %
                   (self.protocol_name, e))
            return
示例#7
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        appid: APPID from OpenWeatherMap

        station_id: station identifier

        latitude: Station latitude in decimal degrees
        Default is station latitude

        longitude: Station longitude in decimal degrees
        Default is station longitude

        altitude: Station altitude in meters
        Default is station altitude
        """
        super(OpenWeatherMap, self).__init__(engine, config_dict)        
        loginf('service version is %s' % VERSION)
        # Check to make sure this version of weewx supports JSON posts.
        # To do this, look for function weewx.restx.RESTThread.get_post_body
        try:
            getattr(weewx.restx.RESTThread, 'get_post_body')
        except AttributeError:
            loginf('WeeWX needs to be upgraded to V3.8 in order to post to OWM')
            loginf('****   OWM upload skipped')
            return

        try:
            site_dict = config_dict['StdRESTful']['OpenWeatherMap']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['appid']
            site_dict['station_id']
        except KeyError as e:
            logerr("Data will not be posted: Missing option %s" % e)
            return
        site_dict.setdefault('latitude', engine.stn_info.latitude_f)
        site_dict.setdefault('longitude', engine.stn_info.longitude_f)
        site_dict.setdefault('altitude', engine.stn_info.altitude_vt[0])
        site_dict['manager_dict'] = weewx.manager.get_manager_dict(
            config_dict['DataBindings'], config_dict['Databases'], 'wx_binding')

        self.archive_queue = queue.Queue()
        self.archive_thread = OpenWeatherMapThread(self.archive_queue, **site_dict)
        self.archive_thread.start()
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        loginf("Data will be uploaded for %s" % site_dict['station_id'])
示例#8
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        station_id: WindGuru station identifier

        password: WindGuru password
        """
        super(WindGuru, self).__init__(engine, config_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['WindGuru']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['station_id']
            site_dict['password']
        except KeyError, e:
            logerr("Data will not be posted: Missing option %s" % e)
            return
示例#9
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        id: WeatherCloud identifier

        key: WeatherCloud key
        """
        super(WeatherCloud, self).__init__(engine, config_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['WeatherCloud']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['id']
            site_dict['key']
        except KeyError, e:
            logerr("Data will not be posted: Missing option %s" % e)
            return
示例#10
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        username: OpenWeatherMap username

        password: OpenWeatherMap password

        station_name: station name

        latitude: Station latitude in decimal degrees
        Default is station latitude

        longitude: Station longitude in decimal degrees
        Default is station longitude

        altitude: Station altitude in meters
        Default is station altitude
        """
        super(OpenWeatherMap, self).__init__(engine, config_dict)
        loginf('service version is %s' % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['OpenWeatherMap']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['username']
            site_dict['password']
            site_dict['station_name']
        except KeyError as e:
            logerr("Data will not be posted: Missing option %s" % e)
            return

        site_dict.setdefault('latitude', engine.stn_info.latitude_f)
        site_dict.setdefault('longitude', engine.stn_info.longitude_f)
        site_dict.setdefault('altitude', engine.stn_info.altitude_vt[0])
        site_dict['manager_dict'] = weewx.manager.get_manager_dict(
            config_dict['DataBindings'], config_dict['Databases'],
            'wx_binding')

        self.archive_queue = queue.Queue()
        self.archive_thread = OpenWeatherMapThread(self.archive_queue,
                                                   **site_dict)
        self.archive_thread.start()

        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        loginf("Data will be uploaded for %s" % site_dict['station_name'])
示例#11
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        Required parameters:

        server_url: URL of the broker, e.g., something of the form
          mqtt://username:password@localhost:1883/
        Default is None

        Optional parameters:

        unit_system: one of US, METRIC, or METRICWX
        Default is None; units will be those of data in the database

        topic: the MQTT topic under which to post
        Default is 'weather'

        append_units_label: should units label be appended to name
        Default is True

        obs_to_upload: Which observations to upload.  Possible values are
        none or all.  When none is specified, only items in the inputs list
        will be uploaded.  When all is specified, all observations will be
        uploaded, subject to overrides in the inputs list.
        Default is all

        inputs: dictionary of weewx observation names with optional upload
        name, format, and units
        Default is None

        tls: dictionary of TLS parameters used by the Paho client to establish
        a secure connection with the broker.
        Default is None
        """
        super(MQTT, self).__init__(engine, config_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['MQTT']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['server_url']
        except KeyError, e:
            logerr("Data will not be uploaded: Missing option %s" % e)
            return
示例#12
0
    def __init__(self, engine, config_dict):
       
        super(LaMetric, self).__init__(engine, config_dict)
        loginf('service version is %s' % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['LaMetric']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['server_ip']
            site_dict['device_key']
            site_dict['icon']
            site_dict['sound']
        except KeyError as e:
            logerr("Data will not be posted: Missing option %s" % e)
            return
        site_dict['manager_dict'] = weewx.manager.get_manager_dict(
            config_dict['DataBindings'], config_dict['Databases'], 'wx_binding')

        self.archive_queue = queue.Queue()
        self.archive_thread = LaMetricThread(self.archive_queue, **site_dict)
        self.archive_thread.start()
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        loginf("Data will be sent to %s" % site_dict['server_ip'])
示例#13
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        Required parameters:

        server_url: URL of the broker, e.g., something of the form
          mqtt://username:password@localhost:1883/
        Default is None

        Optional parameters:

        unit_system: one of US, METRIC, or METRICWX
        Default is None; units will be those of data in the database

        topic: the MQTT topic under which to post
        Default is 'weather'

        append_units_label: should units label be appended to name
        Default is True

        obs_to_upload: Which observations to upload.  Possible values are
        none or all.  When none is specified, only items in the inputs list
        will be uploaded.  When all is specified, all observations will be
        uploaded, subject to overrides in the inputs list.
        Default is all

        inputs: dictionary of weewx observation names with optional upload
        name, format, and units
        Default is None

        tls: dictionary of TLS parameters used by the Paho client to establish
        a secure connection with the broker.
        Default is None

        agg_topic_loop: Allows to set the destination sub-topic for aggregated
        loop packets (only used if aggregation contains aggregate)
        Default is loop

        agg_topic_archive: Allows to set the destination sub-topic for aggregated
        archive packets (only used if aggregation contains aggregate)
        Default is loop
        """
        super(MQTT, self).__init__(engine, config_dict)
        loginf("service version is %s" % VERSION)
        try:
            site_dict = config_dict['StdRESTful']['MQTT']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['server_url']
        except KeyError as e:
            logerr("Data will not be uploaded: Missing option %s" % e)
            return

        # for backward compatibility: 'units' is now 'unit_system'
        _compat(site_dict, 'units', 'unit_system')

        site_dict.setdefault('client_id', '')
        site_dict.setdefault('topic', 'weather')
        site_dict.setdefault('append_units_label', True)
        site_dict.setdefault('augment_record', True)
        site_dict.setdefault('obs_to_upload', 'all')
        site_dict.setdefault('retain', False)
        site_dict.setdefault('qos', 0)
        site_dict.setdefault('aggregation', 'individual,aggregate')

        site_dict.setdefault('agg_topic_loop', 'loop')
        site_dict.setdefault('agg_topic_archive', 'loop')

        usn = site_dict.get('unit_system', None)
        if usn is not None:
            site_dict['unit_system'] = weewx.units.unit_constants[usn]

        if 'tls' in config_dict['StdRESTful']['MQTT']:
            site_dict['tls'] = dict(config_dict['StdRESTful']['MQTT']['tls'])

        if 'inputs' in config_dict['StdRESTful']['MQTT']:
            site_dict['inputs'] = dict(
                config_dict['StdRESTful']['MQTT']['inputs'])

        site_dict['append_units_label'] = to_bool(
            site_dict.get('append_units_label'))
        site_dict['augment_record'] = to_bool(site_dict.get('augment_record'))
        site_dict['retain'] = to_bool(site_dict.get('retain'))
        site_dict['qos'] = to_int(site_dict.get('qos'))
        binding = site_dict.pop('binding', 'archive')
        loginf("binding to %s" % binding)

        # if we are supposed to augment the record with data from weather
        # tables, then get the manager dict to do it.  there may be no weather
        # tables, so be prepared to fail.
        try:
            if site_dict.get('augment_record'):
                _manager_dict = weewx.manager.get_manager_dict_from_config(
                    config_dict, 'wx_binding')
                site_dict['manager_dict'] = _manager_dict
        except weewx.UnknownBinding:
            pass

        self.archive_queue = Queue.Queue()
        self.archive_thread = MQTTThread(self.archive_queue, **site_dict)
        self.archive_thread.start()

        if 'archive' in binding:
            self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        if 'loop' in binding:
            self.bind(weewx.NEW_LOOP_PACKET, self.new_loop_packet)

        if 'topic' in site_dict:
            loginf("topic is %s" % site_dict['topic'])
        if usn is not None:
            loginf("desired unit system is %s" % usn)
        loginf("data will be uploaded to %s" %
               _obfuscate_password(site_dict['server_url']))
        if 'tls' in site_dict:
            loginf("network encryption/authentication will be attempted")
    def gen_windrose_plots(self):
        """Generate the windrose plots.

        Loop through each 2nd level section (ie [[]]) under
        [ImageStackedWindRoseGenerator] and generate the plot defined by each
        2nd level section.
        """

        # Time period taken to generate plots, set plot count to 0
        t1 = time.time()
        ngen = 0
        # Loop over each time span class (day, week, month, etc.):
        for span in self.image_dict.sections:
            # Now, loop over all plot names in this time span class:
            for plot in self.image_dict[span].sections:
                # Accumulate all options from parent nodes:
                p_options = accumulateLeaves(self.image_dict[span][plot])
                # Get end time for plot. In order try self.gen_ts, last known
                # good archive time stamp and then current time
                self.p_gen_ts = self.gen_ts
                if not self.p_gen_ts:
                    self.p_gen_ts = self.archive.lastGoodStamp()
                    if not self.p_gen_ts:
                        self.p_gen_ts = time.time()
                # Get the period for the plot, default to 24 hours if no
                # period set
                self.period = p_options.as_int('period') if 'period' in p_options else 86400
                # Get the path of the image file we will save
                image_root = os.path.join(self.config_dict['WEEWX_ROOT'],
                                          p_options['HTML_ROOT'])
                # Get image file format. Can use any format PIL can write
                # Default to png
                if 'format' in p_options:
                    file_format = p_options['format']
                else:
                    file_format = "png"
                # Get full file name and path for plot
                img_file = os.path.join(image_root, '%s.%s' % (plot,
                                                               file_format))
                # Check whether this plot needs to be done at all:
                if self.skip_this_plot(img_file, plot):
                    continue
                # Create the subdirectory that the image is to be put in.
                # Wrap in a try block in case it already exists.
                try:
                    os.makedirs(os.path.dirname(img_file))
                except os.error:
                    pass
                # Loop over each line to be added to the plot.
                for line_name in self.image_dict[span][plot].sections:

                    # Accumulate options from parent nodes.
                    line_options = accumulateLeaves(self.image_dict[span][plot][line_name])

                    # See if a plot title has been explicitly requested.
                    # 'label' used for consistency in skin.conf with
                    # ImageGenerator sections
                    label = line_options.get('label')
                    if label:
                        self.label = unicode(label, 'utf8')
                    else:
                        # No explicit label so set label to nothing
                        self.label = label
                    # See if a time_stamp has been explicitly requested.
                    self.t_stamp = line_options.get('time_stamp')
                    # See if time_stamp location has been explicitly set
                    _location = line_options.get('time_stamp_location')
                    if _location:
                        self.t_stamp_loc = [x.upper() for x in _location]
                    else:
                        self.t_stamp_loc = None
                    # See what SQL variable type to use for this plot and get
                    # corresponding 'direction' type. Can really only plot
                    # windSpeed and windGust, if anything else default to
                    # windSpeed.
                    self.obName = line_options.get('data_type', line_name)
                    if self.obName == 'windSpeed':
                        self.dirName = 'windDir'
                    elif self.obName == 'windGust':
                        self.dirName = 'windGustDir'
                    else:
                        self.obName == 'windSpeed'
                        self.dirName = 'windDir'
                    # Get our data tuples for speed and direction.
                    vector_tspan = TimeSpan(self.p_gen_ts - self.period + 1,
                                            self.p_gen_ts)
                    (_, time_vec_t_ws_stop, data_speed) = self.archive.getSqlVectors(vector_tspan,
                                                                                     self.obName)
                    (_, time_vec_t_wd_stop, dir_vec) = self.archive.getSqlVectors(vector_tspan,
                                                                                  self.dirName)
                    # Convert our speed values to the units we are going to
                    # use in our plot
                    speed_vec = self.converter.convert(data_speed)
                    # Get units for display on legend
                    self.units = self.skin_dict['Units']['Labels'][speed_vec[1]].strip()
                    # Find maximum speed from our data
                    max_speed = max(speed_vec[0])
                    # Set upper speed range for our plot, set to a multiple of
                    # 10 for a neater display
                    max_speed_range = (int(max_speed / 10.0) + 1) * 10
                    # Setup 2D list with speed range boundaries in speed_list[0]
                    # petal colours in speed_list[1]
                    speed_list = [[0 for x in range(7)] for x in range(2)]
                    # Store petal colours
                    speed_list[1] = self.petal_colors
                    # Loop though each speed range boundary and store in
                    # speed_list[0]
                    i = 1
                    while i < 7:
                        speed_list[0][i] = self.speedFactor[i] * max_speed_range
                        i += 1
                    # Setup 2D list for wind direction
                    # wind_bin[0] represents each of 16 compass directions
                    # ([0] is N, [1] is ENE etc).
                    # wind_bin[1] holds count of obs in a particular speed range
                    # for given direction
                    wind_bin = [[0 for x in range(7)] for x in range(17)]
                    # Setup list to hold obs counts for each speed range
                    speed_bin = [0 for x in range(7)]
                    # How many obs do we have?
                    samples = len(time_vec_t_ws_stop[0])
                    # Loop through each sample and increment direction counts
                    # and speed ranges for each direction as necessary. 'None'
                    # direction is counted as 'calm' (or 0 speed) and
                    # (by definition) no direction and are plotted in the
                    # 'bullseye' on the plot
                    i = 0
                    while i < samples:
                        if (speed_vec[0][i] is None) or (dir_vec[0][i] is None):
                            wind_bin[16][6] += 1
                        else:
                            bin = int((dir_vec[0][i] + 11.25) / 22.5) % 16
                            if speed_vec[0][i] > speed_list[0][5]:
                                wind_bin[bin][6] += 1
                            elif speed_vec[0][i] > speed_list[0][4]:
                                wind_bin[bin][5] += 1
                            elif speed_vec[0][i] > speed_list[0][3]:
                                wind_bin[bin][4] += 1
                            elif speed_vec[0][i] > speed_list[0][2]:
                                wind_bin[bin][3] += 1
                            elif speed_vec[0][i] > speed_list[0][1]:
                                wind_bin[bin][2] += 1
                            elif speed_vec[0][i] > 0:
                                wind_bin[bin][1] += 1
                            else:
                                wind_bin[bin][0] += 1
                        i += 1
                    # Add 'None' obs to 0 speed count
                    speed_bin[0] += wind_bin[16][6]
                    # Don't need the 'None' counts so we can delete them
                    del wind_bin[-1]
                    # Now set total (direction independent) speed counts. Loop
                    # through each petal speed range and increment direction
                    # independent speed ranges as necessary
                    j = 0
                    while j < 7:
                        i = 0
                        while i < 16:
                            speed_bin[j] += wind_bin[i][j]
                            i += 1
                        j += 1
                    # Calc the value to represented by outer ring
                    # (range 0 to 1). Value to rounded up to next multiple of
                    # 0.05 (ie next 5%)
                    self.max_ring_value = (int(max(sum(b) for b in wind_bin) / (0.05 * samples)) + 1) * 0.05
                    # Find which wind rose arm to use to display ring range
                    # labels - look for one that is relatively clear. Only
                    # consider NE, SE, SW and NW preference in order is
                    # SE, SW, NE and NW
                    # Default to SE
                    label_dir = 6
                    # Is SE clear?
                    if sum(wind_bin[6]) / float(samples) <= 0.3 * self.max_ring_value:
                        # If so take it
                        label_dir = 6
                    else:
                        # If not lets loop through the others
                        for i in [10, 2, 14]:
                            # Is SW, NE or NW clear
                            if sum(wind_bin[i])/float(samples) <= 0.3 * self.max_ring_value:
                                # If so let's take it and exit the for loop
                                label_dir = i
                                break
                        else:
                            # If none are free then let's take the smallest of
                            # the four
                            # Set max possible number of readings + 1
                            label_count = samples + 1
                            for i in [2, 6, 10, 14]:    # Loop through directions
                                # If this direction has fewer obs than previous
                                # best (least)
                                if sum(wind_bin[i]) < label_count:
                                    # Set min count so far to this bin
                                    label_count = sum(wind_bin[i])
                                    # Set label_dir to this direction
                                    label_dir = i
                    self.label_dir = label_dir
                    # Set up an Image object to hold our windrose plot
                    self.windrose_image_setup()
                    # Get a Draw object to draw on
                    self.draw = ImageDraw.Draw(self.image)
                    # Set fonts to be used
                    self.plotFont = get_font_handle(self.font_path,
                                                    self.plot_font_size)
                    self.legendFont = get_font_handle(self.font_path,
                                                      self.legend_font_size)
                    self.labelFont = get_font_handle(self.font_path,
                                                     self.label_font_size)
                    # Estimate space required for the legend
                    text_width, text_height = self.draw.textsize("0 (100%)",
                                                                 font=self.legendFont)
                    legend_width = int(text_width + 2 * self.legend_bar_width + 1.5 * self.plot_border)
                    # Estimate space required for label (if required)
                    text_width, text_height = self.draw.textsize("Wind Rose",
                                                                 font=self.labelFont)
                    if self.label:
                        label_height = int(text_width+self.plot_border)
                    else:
                        label_height = 0
                    # Calculate the diameter of the circular plot space in
                    # pixels. Two diameters are calculated, one based on image
                    # height and one based on image width. We will take the
                    # smallest one. To prevent optical distortion for small
                    # plots diameter will be divisible by 22
                    self.roseMaxDiameter = min(int((self.image_height - 2 * self.plot_border - label_height / 2) / 22.0) * 22,
                                               int((self.image_width - (2 * self.plot_border + legend_width)) / 22.0) * 22)
                    if self.image_width > self.image_height:    # If wider than height
                        text_width, text_height = self.draw.textsize("W",
                                                                     font=self.plotFont)
                        # x coord of windrose circle origin(0,0) top left corner
                        self.originX = self.plot_border + text_width + 2 + self.roseMaxDiameter / 2
                        # y coord of windrose circle origin(0,0) is top left corner
                        self.originY = int(self.image_height / 2)
                    else:
                        # x coord of windrose circle origin(0,0) top left corner
                        self.originX = 2 * self.plot_border + self.roseMaxDiameter / 2
                        # y coord of windrose circle origin(0,0) is top left corner
                        self.originY = 2 * self.plot_border + self.roseMaxDiameter / 2
                    # Setup windrose plot. Plot circles, range rings, range
                    # labels, N-S and E-W centre lines and compass pont labels
                    self.windrose_plot_setup()
                    # Plot wind rose petals
                    # Each petal is constructed from overlapping pieslices
                    # starting from outside (biggest) and working in (smallest)
                    # Start at 'North' windrose petal
                    a = 0
                    # Loop through each wind rose arm
                    while a < len(wind_bin):
                        s = len(speed_list[0]) - 1
                        cum_radius = sum(wind_bin[a])
                        if cum_radius > 0:
                            arm_radius = int((10 * self.roseMaxDiameter * sum(wind_bin[a])) / (11 * 2.0 * self.max_ring_value * samples))
                            while s > 0:
                                # Calc radius of current arm
                                pie_radius = int(round(arm_radius * cum_radius/sum(wind_bin[a]) + self.roseMaxDiameter / 22, 0))
                                # Set bound box for pie slice
                                bbox = (self.originX-pie_radius,
                                        self.originY-pie_radius,
                                        self.originX+pie_radius,
                                        self.originY+pie_radius)
                                # Draw pie slice
                                self.draw.pieslice(bbox,
                                                   int(a * 22.5 - 90 - self.petal_width / 2),
                                                   int(a * 22.5 - 90 + self.petal_width / 2),
                                                   fill=speed_list[1][s], outline='black')
                                cum_radius -= wind_bin[a][s]
                                s -= 1  # Move 'in' for next pieslice
                        a += 1  # Next arm
                    # Draw 'bullseye' to represent windSpeed=0 or calm
                    # Produce the label
                    label0 = str(int(round(100.0 * speed_bin[0] / sum(speed_bin), 0))) + '%'
                    # Work out its size, particularly its width
                    text_width, text_height = self.draw.textsize(label0,
                                                                 font=self.plotFont)
                    # Size the bound box
                    bbox = (int(self.originX - self.roseMaxDiameter / 22),
                            int(self.originY - self.roseMaxDiameter / 22),
                            int(self.originX + self.roseMaxDiameter / 22),
                            int(self.originY + self.roseMaxDiameter / 22))
                    self.draw.ellipse(bbox,
                                      outline='black',
                                      fill=speed_list[1][0])   # Draw the circle
                    self.draw.text((int(self.originX-text_width / 2), int(self.originY - text_height / 2)),
                                   label0,
                                   fill=self.plot_font_color,
                                   font=self.plotFont)   # Display the value
                    # Setup the legend. Draw label/title (if set), stacked bar,
                    # bar labels and units
                    self.legend_setup(speed_list, speed_bin)
                # Save the file.
                self.image.save(img_file)
                ngen += 1
        if self.log_success:
            syslog.syslog(syslog.LOG_INFO, "imageStackedWindRose: Generated %d images for %s in %.2f seconds" % (ngen,
                                                                                                                 self.skin_dict['REPORT_NAME'],
                                                                                                                 time.time() - t1))
示例#15
0
    def __init__(self, engine, config_dict):

        super(OpsGenieAlerts, self).__init__(engine, config_dict)

        if not config_dict['OpsGenie'].has_key('Alerts'):
            syslog.syslog(
                syslog.LOG_INFO,
                "restx: OpsGenieAlerts: no Alerts section! No alerts will be set"
            )
            return

        if not config_dict['OpsGenie']['Alerts'].has_key('apiKey'):
            syslog.syslog(
                syslog.LOG_INFO,
                "restx: OpsGenieAlerts: no apiKey! No alerts will be set")
            return

        if config_dict['OpsGenie']['Alerts'].has_key('ExpressionUnits'):
            if config_dict['OpsGenie']['Alerts']['ExpressionUnits'] == "US":
                self.unit_system = weewx.US
            if config_dict['OpsGenie']['Alerts'][
                    'ExpressionUnits'] == "METRIC":
                self.unit_system = weewx.METRIC
            if config_dict['OpsGenie']['Alerts'][
                    'ExpressionUnits'] == "METRICWX":
                self.unit_system = weewx.METRICWX
        else:
            self.unit_system = None

        self.alerts = dict()

        for subsection in config_dict['OpsGenie']['Alerts'].sections:
            alert_config = accumulateLeaves(
                config_dict['OpsGenie']['Alerts'][subsection], max_level=1)

            if not alert_config.has_key('Alias'):
                syslog.syslog(
                    syslog.LOG_INFO,
                    "restx: OpsGenieAlerts: Missing Alias in config section: ".
                    format(subsection))
                continue

            self.alerts[subsection] = dict()
            self.alerts[subsection]['Alias'] = alert_config['Alias']

            try:
                if alert_config['Expression'] is None:
                    raise KeyError("Expression")
                if alert_config['Message'] is None:
                    raise KeyError("Message")
            except KeyError, e:
                syslog.syslog(
                    syslog.LOG_INFO, "restx: OpsGenieAlerts: "
                    "Missing option {} for Alert: {}".format(
                        e, self.alerts[subsection]['Alias']))
                return

            self.alerts[subsection]['Expression'] = alert_config['Expression']
            self.alerts[subsection]['AlertActive'] = False

            self.alerts[subsection]['my_queues'] = Queue.Queue()
            self.alerts[subsection]['my_threads'] = OpsGenieAlertsThread(
                self.alerts[subsection]['my_queues'], alert_config)
            self.alerts[subsection]['my_threads'].start()
            syslog.syslog(
                syslog.LOG_INFO, "restx: OpsGenieAlerts: "
                "Alerts will be created for {}.".format(
                    self.alerts[subsection]['Alias']))
示例#16
0
    def genWindRosePlots(self):
        """Generate the windrose plots.

        Loop through each 2nd level section (ie [[]]) under
        [ImageStackedWindRoseGenerator] and generate the plot defined by each
        2nd level section.
        """

        # Time period taken to generate plots, set plot count to 0
        t1 = time.time()
        ngen = 0
        # Loop over each time span class (day, week, month, etc.):
        for span in self.image_dict.sections:
            # Now, loop over all plot names in this time span class:
            for plot in self.image_dict[span].sections:
                # Accumulate all options from parent nodes:
                p_options = accumulateLeaves(self.image_dict[span][plot])
                # Get end time for plot. In order try self.gen_ts, last known
                # good archive time stamp and then current time
                self.p_gen_ts = self.gen_ts
                if not self.p_gen_ts:
                    self.p_gen_ts = self.archive.lastGoodStamp()
                    if not self.p_gen_ts:
                        self.p_gen_ts = time.time()
                # Get the period for the plot, default to 24 hours if no
                # period set
                self.period = p_options.as_int('period') if p_options.has_key('period') else 86400
                # Get the path of the image file we will save
                image_root = os.path.join(self.config_dict['WEEWX_ROOT'],
                                          p_options['HTML_ROOT'])
                # Get image file format. Can use any format PIL can write
                # Default to png
                if p_options.has_key('format'):
                    format = p_options['format']
                else:
                    format = "png"
                # Get full file name and path for plot
                img_file = os.path.join(image_root, '%s.%s' % (plot,
                                                               format))
                # Check whether this plot needs to be done at all:
                if self.skipThisPlot(img_file, plot):
                    continue
                # Create the subdirectory that the image is to be put in.
                # Wrap in a try block in case it already exists.
                try:
                    os.makedirs(os.path.dirname(img_file))
                except:
                    pass
                # Loop over each line to be added to the plot.
                for line_name in self.image_dict[span][plot].sections:

                    # Accumulate options from parent nodes.
                    line_options = accumulateLeaves(self.image_dict[span][plot][line_name])

                    # See if a plot title has been explicitly requested.
                    # 'label' used for consistency in skin.conf with
                    # ImageGenerator sections
                    label = line_options.get('label')
                    if label:
                        self.label = unicode(label, 'utf8')
                    else:
                        # No explicit label so set label to nothing
                        self.label = label
                    # See if a time_stamp has been explicitly requested.
                    self.t_stamp = line_options.get('time_stamp')
                    # See if time_stamp location has been explicitly set
                    _location = line_options.get('time_stamp_location')
                    if _location:
                        self.t_stamp_loc = [x.upper() for x in _location]
                    else:
                        self.t_stamp_loc = None
                    # See what SQL variable type to use for this plot and get
                    # corresponding 'direction' type. Can really only plot
                    # windSpeed and windGust, if anything else default to
                    # windSpeed.
                    self.obName = line_options.get('data_type', line_name)
                    if self.obName == 'windSpeed':
                        self.dirName = 'windDir'
                    elif self.obName == 'windGust':
                        self.dirName = 'windGustDir'
                    else:
                        self.obName == 'windSpeed'
                        self.dirName = 'windDir'
                    # Get our data tuples for speed and direction.
                    getSqlVectors_TS = TimeSpan(self.p_gen_ts - self.period + 1,
                                                self.p_gen_ts)
                    (_, time_vec_t_ws_stop, data_speed) = self.archive.getSqlVectors(getSqlVectors_TS,
                                                                                     self.obName)
                    (_, time_vec_t_wd_stop, dir_vec) = self.archive.getSqlVectors(getSqlVectors_TS,
                                                                                  self.dirName)
                    # Convert our speed values to the units we are going to
                    # use in our plot
                    speed_vec = self.converter.convert(data_speed)
                    # Get units for display on legend
                    self.units = self.skin_dict['Units']['Labels'][speed_vec[1]].strip()
                    # Find maximum speed from our data
                    maxSpeed = max(speed_vec[0])
                    # Set upper speed range for our plot, set to a multiple of
                    # 10 for a neater display
                    maxSpeedRange = (int(maxSpeed / 10.0) + 1) * 10
                    # Setup 2D list with speed range boundaries in speedList[0]
                    # petal colours in speedList[1]
                    speedList = [[0 for x in range(7)] for x in range(2)]
                    # Store petal colours
                    speedList[1] = self.petal_colors
                    # Loop though each speed range boundary and store in
                    # speedList[0]
                    i = 1
                    while i < 7:
                        speedList[0][i] = self.speedFactor[i] * maxSpeedRange
                        i += 1
                    # Setup 2D list for wind direction
                    # windBin[0] represents each of 16 compass directions
                    # ([0] is N, [1] is ENE etc).
                    # windBin[1] holds count of obs in a partiuclr speed range
                    # for given direction
                    windBin = [[0 for x in range(7)] for x in range(17)]
                    # Setup list to hold obs counts for each speed range
                    speedBin = [0 for x in range(7)]
                    # How many obs do we have?
                    samples = len(time_vec_t_ws_stop[0])
                    # Loop through each sample and increment direction counts
                    # and speed ranges for each direction as necessary. 'None'
                    # direction is counted as 'calm' (or 0 speed) and
                    # (by definition) no direction and are plotted in the
                    # 'bullseye' on the plot
                    i = 0
                    while i < samples:
                        if (speed_vec[0][i] is None) or (dir_vec[0][i] is None):
                            windBin[16][6] += 1
                        else:
                            bin = int((dir_vec[0][i] + 11.25) / 22.5) % 16
                            if speed_vec[0][i] > speedList[0][5]:
                                windBin[bin][6] += 1
                            elif speed_vec[0][i] > speedList[0][4]:
                                windBin[bin][5] += 1
                            elif speed_vec[0][i] > speedList[0][3]:
                                windBin[bin][4] += 1
                            elif speed_vec[0][i] > speedList[0][2]:
                                windBin[bin][3] += 1
                            elif speed_vec[0][i] > speedList[0][1]:
                                windBin[bin][2] += 1
                            elif speed_vec[0][i] > 0:
                                windBin[bin][1] += 1
                            else:
                                windBin[bin][0] += 1
                        i += 1
                    # Add 'None' obs to 0 speed count
                    speedBin[0] += windBin[16][6]
                    # Don't need the 'None' counts so we can delete them
                    del windBin[-1]
                    # Now set total (direction independent) speed counts. Loop
                    # through each petal speed range and increment direction
                    # independent speed ranges as necessary
                    j = 0
                    while j < 7:
                        i = 0
                        while i < 16:
                            speedBin[j] += windBin[i][j]
                            i += 1
                        j += 1
                    # Calc the value to represented by outer ring
                    # (range 0 to 1). Value to rounded up to next multiple of
                    # 0.05 (ie next 5%)
                    self.maxRingValue = (int(max(sum(b) for b in windBin)/(0.05 * samples)) + 1) * 0.05
                    # Find which wind rose arm to use to display ring range
                    # labels - look for one that is relatively clear. Only
                    # consider NE, SE, SW and NW preference in order is
                    # SE, SW, NE and NW
                    # Is SE clear?
                    if sum(windBin[6]) / float(samples) <= 0.3 * self.maxRingValue:
                        labelDir = 6        # If so take it
                    else:                   # If not lets loop through the others
                        for i in [10, 2, 14]:
                            # Is SW, NE or NW clear
                            if sum(windBin[i])/float(samples) <= 0.3 * self.maxRingValue:
                                labelDir = i    # If so let's take it
                                break           # And exit for loop
                        else:                   # If none are free then let's
                                                # take the smallest of the four
                            labelCount = samples + 1  # Set max possible number of
                                                    # readings+1
                            i = 2                   # Start at NE
                            for i in [2, 6, 10, 14]:   # Loop through directions
                                # If this direction has fewer obs than previous
                                # best (least)
                                if sum(windBin[i]) < labelCount:
                                    # Set min count so far to this bin
                                    labelCount = sum(windBin[i])
                                    # Set labelDir to this direction
                                    labelDir = i
                    self.labelDir = labelDir
                    # Set up an Image object to hold our windrose plot
                    self.windRoseImageSetup()
                    # Get a Draw object to draw on
                    self.draw = ImageDraw.Draw(self.image)
                    # Set fonts to be used
                    self.plotFont = get_font_handle(self.font_path,
                                                    self.plot_font_size)
                    self.legendFont = get_font_handle(self.font_path,
                                                      self.legend_font_size)
                    self.labelFont = get_font_handle(self.font_path,
                                                     self.label_font_size)
                    # Estimate space requried for the legend
                    textWidth, textHeight = self.draw.textsize("0 (100%)",
                                                               font=self.legendFont)
                    legendWidth = int(textWidth + 2 * self.legend_bar_width + 1.5 * self.plot_border)
                    # Estimate space required for label (if required)
                    textWidth, textHeight = self.draw.textsize("Wind Rose",
                                                          font=self.labelFont)
                    if self.label:
                        labelHeight = int(textWidth+self.plot_border)
                    else:
                        labelHeight=0
                    # Calculate the diameter of the circular plot space in
                    # pixels. Two diameters are calculated, one based on image
                    # height and one based on image width. We will take the
                    # smallest one. To prevent optical distortion for small
                    # plots diameter will be divisible by 22
                    self.roseMaxDiameter = min(int((self.image_height - 2 * self.plot_border - labelHeight / 2) / 22.0) * 22,
                                               int((self.image_width - (2 * self.plot_border + legendWidth)) / 22.0) * 22)
                    if self.image_width > self.image_height:    # If wider than height
                        textWidth, textHeight = self.draw.textsize("W",
                                                                   font=self.plotFont)
                        # x coord of windrose circle origin(0,0) top left corner
                        self.originX = self.plot_border + textWidth + 2 + self.roseMaxDiameter / 2
                        # y coord of windrose circle origin(0,0) is top left corner
                        self.originY = int(self.image_height / 2)
                    else:
                        # x coord of windrose circle origin(0,0) top left corner
                        self.originX = 2 * self.plot_border + self.roseMaxDiameter / 2
                        # y coord of windrose circle origin(0,0) is top left corner
                        self.originY = 2 * self.plot_border + self.roseMaxDiameter / 2
                    # Setup windrose plot. Plot circles, range rings, range
                    # labels, N-S and E-W centre lines and compass pont labels
                    self.windRosePlotSetup()
                    # Plot wind rose petals
                    # Each petal is constructed from overlapping pieslices
                    # starting from outside (biggest) and working in (smallest)
                    a = 0   #start at 'North' windrose petal
                    while a < len(windBin): #loop through each wind rose arm
                        s = len(speedList[0]) - 1
                        cumRadius = sum(windBin[a])
                        if cumRadius > 0:
                            armRadius = int((10 * self.roseMaxDiameter * sum(windBin[a])) / (11 * 2.0 * self.maxRingValue * samples))
                            while s > 0:
                                # Calc radius of current arm
                                pieRadius = int(round(armRadius * cumRadius/sum(windBin[a]) + self.roseMaxDiameter / 22,0))
                                # Set bound box for pie slice
                                bbox = (self.originX-pieRadius,
                                        self.originY-pieRadius,
                                        self.originX+pieRadius,
                                        self.originY+pieRadius)
                                # Draw pie slice
                                self.draw.pieslice(bbox,
                                                   int(a * 22.5 - 90 - self.petal_width / 2),
                                                   int(a * 22.5 - 90 + self.petal_width / 2),
                                                   fill=speedList[1][s], outline='black')
                                cumRadius -= windBin[a][s]
                                s -= 1  # Move 'in' for next pieslice
                        a += 1  # Next arm
                    # Draw 'bullseye' to represent windSpeed=0 or calm
                    # Produce the label
                    label0 = str(int(round(100.0 * speedBin[0] / sum(speedBin), 0))) + '%'
                    # Work out its size, particularly its width
                    textWidth, textHeight = self.draw.textsize(label0,
                                                               font=self.plotFont)
                    # Size the bound box
                    bbox = (int(self.originX - self.roseMaxDiameter / 22),
                            int(self.originY - self.roseMaxDiameter / 22),
                            int(self.originX + self.roseMaxDiameter / 22),
                            int(self.originY + self.roseMaxDiameter / 22))
                    self.draw.ellipse(bbox,
                                      outline='black',
                                      fill=speedList[1][0])   # Draw the circle
                    self.draw.text((int(self.originX-textWidth / 2), int(self.originY - textHeight / 2)),
                                   label0,
                                   fill=self.plot_font_color,
                                   font=self.plotFont)   # Display the value
                    # Setup the legend. Draw label/title (if set), stacked bar,
                    # bar labels and units
                    self.legendSetup(speedList, speedBin)
                #Save the file.
                self.image.save(img_file)
                ngen += 1
        syslog.syslog(syslog.LOG_INFO, "imageStackedWindRose: Generated %d images for %s in %.2f seconds" % (ngen,
                                                                                                             self.skin_dict['REPORT_NAME'],
                                                                                                             time.time() - t1))