feat(inputs.fibaro): Support HC3 device types (#13754)

This commit is contained in:
Joshua Powers 2023-08-28 14:47:08 -06:00 committed by GitHub
parent 5fb290fb46
commit ca2295e1a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 689 additions and 272 deletions

View File

@ -4,6 +4,9 @@ The Fibaro plugin makes HTTP calls to the Fibaro controller API to gather values
of hooked devices. Those values could be true (1) or false (0) for switches, of hooked devices. Those values could be true (1) or false (0) for switches,
percentage for dimmers, temperature, etc. percentage for dimmers, temperature, etc.
By default, this plugin supports HC2 devices. To support HC3 devices, please
use the device type config option.
## Global configuration options <!-- @/docs/includes/plugin_config.md --> ## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support In addition to the plugin-specific configuration settings, plugins support
@ -28,6 +31,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## Amount of time allowed to complete the HTTP request ## Amount of time allowed to complete the HTTP request
# timeout = "5s" # timeout = "5s"
## Fibaro Device Type
## By default, this plugin will attempt to read using the HC2 API. For HC3
## devices, set this to "HC3"
# device_type = "HC2"
``` ```
## Metrics ## Metrics

View File

@ -3,15 +3,16 @@ package fibaro
import ( import (
_ "embed" _ "embed"
"encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/inputs/fibaro/hc2"
"github.com/influxdata/telegraf/plugins/inputs/fibaro/hc3"
) )
//go:embed sample.conf //go:embed sample.conf
@ -21,66 +22,28 @@ const defaultTimeout = 5 * time.Second
// Fibaro contains connection information // Fibaro contains connection information
type Fibaro struct { type Fibaro struct {
URL string `toml:"url"` URL string `toml:"url"`
Username string `toml:"username"`
// HTTP Basic Auth Credentials Password string `toml:"password"`
Username string `toml:"username"` Timeout config.Duration `toml:"timeout"`
Password string `toml:"password"` DeviceType string `toml:"device_type"`
Timeout config.Duration `toml:"timeout"`
client *http.Client client *http.Client
} }
// LinkRoomsSections links rooms to sections
type LinkRoomsSections struct {
Name string
SectionID uint16
}
// Sections contains sections informations
type Sections struct {
ID uint16 `json:"id"`
Name string `json:"name"`
}
// Rooms contains rooms informations
type Rooms struct {
ID uint16 `json:"id"`
Name string `json:"name"`
SectionID uint16 `json:"sectionID"`
}
// Devices contains devices informations
type Devices struct {
ID uint16 `json:"id"`
Name string `json:"name"`
RoomID uint16 `json:"roomID"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
Properties struct {
BatteryLevel *string `json:"batteryLevel"`
Dead string `json:"dead"`
Energy *string `json:"energy"`
Power *string `json:"power"`
Value interface{} `json:"value"`
Value2 *string `json:"value2"`
} `json:"properties"`
}
// getJSON connects, authenticates and reads JSON payload returned by Fibaro box // getJSON connects, authenticates and reads JSON payload returned by Fibaro box
func (f *Fibaro) getJSON(path string, dataStruct interface{}) error { func (f *Fibaro) getJSON(path string) ([]byte, error) {
var requestURL = f.URL + path var requestURL = f.URL + path
req, err := http.NewRequest("GET", requestURL, nil) req, err := http.NewRequest("GET", requestURL, nil)
if err != nil { if err != nil {
return err return nil, err
} }
req.SetBasicAuth(f.Username, f.Password) req.SetBasicAuth(f.Username, f.Password)
resp, err := f.client.Do(req) resp, err := f.client.Do(req)
if err != nil { if err != nil {
return err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -91,13 +54,24 @@ func (f *Fibaro) getJSON(path string, dataStruct interface{}) error {
http.StatusText(resp.StatusCode), http.StatusText(resp.StatusCode),
http.StatusOK, http.StatusOK,
http.StatusText(http.StatusOK)) http.StatusText(http.StatusOK))
return err return nil, err
} }
dec := json.NewDecoder(resp.Body) bodyBytes, err := io.ReadAll(resp.Body)
err = dec.Decode(&dataStruct)
if err != nil { if err != nil {
return err return nil, fmt.Errorf("unable to read response body: %w", err)
}
return bodyBytes, nil
}
func (f *Fibaro) Init() error {
switch f.DeviceType {
case "":
f.DeviceType = "HC2"
case "HC2", "HC3":
default:
return fmt.Errorf("invalid option for device type")
} }
return nil return nil
@ -118,89 +92,24 @@ func (f *Fibaro) Gather(acc telegraf.Accumulator) error {
} }
} }
var tmpSections []Sections sections, err := f.getJSON("/api/sections")
err := f.getJSON("/api/sections", &tmpSections)
if err != nil { if err != nil {
return err return err
} }
sections := map[uint16]string{} rooms, err := f.getJSON("/api/rooms")
for _, v := range tmpSections {
sections[v.ID] = v.Name
}
var tmpRooms []Rooms
err = f.getJSON("/api/rooms", &tmpRooms)
if err != nil { if err != nil {
return err return err
} }
rooms := map[uint16]LinkRoomsSections{} devices, err := f.getJSON("/api/devices")
for _, v := range tmpRooms {
rooms[v.ID] = LinkRoomsSections{Name: v.Name, SectionID: v.SectionID}
}
var devices []Devices
err = f.getJSON("/api/devices", &devices)
if err != nil { if err != nil {
return err return err
} }
for _, device := range devices { switch f.DeviceType {
// skip device in some cases case "HC2":
if device.RoomID == 0 || return hc2.Parse(acc, sections, rooms, devices)
!device.Enabled || case "HC3":
device.Properties.Dead == "true" || return hc3.Parse(acc, sections, rooms, devices)
device.Type == "com.fibaro.zwaveDevice" {
continue
}
tags := map[string]string{
"deviceId": strconv.FormatUint(uint64(device.ID), 10),
"section": sections[rooms[device.RoomID].SectionID],
"room": rooms[device.RoomID].Name,
"name": device.Name,
"type": device.Type,
}
fields := make(map[string]interface{})
if device.Properties.BatteryLevel != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.BatteryLevel, 64); err == nil {
fields["batteryLevel"] = fValue
}
}
if device.Properties.Energy != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Energy, 64); err == nil {
fields["energy"] = fValue
}
}
if device.Properties.Power != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Power, 64); err == nil {
fields["power"] = fValue
}
}
if device.Properties.Value != nil {
value := device.Properties.Value
switch value {
case "true":
value = "1"
case "false":
value = "0"
}
if fValue, err := strconv.ParseFloat(value.(string), 64); err == nil {
fields["value"] = fValue
}
}
if device.Properties.Value2 != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Value2, 64); err == nil {
fields["value2"] = fValue
}
}
acc.AddFields("fibaro", fields, tags)
} }
return nil return nil

View File

@ -4,131 +4,16 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"path"
"testing" "testing"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/testutil" "github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const sectionsJSON = `
[
{
"id": 1,
"name": "Section 1",
"sortOrder": 1
},
{
"id": 2,
"name": "Section 2",
"sortOrder": 2
},
{
"id": 3,
"name": "Section 3",
"sortOrder": 3
}
]`
const roomsJSON = `
[
{
"id": 1,
"name": "Room 1",
"sectionID": 1,
"icon": "room_1",
"sortOrder": 1
},
{
"id": 2,
"name": "Room 2",
"sectionID": 2,
"icon": "room_2",
"sortOrder": 2
},
{
"id": 3,
"name": "Room 3",
"sectionID": 3,
"icon": "room_3",
"sortOrder": 3
},
{
"id": 4,
"name": "Room 4",
"sectionID": 3,
"icon": "room_4",
"sortOrder": 4
}
]`
const devicesJSON = `
[
{
"id": 1,
"name": "Device 1",
"roomID": 1,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "false"
},
"sortOrder": 1
},
{
"id": 2,
"name": "Device 2",
"roomID": 2,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "true"
},
"sortOrder": 2
},
{
"id": 3,
"name": "Device 3",
"roomID": 3,
"type": "com.fibaro.multilevelSwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "67"
},
"sortOrder": 3
},
{
"id": 4,
"name": "Device 4",
"roomID": 4,
"type": "com.fibaro.temperatureSensor",
"enabled": true,
"properties": {
"batteryLevel": "100",
"dead": "false",
"value": "22.80"
},
"sortOrder": 4
},
{
"id": 5,
"name": "Device 5",
"roomID": 4,
"type": "com.fibaro.FGRM222",
"enabled": true,
"properties": {
"energy": "4.33",
"power": "0.7",
"dead": "false",
"value": "50",
"value2": "75"
},
"sortOrder": 5
}
]`
// TestUnauthorized validates that 401 (wrong credentials) is managed properly // TestUnauthorized validates that 401 (wrong credentials) is managed properly
func TestUnauthorized(t *testing.T) { func TestUnauthorized(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -142,6 +27,7 @@ func TestUnauthorized(t *testing.T) {
Password: "pass", Password: "pass",
client: &http.Client{}, client: &http.Client{},
} }
require.NoError(t, a.Init())
var acc testutil.Accumulator var acc testutil.Accumulator
err := acc.GatherError(a.Gather) err := acc.GatherError(a.Gather)
@ -154,11 +40,17 @@ func TestJSONSuccess(t *testing.T) {
payload := "" payload := ""
switch r.URL.Path { switch r.URL.Path {
case "/api/sections": case "/api/sections":
payload = sectionsJSON content, err := os.ReadFile(path.Join("testdata", "sections.json"))
require.NoError(t, err)
payload = string(content)
case "/api/rooms": case "/api/rooms":
payload = roomsJSON content, err := os.ReadFile(path.Join("testdata", "rooms.json"))
require.NoError(t, err)
payload = string(content)
case "/api/devices": case "/api/devices":
payload = devicesJSON content, err := os.ReadFile(path.Join("testdata", "device_hc2.json"))
require.NoError(t, err)
payload = string(content)
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
_, err := fmt.Fprintln(w, payload) _, err := fmt.Fprintln(w, payload)
@ -172,36 +64,212 @@ func TestJSONSuccess(t *testing.T) {
Password: "pass", Password: "pass",
client: &http.Client{}, client: &http.Client{},
} }
require.NoError(t, a.Init())
var acc testutil.Accumulator var acc testutil.Accumulator
err := acc.GatherError(a.Gather) err := acc.GatherError(a.Gather)
require.NoError(t, err) require.NoError(t, err)
// Gather should add 5 metrics
require.Equal(t, uint64(5), acc.NMetrics()) require.Equal(t, uint64(5), acc.NMetrics())
// Ensure fields / values are correct - Device 1 expected := []telegraf.Metric{
tags := map[string]string{"deviceId": "1", "section": "Section 1", "room": "Room 1", "name": "Device 1", "type": "com.fibaro.binarySwitch"} testutil.MustMetric(
fields := map[string]interface{}{"value": float64(0)} "fibaro",
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags) map[string]string{
"deviceId": "1",
"section": "Section 1",
"room": "Room 1",
"name": "Device 1",
"type": "com.fibaro.binarySwitch",
},
map[string]interface{}{
"value": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "2",
"section": "Section 2",
"room": "Room 2",
"name": "Device 2",
"type": "com.fibaro.binarySwitch",
},
map[string]interface{}{
"value": float64(1),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "3",
"section": "Section 3",
"room": "Room 3",
"name": "Device 3",
"type": "com.fibaro.multilevelSwitch",
},
map[string]interface{}{
"value": float64(67),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "4",
"section": "Section 3",
"room": "Room 4",
"name": "Device 4",
"type": "com.fibaro.temperatureSensor",
},
map[string]interface{}{
"batteryLevel": float64(100),
"value": float64(22.8),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "5",
"section": "Section 3",
"room": "Room 4",
"name": "Device 5",
"type": "com.fibaro.FGRM222",
},
map[string]interface{}{
"energy": float64(4.33),
"power": float64(0.7),
"value": float64(50),
"value2": float64(75),
},
time.Unix(0, 0),
),
}
// Ensure fields / values are correct - Device 2 testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
tags = map[string]string{"deviceId": "2", "section": "Section 2", "room": "Room 2", "name": "Device 2", "type": "com.fibaro.binarySwitch"} }
fields = map[string]interface{}{"value": float64(1)}
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags) func TestHC3JSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Ensure fields / values are correct - Device 3 payload := ""
tags = map[string]string{"deviceId": "3", "section": "Section 3", "room": "Room 3", "name": "Device 3", "type": "com.fibaro.multilevelSwitch"} switch r.URL.Path {
fields = map[string]interface{}{"value": float64(67)} case "/api/sections":
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags) content, err := os.ReadFile(path.Join("testdata", "sections.json"))
require.NoError(t, err)
// Ensure fields / values are correct - Device 4 payload = string(content)
tags = map[string]string{"deviceId": "4", "section": "Section 3", "room": "Room 4", "name": "Device 4", "type": "com.fibaro.temperatureSensor"} case "/api/rooms":
fields = map[string]interface{}{"batteryLevel": float64(100), "value": float64(22.8)} content, err := os.ReadFile(path.Join("testdata", "rooms.json"))
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags) require.NoError(t, err)
payload = string(content)
// Ensure fields / values are correct - Device 5 case "/api/devices":
tags = map[string]string{"deviceId": "5", "section": "Section 3", "room": "Room 4", "name": "Device 5", "type": "com.fibaro.FGRM222"} content, err := os.ReadFile(path.Join("testdata", "device_hc3.json"))
fields = map[string]interface{}{"energy": float64(4.33), "power": float64(0.7), "value": float64(50), "value2": float64(75)} require.NoError(t, err)
acc.AssertContainsTaggedFields(t, "fibaro", fields, tags) payload = string(content)
}
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprintln(w, payload)
require.NoError(t, err)
}))
defer ts.Close()
a := Fibaro{
URL: ts.URL,
Username: "user",
Password: "pass",
DeviceType: "HC3",
client: &http.Client{},
}
require.NoError(t, a.Init())
var acc testutil.Accumulator
err := acc.GatherError(a.Gather)
require.NoError(t, err)
require.Equal(t, uint64(5), acc.NMetrics())
expected := []telegraf.Metric{
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "1",
"section": "Section 1",
"room": "Room 1",
"name": "Device 1",
"type": "com.fibaro.binarySwitch",
},
map[string]interface{}{
"value": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "2",
"section": "Section 2",
"room": "Room 2",
"name": "Device 2",
"type": "com.fibaro.binarySwitch",
},
map[string]interface{}{
"value": float64(1),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "3",
"section": "Section 3",
"room": "Room 3",
"name": "Device 3",
"type": "com.fibaro.multilevelSwitch",
},
map[string]interface{}{
"value": float64(67),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "4",
"section": "Section 3",
"room": "Room 4",
"name": "Device 4",
"type": "com.fibaro.temperatureSensor",
},
map[string]interface{}{
"batteryLevel": float64(100),
"value": float64(22.8),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"fibaro",
map[string]string{
"deviceId": "5",
"section": "Section 3",
"room": "Room 4",
"name": "Device 5",
"type": "com.fibaro.FGRM222",
},
map[string]interface{}{
"energy": float64(4.33),
"power": float64(0.7),
"value": float64(34),
},
time.Unix(0, 0),
),
}
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
}
func TestInvalidDeviceType(t *testing.T) {
a := Fibaro{
DeviceType: "foobar",
}
require.Error(t, a.Init())
} }

View File

@ -0,0 +1,95 @@
package hc2
import (
"encoding/json"
"strconv"
"github.com/influxdata/telegraf"
)
func Parse(acc telegraf.Accumulator, sectionBytes []byte, roomBytes []byte, deviecsBytes []byte) error {
var tmpSections []Sections
if err := json.Unmarshal(sectionBytes, &tmpSections); err != nil {
return err
}
sections := map[uint16]string{}
for _, v := range tmpSections {
sections[v.ID] = v.Name
}
var tmpRooms []Rooms
if err := json.Unmarshal(roomBytes, &tmpRooms); err != nil {
return err
}
rooms := map[uint16]LinkRoomsSections{}
for _, v := range tmpRooms {
rooms[v.ID] = LinkRoomsSections{Name: v.Name, SectionID: v.SectionID}
}
var devices []Devices
if err := json.Unmarshal(deviecsBytes, &devices); err != nil {
return err
}
for _, device := range devices {
// skip device in some cases
if device.RoomID == 0 ||
!device.Enabled ||
device.Properties.Dead == "true" ||
device.Type == "com.fibaro.zwaveDevice" {
continue
}
tags := map[string]string{
"deviceId": strconv.FormatUint(uint64(device.ID), 10),
"section": sections[rooms[device.RoomID].SectionID],
"room": rooms[device.RoomID].Name,
"name": device.Name,
"type": device.Type,
}
fields := make(map[string]interface{})
if device.Properties.BatteryLevel != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.BatteryLevel, 64); err == nil {
fields["batteryLevel"] = fValue
}
}
if device.Properties.Energy != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Energy, 64); err == nil {
fields["energy"] = fValue
}
}
if device.Properties.Power != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Power, 64); err == nil {
fields["power"] = fValue
}
}
if device.Properties.Value != nil {
value := device.Properties.Value
switch value {
case "true":
value = "1"
case "false":
value = "0"
}
if fValue, err := strconv.ParseFloat(value.(string), 64); err == nil {
fields["value"] = fValue
}
}
if device.Properties.Value2 != nil {
if fValue, err := strconv.ParseFloat(*device.Properties.Value2, 64); err == nil {
fields["value2"] = fValue
}
}
acc.AddFields("fibaro", fields, tags)
}
return nil
}

View File

@ -0,0 +1,37 @@
package hc2
// LinkRoomsSections links rooms to sections
type LinkRoomsSections struct {
Name string
SectionID uint16
}
// Sections contains sections informations
type Sections struct {
ID uint16 `json:"id"`
Name string `json:"name"`
}
// Rooms contains rooms informations
type Rooms struct {
ID uint16 `json:"id"`
Name string `json:"name"`
SectionID uint16 `json:"sectionID"`
}
// Devices contains devices informations
type Devices struct {
ID uint16 `json:"id"`
Name string `json:"name"`
RoomID uint16 `json:"roomID"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
Properties struct {
BatteryLevel *string `json:"batteryLevel"`
Dead string `json:"dead"`
Energy *string `json:"energy"`
Power *string `json:"power"`
Value interface{} `json:"value"`
Value2 *string `json:"value2"`
} `json:"properties"`
}

View File

@ -0,0 +1,80 @@
package hc3
import (
"encoding/json"
"fmt"
"strconv"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
)
func Parse(acc telegraf.Accumulator, sectionBytes []byte, roomBytes []byte, deviecsBytes []byte) error {
var tmpSections []Sections
if err := json.Unmarshal(sectionBytes, &tmpSections); err != nil {
return err
}
sections := make(map[uint16]string, len(tmpSections))
for _, v := range tmpSections {
sections[v.ID] = v.Name
}
var tmpRooms []Rooms
if err := json.Unmarshal(roomBytes, &tmpRooms); err != nil {
return err
}
rooms := make(map[uint16]linkRoomsSections, len(tmpRooms))
for _, v := range tmpRooms {
rooms[v.ID] = linkRoomsSections{Name: v.Name, SectionID: v.SectionID}
}
var devices []Devices
if err := json.Unmarshal(deviecsBytes, &devices); err != nil {
return err
}
for _, device := range devices {
// skip device in some cases
if device.RoomID == 0 ||
!device.Enabled ||
device.Properties.Dead ||
device.Type == "com.fibaro.zwaveDevice" {
continue
}
tags := map[string]string{
"deviceId": strconv.FormatUint(uint64(device.ID), 10),
"section": sections[rooms[device.RoomID].SectionID],
"room": rooms[device.RoomID].Name,
"name": device.Name,
"type": device.Type,
}
fields := make(map[string]interface{})
if device.Properties.BatteryLevel != nil {
fields["batteryLevel"] = *device.Properties.BatteryLevel
}
if device.Properties.Energy != nil {
fields["energy"] = *device.Properties.Energy
}
if device.Properties.Power != nil {
fields["power"] = *device.Properties.Power
}
// Value can be a JSON bool, string, or numeric value
if device.Properties.Value != nil {
v, err := internal.ToFloat64(device.Properties.Value)
if err != nil {
acc.AddError(fmt.Errorf("unable to convert value: %w", err))
} else {
fields["value"] = v
}
}
acc.AddFields("fibaro", fields, tags)
}
return nil
}

View File

@ -0,0 +1,37 @@
package hc3
// LinkRoomsSections links rooms to sections
type linkRoomsSections struct {
Name string
SectionID uint16
}
// Sections contains sections informations
type Sections struct {
ID uint16 `json:"id"`
Name string `json:"name"`
}
// Rooms contains rooms informations
type Rooms struct {
ID uint16 `json:"id"`
Name string `json:"name"`
SectionID uint16 `json:"sectionID"`
}
// Devices contains devices informations
type Devices struct {
ID uint16 `json:"id"`
Name string `json:"name"`
RoomID uint16 `json:"roomID"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
Properties struct {
BatteryLevel *float64 `json:"batteryLevel"`
Dead bool `json:"dead"`
Energy *float64 `json:"energy"`
Power *float64 `json:"power"`
Value interface{} `json:"value"`
Value2 *string `json:"value2"`
} `json:"properties"`
}

View File

@ -10,3 +10,8 @@
## Amount of time allowed to complete the HTTP request ## Amount of time allowed to complete the HTTP request
# timeout = "5s" # timeout = "5s"
## Fibaro Device Type
## By default, this plugin will attempt to read using the HC2 API. For HC3
## devices, set this to "HC3"
# device_type = "HC2"

View File

@ -0,0 +1,66 @@
[
{
"id": 1,
"name": "Device 1",
"roomID": 1,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "false"
},
"sortOrder": 1
},
{
"id": 2,
"name": "Device 2",
"roomID": 2,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "true"
},
"sortOrder": 2
},
{
"id": 3,
"name": "Device 3",
"roomID": 3,
"type": "com.fibaro.multilevelSwitch",
"enabled": true,
"properties": {
"dead": "false",
"value": "67"
},
"sortOrder": 3
},
{
"id": 4,
"name": "Device 4",
"roomID": 4,
"type": "com.fibaro.temperatureSensor",
"enabled": true,
"properties": {
"batteryLevel": "100",
"dead": "false",
"value": "22.80"
},
"sortOrder": 4
},
{
"id": 5,
"name": "Device 5",
"roomID": 4,
"type": "com.fibaro.FGRM222",
"enabled": true,
"properties": {
"energy": "4.33",
"power": "0.7",
"dead": "false",
"value": "50",
"value2": "75"
},
"sortOrder": 5
}
]

View File

@ -0,0 +1,65 @@
[
{
"id": 1,
"name": "Device 1",
"roomID": 1,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": false,
"value": false
},
"sortOrder": 1
},
{
"id": 2,
"name": "Device 2",
"roomID": 2,
"type": "com.fibaro.binarySwitch",
"enabled": true,
"properties": {
"dead": false,
"value": true
},
"sortOrder": 2
},
{
"id": 3,
"name": "Device 3",
"roomID": 3,
"type": "com.fibaro.multilevelSwitch",
"enabled": true,
"properties": {
"dead": false,
"value": "67"
},
"sortOrder": 3
},
{
"id": 4,
"name": "Device 4",
"roomID": 4,
"type": "com.fibaro.temperatureSensor",
"enabled": true,
"properties": {
"batteryLevel": 100,
"dead": false,
"value": 22.80
},
"sortOrder": 4
},
{
"id": 5,
"name": "Device 5",
"roomID": 4,
"type": "com.fibaro.FGRM222",
"enabled": true,
"properties": {
"energy": 4.33,
"power": 0.7,
"dead": false,
"value": 34
},
"sortOrder": 5
}
]

View File

@ -0,0 +1,30 @@
[
{
"id": 1,
"name": "Room 1",
"sectionID": 1,
"icon": "room_1",
"sortOrder": 1
},
{
"id": 2,
"name": "Room 2",
"sectionID": 2,
"icon": "room_2",
"sortOrder": 2
},
{
"id": 3,
"name": "Room 3",
"sectionID": 3,
"icon": "room_3",
"sortOrder": 3
},
{
"id": 4,
"name": "Room 4",
"sectionID": 3,
"icon": "room_4",
"sortOrder": 4
}
]

View File

@ -0,0 +1,17 @@
[
{
"id": 1,
"name": "Section 1",
"sortOrder": 1
},
{
"id": 2,
"name": "Section 2",
"sortOrder": 2
},
{
"id": 3,
"name": "Section 3",
"sortOrder": 3
}
]