Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
102
plugins/inputs/openweathermap/README.md
Normal file
102
plugins/inputs/openweathermap/README.md
Normal file
|
@ -0,0 +1,102 @@
|
|||
# OpenWeatherMap Input Plugin
|
||||
|
||||
Collect current weather and forecast data from OpenWeatherMap.
|
||||
|
||||
To use this plugin you will need an [api key][] (app_id).
|
||||
|
||||
City identifiers can be found in the [city list][]. Alternately you
|
||||
can [search][] by name; the `city_id` can be found as the last digits
|
||||
of the URL: <https://openweathermap.org/city/2643743>. Language
|
||||
identifiers can be found in the [lang list][]. Documentation for
|
||||
condition ID, icon, and main is at [weather conditions][].
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Read current weather and forecasts data from openweathermap.org
|
||||
[[inputs.openweathermap]]
|
||||
## OpenWeatherMap API key.
|
||||
app_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
## City ID's to collect weather data from.
|
||||
city_id = ["5391959"]
|
||||
|
||||
## Language of the description field. Can be one of "ar", "bg",
|
||||
## "ca", "cz", "de", "el", "en", "fa", "fi", "fr", "gl", "hr", "hu",
|
||||
## "it", "ja", "kr", "la", "lt", "mk", "nl", "pl", "pt", "ro", "ru",
|
||||
## "se", "sk", "sl", "es", "tr", "ua", "vi", "zh_cn", "zh_tw"
|
||||
# lang = "en"
|
||||
|
||||
## APIs to fetch; can contain "weather" or "forecast".
|
||||
# fetch = ["weather", "forecast"]
|
||||
|
||||
## OpenWeatherMap base URL
|
||||
# base_url = "https://api.openweathermap.org/"
|
||||
|
||||
## Timeout for HTTP response.
|
||||
# response_timeout = "5s"
|
||||
|
||||
## Preferred unit system for temperature and wind speed. Can be one of
|
||||
## "metric", "imperial", or "standard".
|
||||
# units = "metric"
|
||||
|
||||
## Style to query the current weather; available options
|
||||
## batch -- query multiple cities at once using the "group" endpoint
|
||||
## individual -- query each city individually using the "weather" endpoint
|
||||
## You should use "individual" here as it is documented and provides more
|
||||
## frequent updates. The default is "batch" for backward compatibility.
|
||||
# query_style = "batch"
|
||||
|
||||
## Query interval to fetch data.
|
||||
## By default the global 'interval' setting is used. You should override the
|
||||
## interval here if the global setting is shorter than 10 minutes as
|
||||
## OpenWeatherMap weather data is only updated every 10 minutes.
|
||||
# interval = "10m"
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
- weather
|
||||
- tags:
|
||||
- city_id
|
||||
- forecast
|
||||
- condition_id
|
||||
- condition_main
|
||||
- fields:
|
||||
- cloudiness (int, percent)
|
||||
- humidity (int, percent)
|
||||
- pressure (float, atmospheric pressure hPa)
|
||||
- rain (float, rain volume for the last 1-3 hours (depending on API response) in mm)
|
||||
- snow (float, snow volume for the last 1-3 hours (depending on API response) in mm)
|
||||
- sunrise (int, nanoseconds since unix epoch)
|
||||
- sunset (int, nanoseconds since unix epoch)
|
||||
- temperature (float, degrees)
|
||||
- feels_like (float, degrees)
|
||||
- visibility (int, meters, not available on forecast data)
|
||||
- wind_degrees (float, wind direction in degrees)
|
||||
- wind_speed (float, wind speed in meters/sec or miles/sec)
|
||||
- condition_description (string, localized long description)
|
||||
- condition_icon
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
weather,city=San\ Francisco,city_id=5391959,condition_id=803,condition_main=Clouds,country=US,forecast=114h,host=robot pressure=1027,temperature=10.09,wind_degrees=34,wind_speed=1.24,condition_description="broken clouds",cloudiness=80i,humidity=67i,rain=0,feels_like=8.9,condition_icon="04n" 1645952400000000000
|
||||
weather,city=San\ Francisco,city_id=5391959,condition_id=804,condition_main=Clouds,country=US,forecast=117h,host=robot humidity=65i,rain=0,temperature=10.12,wind_degrees=31,cloudiness=90i,pressure=1026,feels_like=8.88,wind_speed=1.31,condition_description="overcast clouds",condition_icon="04n" 1645963200000000000
|
||||
weather,city=San\ Francisco,city_id=5391959,condition_id=804,condition_main=Clouds,country=US,forecast=120h,host=robot cloudiness=100i,humidity=61i,rain=0,temperature=10.28,wind_speed=1.94,condition_icon="04d",pressure=1027,feels_like=8.96,wind_degrees=16,condition_description="overcast clouds" 1645974000000000000
|
||||
```
|
||||
|
||||
[api key]: https://openweathermap.org/appid
|
||||
[city list]: http://bulk.openweathermap.org/sample/city.list.json.gz
|
||||
[search]: https://openweathermap.org/find
|
||||
[lang list]: https://openweathermap.org/current#multi
|
||||
[weather conditions]: https://openweathermap.org/weather-conditions
|
360
plugins/inputs/openweathermap/openweathermap.go
Normal file
360
plugins/inputs/openweathermap/openweathermap.go
Normal file
|
@ -0,0 +1,360 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package openweathermap
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
// https://openweathermap.org/current#severalid
|
||||
// Limit for the number of city IDs per request.
|
||||
const maxIDsPerBatch int = 20
|
||||
|
||||
type OpenWeatherMap struct {
|
||||
AppID string `toml:"app_id"`
|
||||
CityID []string `toml:"city_id"`
|
||||
Lang string `toml:"lang"`
|
||||
Fetch []string `toml:"fetch"`
|
||||
BaseURL string `toml:"base_url"`
|
||||
ResponseTimeout config.Duration `toml:"response_timeout"`
|
||||
Units string `toml:"units"`
|
||||
QueryStyle string `toml:"query_style"`
|
||||
|
||||
client *http.Client
|
||||
cityIDBatches []string
|
||||
baseParsedURL *url.URL
|
||||
}
|
||||
|
||||
func (*OpenWeatherMap) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) Init() error {
|
||||
// Set the default for the base-URL if not given
|
||||
if n.BaseURL == "" {
|
||||
n.BaseURL = "https://api.openweathermap.org/"
|
||||
}
|
||||
|
||||
// Check the query-style setting
|
||||
switch n.QueryStyle {
|
||||
case "":
|
||||
n.QueryStyle = "batch"
|
||||
case "batch", "individual":
|
||||
// Do nothing, those are valid
|
||||
default:
|
||||
return fmt.Errorf("unknown query-style: %s", n.QueryStyle)
|
||||
}
|
||||
|
||||
// Check the unit setting
|
||||
switch n.Units {
|
||||
case "":
|
||||
n.Units = "metric"
|
||||
case "imperial", "standard", "metric":
|
||||
// Do nothing, those are valid
|
||||
default:
|
||||
return fmt.Errorf("unknown units: %s", n.Units)
|
||||
}
|
||||
|
||||
// Check the language setting
|
||||
switch n.Lang {
|
||||
case "":
|
||||
n.Lang = "en"
|
||||
case "ar", "bg", "ca", "cz", "de", "el", "en", "fa", "fi", "fr", "gl",
|
||||
"hr", "hu", "it", "ja", "kr", "la", "lt", "mk", "nl", "pl",
|
||||
"pt", "ro", "ru", "se", "sk", "sl", "es", "tr", "ua", "vi",
|
||||
"zh_cn", "zh_tw":
|
||||
// Do nothing, those are valid
|
||||
default:
|
||||
return fmt.Errorf("unknown language: %s", n.Lang)
|
||||
}
|
||||
|
||||
// Check the properties to fetch
|
||||
if len(n.Fetch) == 0 {
|
||||
n.Fetch = []string{"weather", "forecast"}
|
||||
}
|
||||
for _, fetch := range n.Fetch {
|
||||
switch fetch {
|
||||
case "forecast", "weather":
|
||||
// Do nothing, those are valid
|
||||
default:
|
||||
return fmt.Errorf("unknown property to fetch: %s", fetch)
|
||||
}
|
||||
}
|
||||
|
||||
// Split the city IDs into batches smaller than the maximum size
|
||||
nBatches := len(n.CityID) / maxIDsPerBatch
|
||||
if len(n.CityID)%maxIDsPerBatch != 0 {
|
||||
nBatches++
|
||||
}
|
||||
batches := make([][]string, nBatches)
|
||||
for i, id := range n.CityID {
|
||||
batch := i / maxIDsPerBatch
|
||||
batches[batch] = append(batches[batch], id)
|
||||
}
|
||||
n.cityIDBatches = make([]string, 0, nBatches)
|
||||
for _, batch := range batches {
|
||||
n.cityIDBatches = append(n.cityIDBatches, strings.Join(batch, ","))
|
||||
}
|
||||
|
||||
// Parse the base-URL used later to construct the property API endpoint
|
||||
u, err := url.Parse(n.BaseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.baseParsedURL = u
|
||||
|
||||
// Create an HTTP client to be used in each collection interval
|
||||
n.client = &http.Client{
|
||||
Transport: &http.Transport{},
|
||||
Timeout: time.Duration(n.ResponseTimeout),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
for _, fetch := range n.Fetch {
|
||||
switch fetch {
|
||||
case "forecast":
|
||||
for _, cityID := range n.CityID {
|
||||
wg.Add(1)
|
||||
go func(city string) {
|
||||
defer wg.Done()
|
||||
acc.AddError(n.gatherForecast(acc, city))
|
||||
}(cityID)
|
||||
}
|
||||
case "weather":
|
||||
switch n.QueryStyle {
|
||||
case "individual":
|
||||
for _, cityID := range n.CityID {
|
||||
wg.Add(1)
|
||||
go func(city string) {
|
||||
defer wg.Done()
|
||||
acc.AddError(n.gatherWeather(acc, city))
|
||||
}(cityID)
|
||||
}
|
||||
case "batch":
|
||||
for _, cityIDs := range n.cityIDBatches {
|
||||
wg.Add(1)
|
||||
go func(cities string) {
|
||||
defer wg.Done()
|
||||
acc.AddError(n.gatherWeatherBatch(acc, cities))
|
||||
}(cityIDs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) gatherWeather(acc telegraf.Accumulator, city string) error {
|
||||
// Query the data and decode the response
|
||||
addr := n.formatURL("/data/2.5/weather", city)
|
||||
buf, err := n.gatherURL(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("querying %q failed: %w", addr, err)
|
||||
}
|
||||
|
||||
var e weatherEntry
|
||||
if err := json.Unmarshal(buf, &e); err != nil {
|
||||
return fmt.Errorf("parsing JSON response failed: %w", err)
|
||||
}
|
||||
|
||||
// Construct the metric
|
||||
tm := time.Unix(e.Dt, 0)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"cloudiness": e.Clouds.All,
|
||||
"humidity": e.Main.Humidity,
|
||||
"pressure": e.Main.Pressure,
|
||||
"rain": e.rain(),
|
||||
"snow": e.snow(),
|
||||
"sunrise": time.Unix(e.Sys.Sunrise, 0).UnixNano(),
|
||||
"sunset": time.Unix(e.Sys.Sunset, 0).UnixNano(),
|
||||
"temperature": e.Main.Temp,
|
||||
"feels_like": e.Main.Feels,
|
||||
"visibility": e.Visibility,
|
||||
"wind_degrees": e.Wind.Deg,
|
||||
"wind_speed": e.Wind.Speed,
|
||||
}
|
||||
tags := map[string]string{
|
||||
"city": e.Name,
|
||||
"city_id": strconv.FormatInt(e.ID, 10),
|
||||
"country": e.Sys.Country,
|
||||
"forecast": "*",
|
||||
}
|
||||
|
||||
if len(e.Weather) > 0 {
|
||||
fields["condition_description"] = e.Weather[0].Description
|
||||
fields["condition_icon"] = e.Weather[0].Icon
|
||||
tags["condition_id"] = strconv.FormatInt(e.Weather[0].ID, 10)
|
||||
tags["condition_main"] = e.Weather[0].Main
|
||||
}
|
||||
|
||||
acc.AddFields("weather", fields, tags, tm)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) gatherWeatherBatch(acc telegraf.Accumulator, cities string) error {
|
||||
// Query the data and decode the response
|
||||
addr := n.formatURL("/data/2.5/group", cities)
|
||||
buf, err := n.gatherURL(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("querying %q failed: %w", addr, err)
|
||||
}
|
||||
|
||||
var status status
|
||||
if err := json.Unmarshal(buf, &status); err != nil {
|
||||
return fmt.Errorf("parsing JSON response failed: %w", err)
|
||||
}
|
||||
|
||||
// Construct the metrics
|
||||
for _, e := range status.List {
|
||||
tm := time.Unix(e.Dt, 0)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"cloudiness": e.Clouds.All,
|
||||
"humidity": e.Main.Humidity,
|
||||
"pressure": e.Main.Pressure,
|
||||
"rain": e.rain(),
|
||||
"snow": e.snow(),
|
||||
"sunrise": time.Unix(e.Sys.Sunrise, 0).UnixNano(),
|
||||
"sunset": time.Unix(e.Sys.Sunset, 0).UnixNano(),
|
||||
"temperature": e.Main.Temp,
|
||||
"feels_like": e.Main.Feels,
|
||||
"visibility": e.Visibility,
|
||||
"wind_degrees": e.Wind.Deg,
|
||||
"wind_speed": e.Wind.Speed,
|
||||
}
|
||||
tags := map[string]string{
|
||||
"city": e.Name,
|
||||
"city_id": strconv.FormatInt(e.ID, 10),
|
||||
"country": e.Sys.Country,
|
||||
"forecast": "*",
|
||||
}
|
||||
|
||||
if len(e.Weather) > 0 {
|
||||
fields["condition_description"] = e.Weather[0].Description
|
||||
fields["condition_icon"] = e.Weather[0].Icon
|
||||
tags["condition_id"] = strconv.FormatInt(e.Weather[0].ID, 10)
|
||||
tags["condition_main"] = e.Weather[0].Main
|
||||
}
|
||||
|
||||
acc.AddFields("weather", fields, tags, tm)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) gatherForecast(acc telegraf.Accumulator, city string) error {
|
||||
// Query the data and decode the response
|
||||
addr := n.formatURL("/data/2.5/forecast", city)
|
||||
buf, err := n.gatherURL(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("querying %q failed: %w", addr, err)
|
||||
}
|
||||
|
||||
var status status
|
||||
if err := json.Unmarshal(buf, &status); err != nil {
|
||||
return fmt.Errorf("parsing JSON response failed: %w", err)
|
||||
}
|
||||
|
||||
// Construct the metric
|
||||
tags := map[string]string{
|
||||
"city_id": strconv.FormatInt(status.City.ID, 10),
|
||||
"forecast": "*",
|
||||
"city": status.City.Name,
|
||||
"country": status.City.Country,
|
||||
}
|
||||
for i, e := range status.List {
|
||||
tm := time.Unix(e.Dt, 0)
|
||||
fields := map[string]interface{}{
|
||||
"cloudiness": e.Clouds.All,
|
||||
"humidity": e.Main.Humidity,
|
||||
"pressure": e.Main.Pressure,
|
||||
"rain": e.rain(),
|
||||
"snow": e.snow(),
|
||||
"temperature": e.Main.Temp,
|
||||
"feels_like": e.Main.Feels,
|
||||
"wind_degrees": e.Wind.Deg,
|
||||
"wind_speed": e.Wind.Speed,
|
||||
}
|
||||
if len(e.Weather) > 0 {
|
||||
fields["condition_description"] = e.Weather[0].Description
|
||||
fields["condition_icon"] = e.Weather[0].Icon
|
||||
tags["condition_id"] = strconv.FormatInt(e.Weather[0].ID, 10)
|
||||
tags["condition_main"] = e.Weather[0].Main
|
||||
}
|
||||
tags["forecast"] = fmt.Sprintf("%dh", (i+1)*3)
|
||||
acc.AddFields("weather", fields, tags, tm)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) formatURL(path, city string) string {
|
||||
v := url.Values{
|
||||
"id": []string{city},
|
||||
"APPID": []string{n.AppID},
|
||||
"lang": []string{n.Lang},
|
||||
"units": []string{n.Units},
|
||||
}
|
||||
|
||||
relative := &url.URL{
|
||||
Path: path,
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
|
||||
return n.baseParsedURL.ResolveReference(relative).String()
|
||||
}
|
||||
|
||||
func (n *OpenWeatherMap) gatherURL(addr string) ([]byte, error) {
|
||||
resp, err := n.client.Get(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making HTTP request to %q: %w", addr, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%s returned HTTP status %s", addr, resp.Status)
|
||||
}
|
||||
|
||||
mediaType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mediaType != "application/json" {
|
||||
return nil, fmt.Errorf("%s returned unexpected content type %s", addr, mediaType)
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("openweathermap", func() telegraf.Input {
|
||||
return &OpenWeatherMap{
|
||||
ResponseTimeout: config.Duration(5 * time.Second),
|
||||
}
|
||||
})
|
||||
}
|
180
plugins/inputs/openweathermap/openweathermap_test.go
Normal file
180
plugins/inputs/openweathermap/openweathermap_test.go
Normal file
|
@ -0,0 +1,180 @@
|
|||
package openweathermap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestFormatURL(t *testing.T) {
|
||||
n := &OpenWeatherMap{
|
||||
AppID: "appid",
|
||||
Units: "metric",
|
||||
Lang: "de",
|
||||
BaseURL: "http://foo.com",
|
||||
}
|
||||
require.NoError(t, n.Init())
|
||||
|
||||
require.Equal(t,
|
||||
"http://foo.com/data/2.5/forecast?APPID=appid&id=12345&lang=de&units=metric",
|
||||
n.formatURL("/data/2.5/forecast", "12345"))
|
||||
}
|
||||
|
||||
func TestDefaultUnits(t *testing.T) {
|
||||
n := &OpenWeatherMap{}
|
||||
require.NoError(t, n.Init())
|
||||
|
||||
require.Equal(t, "metric", n.Units)
|
||||
}
|
||||
|
||||
func TestDefaultLang(t *testing.T) {
|
||||
n := &OpenWeatherMap{}
|
||||
require.NoError(t, n.Init())
|
||||
|
||||
require.Equal(t, "en", n.Lang)
|
||||
}
|
||||
|
||||
func TestCases(t *testing.T) {
|
||||
// Get all directories in testdata
|
||||
folders, err := os.ReadDir("testcases")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Register the plugin
|
||||
inputs.Add("openweathermap", func() telegraf.Input {
|
||||
return &OpenWeatherMap{
|
||||
ResponseTimeout: config.Duration(5 * time.Second),
|
||||
}
|
||||
})
|
||||
|
||||
// Prepare the influx parser for expectations
|
||||
parser := &influx.Parser{}
|
||||
require.NoError(t, parser.Init())
|
||||
|
||||
for _, f := range folders {
|
||||
// Only handle folders
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
testcasePath := filepath.Join("testcases", f.Name())
|
||||
configFilename := filepath.Join(testcasePath, "telegraf.conf")
|
||||
expectedFilename := filepath.Join(testcasePath, "expected.out")
|
||||
expectedErrorFilename := filepath.Join(testcasePath, "expected.err")
|
||||
|
||||
t.Run(f.Name(), func(t *testing.T) {
|
||||
// Read the input data
|
||||
input, err := readInputData(testcasePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read the expected output if any
|
||||
var expected []telegraf.Metric
|
||||
if _, err := os.Stat(expectedFilename); err == nil {
|
||||
var err error
|
||||
expected, err = testutil.ParseMetricsFromFile(expectedFilename, parser)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Read the expected output if any
|
||||
var expectedErrors []string
|
||||
if _, err := os.Stat(expectedErrorFilename); err == nil {
|
||||
var err error
|
||||
expectedErrors, err = testutil.ParseLinesFromFile(expectedErrorFilename)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, expectedErrors)
|
||||
}
|
||||
|
||||
// Start the test-server
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Lookup the response
|
||||
key := strings.TrimPrefix(r.URL.Path, "/data/2.5/")
|
||||
if resp, found := input[key]; found {
|
||||
w.Header()["Content-Type"] = []string{"application/json"}
|
||||
if _, err := w.Write(resp); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Try to append the key and find the response
|
||||
ids := strings.Split(r.URL.Query().Get("id"), ",")
|
||||
if len(ids) > 1 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(ids) == 1 {
|
||||
key += "_" + ids[0]
|
||||
if resp, found := input[key]; found {
|
||||
w.Header()["Content-Type"] = []string{"application/json"}
|
||||
if _, err := w.Write(resp); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Configure the plugin
|
||||
cfg := config.NewConfig()
|
||||
require.NoError(t, cfg.LoadConfig(configFilename))
|
||||
require.Len(t, cfg.Inputs, 1)
|
||||
|
||||
// Fake the reading
|
||||
plugin := cfg.Inputs[0].Input.(*OpenWeatherMap)
|
||||
plugin.BaseURL = server.URL
|
||||
require.NoError(t, plugin.Init())
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, plugin.Gather(&acc))
|
||||
if len(acc.Errors) > 0 {
|
||||
var actualErrorMsgs []string
|
||||
for _, err := range acc.Errors {
|
||||
actualErrorMsgs = append(actualErrorMsgs, err.Error())
|
||||
}
|
||||
require.ElementsMatch(t, actualErrorMsgs, expectedErrors)
|
||||
}
|
||||
|
||||
// Check the metric nevertheless as we might get some metrics despite errors.
|
||||
actual := acc.GetTelegrafMetrics()
|
||||
testutil.RequireMetricsEqual(t, expected, actual, testutil.SortMetrics())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func readInputData(path string) (map[string][]byte, error) {
|
||||
pattern := filepath.Join(path, "response_*.json")
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Iterate over the response_*.json files and read the into the data map
|
||||
data := make(map[string][]byte, len(matches))
|
||||
for _, filename := range matches {
|
||||
resp, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response %q failed: %w", filename, err)
|
||||
}
|
||||
key := filepath.Base(filename)
|
||||
key = strings.TrimPrefix(key, "response_")
|
||||
key = strings.TrimSuffix(key, ".json")
|
||||
data[key] = resp
|
||||
}
|
||||
return data, nil
|
||||
}
|
39
plugins/inputs/openweathermap/sample.conf
Normal file
39
plugins/inputs/openweathermap/sample.conf
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Read current weather and forecasts data from openweathermap.org
|
||||
[[inputs.openweathermap]]
|
||||
## OpenWeatherMap API key.
|
||||
app_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
## City ID's to collect weather data from.
|
||||
city_id = ["5391959"]
|
||||
|
||||
## Language of the description field. Can be one of "ar", "bg",
|
||||
## "ca", "cz", "de", "el", "en", "fa", "fi", "fr", "gl", "hr", "hu",
|
||||
## "it", "ja", "kr", "la", "lt", "mk", "nl", "pl", "pt", "ro", "ru",
|
||||
## "se", "sk", "sl", "es", "tr", "ua", "vi", "zh_cn", "zh_tw"
|
||||
# lang = "en"
|
||||
|
||||
## APIs to fetch; can contain "weather" or "forecast".
|
||||
# fetch = ["weather", "forecast"]
|
||||
|
||||
## OpenWeatherMap base URL
|
||||
# base_url = "https://api.openweathermap.org/"
|
||||
|
||||
## Timeout for HTTP response.
|
||||
# response_timeout = "5s"
|
||||
|
||||
## Preferred unit system for temperature and wind speed. Can be one of
|
||||
## "metric", "imperial", or "standard".
|
||||
# units = "metric"
|
||||
|
||||
## Style to query the current weather; available options
|
||||
## batch -- query multiple cities at once using the "group" endpoint
|
||||
## individual -- query each city individually using the "weather" endpoint
|
||||
## You should use "individual" here as it is documented and provides more
|
||||
## frequent updates. The default is "batch" for backward compatibility.
|
||||
# query_style = "batch"
|
||||
|
||||
## Query interval to fetch data.
|
||||
## By default the global 'interval' setting is used. You should override the
|
||||
## interval here if the global setting is shorter than 10 minutes as
|
||||
## OpenWeatherMap weather data is only updated every 10 minutes.
|
||||
# interval = "10m"
|
|
@ -0,0 +1,2 @@
|
|||
weather,city=Paris,city_id=2988507,condition_id=500,condition_main=Rain,country=FR,forecast=3h cloudiness=88i,condition_description="light rain",condition_icon="10n",feels_like=5.71,humidity=91i,pressure=1018.65,rain=0.035,snow=0,temperature=6.71,wind_degrees=228.501,wind_speed=3.76 1543622400000000000
|
||||
weather,city=Paris,city_id=2988507,condition_id=500,condition_main=Rain,country=FR,forecast=6h cloudiness=92i,condition_description="light rain",condition_icon="10n",feels_like=5.38,humidity=98i,pressure=1032.18,rain=0.049999999999997,snow=0,temperature=6.38,wind_degrees=335.005,wind_speed=2.66 1544043600000000000
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"city": {
|
||||
"coord": {
|
||||
"lat": 48.8534,
|
||||
"lon": 2.3488
|
||||
},
|
||||
"country": "FR",
|
||||
"id": 2988507,
|
||||
"name": "Paris"
|
||||
},
|
||||
"cnt": 40,
|
||||
"cod": "200",
|
||||
"list": [
|
||||
{
|
||||
"clouds": {
|
||||
"all": 88
|
||||
},
|
||||
"dt": 1543622400,
|
||||
"dt_txt": "2018-12-01 00:00:00",
|
||||
"main": {
|
||||
"grnd_level": 1018.65,
|
||||
"humidity": 91,
|
||||
"pressure": 1018.65,
|
||||
"sea_level": 1030.99,
|
||||
"temp": 6.71,
|
||||
"feels_like": 5.71,
|
||||
"temp_kf": -2.14
|
||||
},
|
||||
"rain": {
|
||||
"3h": 0.035
|
||||
},
|
||||
"sys": {
|
||||
"pod": "n"
|
||||
},
|
||||
"weather": [
|
||||
{
|
||||
"description": "light rain",
|
||||
"icon": "10n",
|
||||
"id": 500,
|
||||
"main": "Rain"
|
||||
}
|
||||
],
|
||||
"wind": {
|
||||
"deg": 228.501,
|
||||
"speed": 3.76
|
||||
}
|
||||
},
|
||||
{
|
||||
"clouds": {
|
||||
"all": 92
|
||||
},
|
||||
"dt": 1544043600,
|
||||
"dt_txt": "2018-12-05 21:00:00",
|
||||
"main": {
|
||||
"grnd_level": 1032.18,
|
||||
"humidity": 98,
|
||||
"pressure": 1032.18,
|
||||
"sea_level": 1044.78,
|
||||
"temp": 6.38,
|
||||
"feels_like": 5.38,
|
||||
"temp_kf": 0
|
||||
},
|
||||
"rain": {
|
||||
"3h": 0.049999999999997
|
||||
},
|
||||
"sys": {
|
||||
"pod": "n"
|
||||
},
|
||||
"weather": [
|
||||
{
|
||||
"description": "light rain",
|
||||
"icon": "10n",
|
||||
"id": 500,
|
||||
"main": "Rain"
|
||||
}
|
||||
],
|
||||
"wind": {
|
||||
"deg": 335.005,
|
||||
"speed": 2.66
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": 0.0025
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,4 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["2988507"]
|
||||
fetch = ["weather", "forecast"]
|
|
@ -0,0 +1,4 @@
|
|||
weather,city=Paris,city_id=111,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=1,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=222,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=3,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=333,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=1.3,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=444,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"cnt": 2,
|
||||
"list": [{
|
||||
"dt": 1544194800,
|
||||
"id": 111,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"rain": {
|
||||
"1h": 1.000
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 222,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"rain": {
|
||||
"3h": 3.000
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 333,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"rain": {
|
||||
"1h": 1.300,
|
||||
"3h": 999
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 444,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["111", "222", "333", "444"]
|
||||
fetch = ["weather"]
|
|
@ -0,0 +1,4 @@
|
|||
weather,city=Paris,city_id=111,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=1,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=222,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=3,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=333,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=1.3,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
||||
weather,city=Paris,city_id=444,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"cnt": 2,
|
||||
"list": [{
|
||||
"dt": 1544194800,
|
||||
"id": 111,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"snow": {
|
||||
"1h": 1.000
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 222,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"snow": {
|
||||
"3h": 3.000
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 333,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"snow": {
|
||||
"1h": 1.300,
|
||||
"3h": 999
|
||||
},
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"dt": 1544194800,
|
||||
"id": 444,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["111", "222", "333", "444"]
|
||||
fetch = ["weather"]
|
|
@ -0,0 +1 @@
|
|||
weather,city=Paris,city_id=2988507,condition_id=300,condition_main=Drizzle,country=FR,forecast=* cloudiness=0i,condition_description="light intensity drizzle",condition_icon="09d",feels_like=8.25,humidity=87i,pressure=1007,rain=0,snow=0,sunrise=1544167818000000000i,sunset=1544198047000000000i,temperature=9.25,visibility=10000i,wind_degrees=290,wind_speed=8.7 1544194800000000000
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"cnt": 1,
|
||||
"list": [{
|
||||
"clouds": {
|
||||
"all": 0
|
||||
},
|
||||
"coord": {
|
||||
"lat": 48.85,
|
||||
"lon": 2.35
|
||||
},
|
||||
"dt": 1544194800,
|
||||
"id": 2988507,
|
||||
"main": {
|
||||
"humidity": 87,
|
||||
"pressure": 1007,
|
||||
"temp": 9.25,
|
||||
"feels_like": 8.25
|
||||
},
|
||||
"name": "Paris",
|
||||
"sys": {
|
||||
"country": "FR",
|
||||
"id": 6550,
|
||||
"message": 0.002,
|
||||
"sunrise": 1544167818,
|
||||
"sunset": 1544198047,
|
||||
"type": 1
|
||||
},
|
||||
"visibility": 10000,
|
||||
"weather": [
|
||||
{
|
||||
"description": "light intensity drizzle",
|
||||
"icon": "09d",
|
||||
"id": 300,
|
||||
"main": "Drizzle"
|
||||
}
|
||||
],
|
||||
"wind": {
|
||||
"deg": 290,
|
||||
"speed": 8.7
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["2988507"]
|
||||
fetch = ["weather"]
|
|
@ -0,0 +1,3 @@
|
|||
weather,city=Moscow,city_id=524901,condition_id=802,condition_main=Clouds,country=RU,forecast=* cloudiness=40i,condition_description="scattered clouds",condition_icon="03d",feels_like=8.57,humidity=46i,pressure=1014,rain=0,snow=0,sunrise=1556416455000000000i,sunset=1556470779000000000i,temperature=9.57,visibility=10000i,wind_degrees=60,wind_speed=5 1556444155000000000
|
||||
weather,city=Kiev,city_id=703448,condition_id=520,condition_main=Rain,country=UA,forecast=* cloudiness=0i,condition_description="light intensity shower rain",condition_icon="09d",feels_like=18.29,humidity=63i,pressure=1009,rain=0,snow=0,sunrise=1556419155000000000i,sunset=1556471486000000000i,temperature=19.29,visibility=10000i,wind_degrees=0,wind_speed=1 1556444155000000000
|
||||
weather,city=London,city_id=2643743,condition_id=803,condition_main=Clouds,country=GB,forecast=* cloudiness=75i,condition_description="broken clouds",condition_icon="04d",feels_like=9.62,humidity=66i,pressure=1019,rain=0.072,snow=0,sunrise=1556426319000000000i,sunset=1556479032000000000i,temperature=10.62,visibility=10000i,wind_degrees=290,wind_speed=6.2 1556444155000000000
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"cnt": 3,
|
||||
"list": [{
|
||||
"coord": {
|
||||
"lon": 37.62,
|
||||
"lat": 55.75
|
||||
},
|
||||
"sys": {
|
||||
"type": 1,
|
||||
"id": 9029,
|
||||
"message": 0.0061,
|
||||
"country": "RU",
|
||||
"sunrise": 1556416455,
|
||||
"sunset": 1556470779
|
||||
},
|
||||
"weather": [{
|
||||
"id": 802,
|
||||
"main": "Clouds",
|
||||
"description": "scattered clouds",
|
||||
"icon": "03d"
|
||||
}],
|
||||
"main": {
|
||||
"temp": 9.57,
|
||||
"feels_like": 8.57,
|
||||
"pressure": 1014,
|
||||
"humidity": 46
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 5,
|
||||
"deg": 60
|
||||
},
|
||||
"clouds": {
|
||||
"all": 40
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"id": 524901,
|
||||
"name": "Moscow"
|
||||
}, {
|
||||
"coord": {
|
||||
"lon": 30.52,
|
||||
"lat": 50.43
|
||||
},
|
||||
"sys": {
|
||||
"type": 1,
|
||||
"id": 8903,
|
||||
"message": 0.0076,
|
||||
"country": "UA",
|
||||
"sunrise": 1556419155,
|
||||
"sunset": 1556471486
|
||||
},
|
||||
"weather": [{
|
||||
"id": 520,
|
||||
"main": "Rain",
|
||||
"description": "light intensity shower rain",
|
||||
"icon": "09d"
|
||||
}],
|
||||
"main": {
|
||||
"temp": 19.29,
|
||||
"feels_like": 18.29,
|
||||
"pressure": 1009,
|
||||
"humidity": 63
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 1
|
||||
},
|
||||
"clouds": {
|
||||
"all": 0
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"id": 703448,
|
||||
"name": "Kiev"
|
||||
}, {
|
||||
"coord": {
|
||||
"lon": -0.13,
|
||||
"lat": 51.51
|
||||
},
|
||||
"sys": {
|
||||
"type": 1,
|
||||
"id": 1414,
|
||||
"message": 0.0088,
|
||||
"country": "GB",
|
||||
"sunrise": 1556426319,
|
||||
"sunset": 1556479032
|
||||
},
|
||||
"weather": [{
|
||||
"id": 803,
|
||||
"main": "Clouds",
|
||||
"description": "broken clouds",
|
||||
"icon": "04d"
|
||||
}],
|
||||
"main": {
|
||||
"temp": 10.62,
|
||||
"feels_like": 9.62,
|
||||
"pressure": 1019,
|
||||
"humidity": 66
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 6.2,
|
||||
"deg": 290
|
||||
},
|
||||
"rain": {
|
||||
"3h": 0.072
|
||||
},
|
||||
"clouds": {
|
||||
"all": 75
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"id": 2643743,
|
||||
"name": "London"
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["524901", "703448", "2643743"]
|
||||
fetch = ["weather", "forecast"]
|
|
@ -0,0 +1,3 @@
|
|||
weather,city=Moscow,city_id=524901,condition_id=802,condition_main=Clouds,country=RU,forecast=* cloudiness=40i,condition_description="scattered clouds",condition_icon="03d",feels_like=8.57,humidity=46i,pressure=1014,rain=0,snow=0,sunrise=1556416455000000000i,sunset=1556470779000000000i,temperature=9.57,visibility=10000i,wind_degrees=60,wind_speed=5 1556444155000000000
|
||||
weather,city=Kiev,city_id=703448,condition_id=520,condition_main=Rain,country=UA,forecast=* cloudiness=0i,condition_description="light intensity shower rain",condition_icon="09d",feels_like=18.29,humidity=63i,pressure=1009,rain=0,snow=0,sunrise=1556419155000000000i,sunset=1556471486000000000i,temperature=19.29,visibility=10000i,wind_degrees=0,wind_speed=1 1556444155000000000
|
||||
weather,city=London,city_id=2643743,condition_id=804,condition_main=Clouds,country=GB,forecast=* cloudiness=100i,condition_description="overcast clouds",condition_icon="04n",feels_like=7.91,humidity=90i,pressure=997,rain=0,snow=0,sunrise=1698648577000000000i,sunset=1698683914000000000i,temperature=8.94,visibility=10000i,wind_degrees=250,wind_speed=2.06 1556444155000000000
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"coord": {
|
||||
"lon": -0.1257,
|
||||
"lat": 51.5085
|
||||
},
|
||||
"weather": [
|
||||
{
|
||||
"id": 804,
|
||||
"main": "Clouds",
|
||||
"description": "overcast clouds",
|
||||
"icon": "04n"
|
||||
}
|
||||
],
|
||||
"base": "stations",
|
||||
"main": {
|
||||
"temp": 8.94,
|
||||
"feels_like": 7.91,
|
||||
"temp_min": 7.38,
|
||||
"temp_max": 9.98,
|
||||
"pressure": 997,
|
||||
"humidity": 90
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 2.06,
|
||||
"deg": 250
|
||||
},
|
||||
"clouds": {
|
||||
"all": 100
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"sys": {
|
||||
"type": 2,
|
||||
"id": 2006068,
|
||||
"country": "GB",
|
||||
"sunrise": 1698648577,
|
||||
"sunset": 1698683914
|
||||
},
|
||||
"timezone": 0,
|
||||
"id": 2643743,
|
||||
"name": "London",
|
||||
"cod": 200
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"coord": {
|
||||
"lon": 37.62,
|
||||
"lat": 55.75
|
||||
},
|
||||
"sys": {
|
||||
"type": 1,
|
||||
"id": 9029,
|
||||
"message": 0.0061,
|
||||
"country": "RU",
|
||||
"sunrise": 1556416455,
|
||||
"sunset": 1556470779
|
||||
},
|
||||
"weather": [
|
||||
{
|
||||
"id": 802,
|
||||
"main": "Clouds",
|
||||
"description": "scattered clouds",
|
||||
"icon": "03d"
|
||||
}
|
||||
],
|
||||
"main": {
|
||||
"temp": 9.57,
|
||||
"feels_like": 8.57,
|
||||
"pressure": 1014,
|
||||
"humidity": 46
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 5,
|
||||
"deg": 60
|
||||
},
|
||||
"clouds": {
|
||||
"all": 40
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"id": 524901,
|
||||
"name": "Moscow"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"coord": {
|
||||
"lon": 30.52,
|
||||
"lat": 50.43
|
||||
},
|
||||
"sys": {
|
||||
"type": 1,
|
||||
"id": 8903,
|
||||
"message": 0.0076,
|
||||
"country": "UA",
|
||||
"sunrise": 1556419155,
|
||||
"sunset": 1556471486
|
||||
},
|
||||
"weather": [
|
||||
{
|
||||
"id": 520,
|
||||
"main": "Rain",
|
||||
"description": "light intensity shower rain",
|
||||
"icon": "09d"
|
||||
}
|
||||
],
|
||||
"main": {
|
||||
"temp": 19.29,
|
||||
"feels_like": 18.29,
|
||||
"pressure": 1009,
|
||||
"humidity": 63
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 1
|
||||
},
|
||||
"clouds": {
|
||||
"all": 0
|
||||
},
|
||||
"dt": 1556444155,
|
||||
"id": 703448,
|
||||
"name": "Kiev"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[[inputs.openweathermap]]
|
||||
app_id = "noappid"
|
||||
city_id = ["524901", "703448", "2643743"]
|
||||
fetch = ["weather", "forecast"]
|
||||
query_style = "individual"
|
71
plugins/inputs/openweathermap/types.go
Normal file
71
plugins/inputs/openweathermap/types.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package openweathermap
|
||||
|
||||
type weatherEntry struct {
|
||||
Dt int64 `json:"dt"`
|
||||
Clouds struct {
|
||||
All int64 `json:"all"`
|
||||
} `json:"clouds"`
|
||||
Main struct {
|
||||
Humidity int64 `json:"humidity"`
|
||||
Pressure float64 `json:"pressure"`
|
||||
Temp float64 `json:"temp"`
|
||||
Feels float64 `json:"feels_like"`
|
||||
} `json:"main"`
|
||||
Rain struct {
|
||||
Rain1 float64 `json:"1h"`
|
||||
Rain3 float64 `json:"3h"`
|
||||
} `json:"rain"`
|
||||
Snow struct {
|
||||
Snow1 float64 `json:"1h"`
|
||||
Snow3 float64 `json:"3h"`
|
||||
} `json:"snow"`
|
||||
Sys struct {
|
||||
Country string `json:"country"`
|
||||
Sunrise int64 `json:"sunrise"`
|
||||
Sunset int64 `json:"sunset"`
|
||||
} `json:"sys"`
|
||||
Wind struct {
|
||||
Deg float64 `json:"deg"`
|
||||
Speed float64 `json:"speed"`
|
||||
} `json:"wind"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Coord struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
} `json:"coord"`
|
||||
Visibility int64 `json:"visibility"`
|
||||
Weather []struct {
|
||||
ID int64 `json:"id"`
|
||||
Main string `json:"main"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"weather"`
|
||||
}
|
||||
|
||||
func (e weatherEntry) snow() float64 {
|
||||
if e.Snow.Snow1 > 0 {
|
||||
return e.Snow.Snow1
|
||||
}
|
||||
return e.Snow.Snow3
|
||||
}
|
||||
|
||||
func (e weatherEntry) rain() float64 {
|
||||
if e.Rain.Rain1 > 0 {
|
||||
return e.Rain.Rain1
|
||||
}
|
||||
return e.Rain.Rain3
|
||||
}
|
||||
|
||||
type status struct {
|
||||
City struct {
|
||||
Coord struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
} `json:"coord"`
|
||||
Country string `json:"country"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"city"`
|
||||
List []weatherEntry `json:"list"`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue