Add Fireboard Input Plugin (#6052)
This commit is contained in:
parent
109d1e1e15
commit
e098758d78
|
@ -42,6 +42,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/file"
|
_ "github.com/influxdata/telegraf/plugins/inputs/file"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/filecount"
|
_ "github.com/influxdata/telegraf/plugins/inputs/filecount"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/filestat"
|
_ "github.com/influxdata/telegraf/plugins/inputs/filestat"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/inputs/fireboard"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/fluentd"
|
_ "github.com/influxdata/telegraf/plugins/inputs/fluentd"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/github"
|
_ "github.com/influxdata/telegraf/plugins/inputs/github"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/graylog"
|
_ "github.com/influxdata/telegraf/plugins/inputs/graylog"
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Fireboard Input Plugin
|
||||||
|
|
||||||
|
The fireboard plugin gathers the real time temperature data from fireboard
|
||||||
|
thermometers. In order to use this input plugin, you'll need to sign up
|
||||||
|
to use their REST API, you can find more information on their website
|
||||||
|
here [https://docs.fireboard.io/reference/restapi.html]
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
This section contains the default TOML to configure the plugin. You can
|
||||||
|
generate it using `telegraf --usage <plugin-name>`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[inputs.fireboard]]
|
||||||
|
## Specify auth token for your account
|
||||||
|
auth_token = "invalidAuthToken"
|
||||||
|
## You can override the fireboard server URL if necessary
|
||||||
|
# url = https://fireboard.io/api/v1/devices.json
|
||||||
|
## You can set a different http_timeout if you need to
|
||||||
|
# http_timeout = 4
|
||||||
|
```
|
||||||
|
|
||||||
|
#### auth_token
|
||||||
|
|
||||||
|
In lieu of requiring a username and password, this plugin requires the
|
||||||
|
authToken that you can generate using the Fireboard REST API as described
|
||||||
|
in their docs [https://docs.fireboard.io/reference/restapi.html#Authentication]
|
||||||
|
|
||||||
|
#### url
|
||||||
|
|
||||||
|
While there should be no reason to override the URL, the option is available
|
||||||
|
in case Fireboard changes their site, etc.
|
||||||
|
|
||||||
|
#### http_timeout
|
||||||
|
|
||||||
|
If you need to increase the HTTP timeout, you can do so here. You can set this
|
||||||
|
value in seconds. The default value is four (4) seconds.
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
|
||||||
|
The Fireboard REST API docs have good examples of the data that is available,
|
||||||
|
currently this input only returns the real time temperatures. Temperature
|
||||||
|
values are included if they are less than a minute old.
|
||||||
|
|
||||||
|
- fireboard
|
||||||
|
- tags:
|
||||||
|
- channel
|
||||||
|
- scale (Celcius; Farenheit)
|
||||||
|
- title (name of the Fireboard)
|
||||||
|
- uuid (UUID of the Fireboard)
|
||||||
|
- fields:
|
||||||
|
- temperature (float, unit)
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
This section shows example output in Line Protocol format. You can often use
|
||||||
|
`telegraf --input-filter <plugin-name> --test` or use the `file` output to get
|
||||||
|
this information.
|
||||||
|
|
||||||
|
```
|
||||||
|
fireboard,channel=2,host=patas-mbp,scale=Farenheit,title=telegraf-FireBoard,uuid=b55e766c-b308-49b5-93a4-df89fe31efd0 temperature=78.2 1561690040000000000
|
||||||
|
```
|
|
@ -0,0 +1,157 @@
|
||||||
|
package fireboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/internal"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fireboard gathers statistics from the fireboard.io servers
|
||||||
|
type Fireboard struct {
|
||||||
|
AuthToken string `toml:"auth_token"`
|
||||||
|
URL string `toml:"url"`
|
||||||
|
HTTPTimeout internal.Duration `toml:"http_timeout"`
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFireboard return a new instance of Fireboard with a default http client
|
||||||
|
func NewFireboard() *Fireboard {
|
||||||
|
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
Timeout: time.Duration(4 * time.Second),
|
||||||
|
}
|
||||||
|
return &Fireboard{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTT fireboardStats represents the data that is received from Fireboard
|
||||||
|
type RTT struct {
|
||||||
|
Temp float64 `json:"temp"`
|
||||||
|
Channel int64 `json:"channel"`
|
||||||
|
Degreetype int `json:"degreetype"`
|
||||||
|
Created string `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type fireboardStats struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Latesttemps []RTT `json:"latest_temps"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A sample configuration to only gather stats from localhost, default port.
|
||||||
|
const sampleConfig = `
|
||||||
|
## Specify auth token for your account
|
||||||
|
auth_token = "invalidAuthToken"
|
||||||
|
## You can override the fireboard server URL if necessary
|
||||||
|
# url = https://fireboard.io/api/v1/devices.json
|
||||||
|
## You can set a different http_timeout if you need to
|
||||||
|
## You should set a string using an number and time indicator
|
||||||
|
## for example "12s" for 12 seconds.
|
||||||
|
# http_timeout = "4s"
|
||||||
|
`
|
||||||
|
|
||||||
|
// SampleConfig Returns a sample configuration for the plugin
|
||||||
|
func (r *Fireboard) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description Returns a description of the plugin
|
||||||
|
func (r *Fireboard) Description() string {
|
||||||
|
return "Read real time temps from fireboard.io servers"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the things
|
||||||
|
func (r *Fireboard) Init() error {
|
||||||
|
|
||||||
|
if len(r.AuthToken) == 0 {
|
||||||
|
return fmt.Errorf("You must specify an authToken")
|
||||||
|
}
|
||||||
|
if len(r.URL) == 0 {
|
||||||
|
r.URL = "https://fireboard.io/api/v1/devices.json"
|
||||||
|
}
|
||||||
|
// Have a default timeout of 4s
|
||||||
|
if r.HTTPTimeout.Duration == 0 {
|
||||||
|
r.HTTPTimeout.Duration = time.Second * 4
|
||||||
|
}
|
||||||
|
|
||||||
|
r.client.Timeout = r.HTTPTimeout.Duration
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather Reads stats from all configured servers.
|
||||||
|
func (r *Fireboard) Gather(acc telegraf.Accumulator) error {
|
||||||
|
|
||||||
|
// Perform the GET request to the fireboard servers
|
||||||
|
req, err := http.NewRequest("GET", r.URL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Token "+r.AuthToken)
|
||||||
|
resp, err := r.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Successful responses will always return status code 200
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
if resp.StatusCode == http.StatusForbidden {
|
||||||
|
return fmt.Errorf("fireboard server responded with %d [Forbidden], verify your authToken", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("fireboard responded with unexepcted status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
// Decode the response JSON into a new stats struct
|
||||||
|
var stats []fireboardStats
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
|
||||||
|
return fmt.Errorf("unable to decode fireboard response: %s", err)
|
||||||
|
}
|
||||||
|
// Range over all devices, gathering stats. Returns early in case of any error.
|
||||||
|
for _, s := range stats {
|
||||||
|
r.gatherTemps(s, acc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return text description of degree type (scale)
|
||||||
|
func scale(n int) string {
|
||||||
|
switch n {
|
||||||
|
case 1:
|
||||||
|
return "Celcius"
|
||||||
|
case 2:
|
||||||
|
return "Fahrenheit"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gathers stats from a single device, adding them to the accumulator
|
||||||
|
func (r *Fireboard) gatherTemps(s fireboardStats, acc telegraf.Accumulator) {
|
||||||
|
// Construct lookup for scale values
|
||||||
|
|
||||||
|
for _, t := range s.Latesttemps {
|
||||||
|
tags := map[string]string{
|
||||||
|
"title": s.Title,
|
||||||
|
"uuid": s.UUID,
|
||||||
|
"channel": strconv.FormatInt(t.Channel, 10),
|
||||||
|
"scale": scale(t.Degreetype),
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"temperature": t.Temp,
|
||||||
|
}
|
||||||
|
acc.AddFields("fireboard", fields, tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("fireboard", func() telegraf.Input {
|
||||||
|
return NewFireboard()
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package fireboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFireboard(t *testing.T) {
|
||||||
|
// Create a test server with the const response JSON
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintln(w, response)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Parse the URL of the test server, used to verify the expected host
|
||||||
|
u, err := url.Parse(ts.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a new fb instance with our given test server
|
||||||
|
fireboard := NewFireboard()
|
||||||
|
fireboard.AuthToken = "b4bb6e6a7b6231acb9f71b304edb2274693d8849"
|
||||||
|
fireboard.URL = u.String()
|
||||||
|
|
||||||
|
// Create a test accumulator
|
||||||
|
acc := &testutil.Accumulator{}
|
||||||
|
|
||||||
|
// Gather data from the test server
|
||||||
|
err = fireboard.Gather(acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Expect the correct values for all known keys
|
||||||
|
expectFields := map[string]interface{}{
|
||||||
|
"temperature": float64(79.9),
|
||||||
|
}
|
||||||
|
// Expect the correct values for all tags
|
||||||
|
expectTags := map[string]string{
|
||||||
|
"title": "telegraf-FireBoard",
|
||||||
|
"uuid": "b55e766c-b308-49b5-93a4-df89fe31efd0",
|
||||||
|
"channel": strconv.FormatInt(1, 10),
|
||||||
|
"scale": "Fahrenheit",
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AssertContainsTaggedFields(t, "fireboard", expectFields, expectTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = `
|
||||||
|
[{
|
||||||
|
"id": 99999,
|
||||||
|
"title": "telegraf-FireBoard",
|
||||||
|
"created": "2019-03-23T16:48:32.152010Z",
|
||||||
|
"uuid": "b55e766c-b308-49b5-93a4-df89fe31efd0",
|
||||||
|
"hardware_id": "XXXXXXXXX",
|
||||||
|
"latest_temps": [
|
||||||
|
{
|
||||||
|
"temp": 79.9,
|
||||||
|
"channel": 1,
|
||||||
|
"degreetype": 2,
|
||||||
|
"created": "2019-06-25T06:07:10Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"last_templog": "2019-06-25T06:06:40Z",
|
||||||
|
"model": "FBX11E",
|
||||||
|
"channel_count": 6,
|
||||||
|
"degreetype": 2
|
||||||
|
}]
|
||||||
|
`
|
Loading…
Reference in New Issue