1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View 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

View 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),
}
})
}

View 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
}

View 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"

View file

@ -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

View file

@ -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
}

View file

@ -0,0 +1,4 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["2988507"]
fetch = ["weather", "forecast"]

View file

@ -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

View file

@ -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
}
}]
}

View file

@ -0,0 +1,4 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["111", "222", "333", "444"]
fetch = ["weather"]

View file

@ -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

View file

@ -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
}
}]
}

View file

@ -0,0 +1,4 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["111", "222", "333", "444"]
fetch = ["weather"]

View file

@ -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

View file

@ -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
}
}]
}

View file

@ -0,0 +1,4 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["2988507"]
fetch = ["weather"]

View file

@ -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

View file

@ -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"
}]
}

View file

@ -0,0 +1,4 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["524901", "703448", "2643743"]
fetch = ["weather", "forecast"]

View file

@ -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

View file

@ -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
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -0,0 +1,5 @@
[[inputs.openweathermap]]
app_id = "noappid"
city_id = ["524901", "703448", "2643743"]
fetch = ["weather", "forecast"]
query_style = "individual"

View 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"`
}