From ad6946cd8cc4dd4db3837291b20ce7a60abea1c8 Mon Sep 17 00:00:00 2001 From: "David G. Simmons" Date: Sat, 4 Nov 2017 09:14:52 -0400 Subject: [PATCH] Undo Revert "Revert changes since 9b0af4478" This reverts commit 2c31345c70dac556664f12a39e68cd7c85856e4e. --- .gitignore | 1 + plugins/inputs/webhooks/particle/README.md | 35 +++++++ .../webhooks/particle/particle_webhooks.go | 67 +++++++++++++ .../particle/particle_webhooks_test.go | 97 +++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 plugins/inputs/webhooks/particle/README.md create mode 100644 plugins/inputs/webhooks/particle/particle_webhooks.go create mode 100644 plugins/inputs/webhooks/particle/particle_webhooks_test.go diff --git a/.gitignore b/.gitignore index 8269337df..e93bc8dff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tivan .idea *~ *# +.DS_Store diff --git a/plugins/inputs/webhooks/particle/README.md b/plugins/inputs/webhooks/particle/README.md new file mode 100644 index 000000000..3c345daa8 --- /dev/null +++ b/plugins/inputs/webhooks/particle/README.md @@ -0,0 +1,35 @@ +# particle webhooks + +You should configure your Particle.io's Webhooks to point at the `webhooks` service. To do this go to `(https://console.particle.io/)[https://console.particle.io]` and click `Integrations > New Integration > Webhook`. In the resulting page set `URL` to `http://:1619/particle`, and under `Advanced Settings` click on `JSON` and add: + +``` +{ + "influx_db": "your_measurement_name" +} +``` + +If required, enter your username and password, etc. and then click `Save` + + +## Events + +Your Particle device should publish an event that contains a JSON in the form of: +``` +String data = String::format("{ \"tags\" : { + \"tag_name\": \"tag_value\", + \"other_tag\": \"other_value\" + }, + \"values\": { + \"value_name\": %f, + \"other_value\": %f, + } + }", value_value, other_value + ); + Particle.publish("event_name", data, PRIVATE); +``` +Escaping the "" is required in the source file on the Particle device. +The number of tag values and field values is not restricted so you can send as many values per webhook call as you'd like. + + + +See [webhook doc](https://docs.particle.io/reference/webhooks/) diff --git a/plugins/inputs/webhooks/particle/particle_webhooks.go b/plugins/inputs/webhooks/particle/particle_webhooks.go new file mode 100644 index 000000000..258619856 --- /dev/null +++ b/plugins/inputs/webhooks/particle/particle_webhooks.go @@ -0,0 +1,67 @@ +package particle + +import ( + "encoding/json" + "log" + "net/http" + "time" + + "github.com/gorilla/mux" + "github.com/influxdata/telegraf" +) + +type event struct { + Name string `json:"event"` + Data data `json:"data"` + TTL int `json:"ttl"` + PublishedAt string `json:"published_at"` + Database string `json:"influx_db"` +} + +type data struct { + Tags map[string]string `json:"tags"` + Fields map[string]interface{} `json:"values"` +} + +func newEvent() *event { + return &event{ + Data: data{ + Tags: make(map[string]string), + Fields: make(map[string]interface{}), + }, + } +} + +func (e *event) Time() (time.Time, error) { + return time.Parse("2006-01-02T15:04:05Z", e.PublishedAt) +} + +type ParticleWebhook struct { + Path string + acc telegraf.Accumulator +} + +func (rb *ParticleWebhook) Register(router *mux.Router, acc telegraf.Accumulator) { + router.HandleFunc(rb.Path, rb.eventHandler).Methods("POST") + log.Printf("I! Started the webhooks_particle on %s\n", rb.Path) + rb.acc = acc +} + +func (rb *ParticleWebhook) eventHandler(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + e := newEvent() + if err := json.NewDecoder(r.Body).Decode(e); err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + pTime, err := e.Time() + if err != nil { + pTime = time.Now() + log.Printf("error parsing particle event time: %s. Using telegraf host time instead: %s", e.PublishedAt, pTime) + } + + rb.acc.AddFields(e.Name, e.Data.Fields, e.Data.Tags, pTime) + w.WriteHeader(http.StatusOK) +} diff --git a/plugins/inputs/webhooks/particle/particle_webhooks_test.go b/plugins/inputs/webhooks/particle/particle_webhooks_test.go new file mode 100644 index 000000000..850b2c4fc --- /dev/null +++ b/plugins/inputs/webhooks/particle/particle_webhooks_test.go @@ -0,0 +1,97 @@ +package particle + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/influxdata/telegraf/testutil" +) + +func postWebhooks(rb *ParticleWebhook, eventBody string) *httptest.ResponseRecorder { + req, _ := http.NewRequest("POST", "/", strings.NewReader(eventBody)) + w := httptest.NewRecorder() + w.Code = 500 + + rb.eventHandler(w, req) + + return w +} + +func TestNewItem(t *testing.T) { + t.Parallel() + var acc testutil.Accumulator + rb := &ParticleWebhook{Path: "/particle", acc: &acc} + resp := postWebhooks(rb, NewItemJSON()) + if resp.Code != http.StatusOK { + t.Errorf("POST new_item returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK) + } + + fields := map[string]interface{}{ + "temp_c": 26.680000, + "temp_f": 80.024001, + "infrared": 528.0, + "lux": 0.0, + "humidity": 44.937500, + "pressure": 998.998901, + "altitude": 119.331436, + "broadband": 1266.0, + } + + tags := map[string]string{ + "id": "230035001147343438323536", + "location": "TravelingWilbury", + } + + acc.AssertContainsTaggedFields(t, "temperature", fields, tags) +} + +func TestUnknowItem(t *testing.T) { + t.Parallel() + var acc testutil.Accumulator + rb := &ParticleWebhook{Path: "/particle", acc: &acc} + resp := postWebhooks(rb, UnknowJSON()) + if resp.Code != http.StatusOK { + t.Errorf("POST unknown returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK) + } +} + +func NewItemJSON() string { + return ` + { + "event": "temperature", + "data": { + "tags": { + "id": "230035001147343438323536", + "location": "TravelingWilbury" + }, + "values": { + "temp_c": 26.680000, + "temp_f": 80.024001, + "humidity": 44.937500, + "pressure": 998.998901, + "altitude": 119.331436, + "broadband": 1266, + "infrared": 528, + "lux": 0.0 + } + }, + "ttl": 60, + "published_at": "2017-09-28T21:54:10.897Z", + "coreid": "123456789938323536", + "userid": "1234ee123ac8e5ec1231a123d", + "version": 10, + "public": false, + "productID": 1234, + "name": "sensor", + "influx_db": "mydata" + }` +} + +func UnknowJSON() string { + return ` + { + "event": "roger" + }` +}