We've already created a data source plugin that creates Zenoss events for weather alerts. Now we want to use the Weather Underground Conditions API to monitor current weather conditions for each location. The purpose of this is to illustrate that these Python data source plugins can also be used to collect datapoints.
Follow these steps to create the Conditions data source plugin:
Add the following contents to the end of
$ZP_DIR/dsplugins.py
.class Conditions(PythonDataSourcePlugin): """Weather Underground conditions data source plugin.""" @classmethod def config_key(cls, datasource, context): return ( context.device().id, datasource.getCycleTime(context), context.id, 'wunderground-conditions', ) @classmethod def params(cls, datasource, context): return { 'api_key': context.zWundergroundAPIKey, 'api_link': context.api_link, 'location_name': context.title, } @inlineCallbacks def collect(self, config): data = self.new_data() for datasource in config.datasources: try: response = yield getPage( 'http://api.wunderground.com/api/{api_key}/conditions{api_link}.json' .format( api_key=datasource.params['api_key'], api_link=datasource.params['api_link'])) response = json.loads(response) except Exception: LOG.exception( "%s: failed to get conditions data for %s", config.id, datasource.location_name) continue current_observation = response['current_observation'] for datapoint_id in (x.id for x in datasource.points): if datapoint_id not in current_observation: continue try: value = current_observation[datapoint_id] if isinstance(value, basestring): value = value.strip(' %') value = float(value) except (TypeError, ValueError): # Sometimes values are NA or not available. continue dpname = '_'.join((datasource.datasource, datapoint_id)) data['values'][datasource.component][dpname] = (value, 'N') returnValue(data)
Most of the Conditions plugin is almost identical to the Alerts plugin so I won't repeat what can be read back in that section. The main difference starts at the
current_observation = response['current_observation']
line of the collect method.It grabs the current_observation data from the response then iterates over every datapoint configured on the datasource. This is a nice approach because it allows for some user-flexibility in what datapoints are captured from the Conditions API. If the API made temp_c and temp_f available, we could choose to collect temp_c just by adding a datapoint by that name.
The following line is the most important in terms of explaining how to have your plugin return datapoint values.
data['values'][datasource.component][dpname] = (value, 'N')
We just stick
(value, 'N')
into the component's datapoint dictionary. The'N'
is the timestamp at which the value occurred. If you know the time it should be specified as the integer UNIX timestamp. Use'N'
if you don't know. This will use the current time.Restart Zenoss.
After adding a new datasource plugin you must restart Zenoss. While developing it's enough to just restart zenhub with the following command.
serviced service restart zenhub
That's it. The datasource plugin has been created. Now we just need to do some Zenoss configuration to allow us to use it.
To use this new plugin we'll add a new datasource and corresponding graphs to the existing Location monitoring template defined in zenpack.yaml.
Follow these steps to update the monitoring template:
Update $ZP_DIR/zenpack.yaml to add the conditions entry within the existing datasources section.
device_classes: /WeatherUnderground: templates: Location: description: Location weather monitoring using the Weather Underground API. targetPythonClass: ZenPacks.training.WeatherUnderground.WundergroundLocation datasources: conditions: type: Python plugin_classname: ZenPacks.training.WeatherUnderground.dsplugins.Conditions cycletime: "600" datapoints: temp_c: GAUGE feelslike_c: GAUGE heat_index_c: GAUGE windchill_c: GAUGE dewpoint_c: GAUGE relative_humidity: GAUGE pressure_mb: GAUGE precip_1hr_metric: GAUGE UV: GAUGE wind_kph: GAUGE wind_gust_kph: GAUGE visibility_km: GAUGE graphs: Temperatures: units: "degrees C." graphpoints: Temperature: dpName: conditions_temp_c format: "%7.2lf" Feels Like: dpName: conditions_feelslike_c format: "%7.2lf" Heat Index: dpName: conditions_heat_index_c format: "%7.2lf" Wind Chill: dpName: conditions_windchilltemp_c format: "%7.2lf" Dewpoint: dpName: conditions_dewpoint_c format: "%7.2lf" Relative Humidity: units: percent miny: 0 maxy: 100 graphpoints: Relative Humidity: dpName: conditions_relative_humidity format: "%7.2lf%%" Pressure: units: millibars miny: 0 graphpoints: Pressure: dpName: conditions_pressure_mb format: "%7.0lf" Precipitation: units: centimeters miny: 0 graphpoints: 1 Hour: dpName: conditions_precip_1hr_metric format: "%7.2lf" UV Index: units: UV index miny: 0 maxy: 12 graphpoints: UV Index: dpName: conditions_UV format: "%7.0lf" Wind Speed: units: kph miny: 0 graphpoints: Sustained: dpName: conditions_wind_kph format: "%7.2lf" Gust: dpName: conditions_wind_gust_kph format: "%7.2lf" Visibility: units: kilometers miny: 0 graphpoints: Visibility: dpName: conditions_visibility_km format: "%7.2lf"
You can refer to :ref:`monitoring-templates` for more information on creating monitoring templates in YAML.
Reinstall the ZenPack to update the monitoring template.
zenpack --link --install $ZP_TOP_DIR
Navigate to Advanced -> Monitoring Templates in the web interface to verify that the Location monitoring template has been updated with the conditions datasource and corresponding graphs.
Follow these steps to test weather condition monitoring:
Run the following command to collect from wunderground.com.
zenpython run -v10 --device=wunderground.com
There will be a lot of output from this command, but we're mainly looking for at least one datapoint being written. If one works, it's likely that they all work. Look for a line similar to the following:
DEBUG zen.MetricWriter: publishing metric wunderground.com/conditions_temp_c 14.1 1452024379