-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from mutablelogic/v1
Added home assistant client
- Loading branch information
Showing
8 changed files
with
417 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
homeassistant implements an API client for Home Assistant API | ||
https://developers.home-assistant.io/docs/api/rest/ | ||
*/ | ||
package homeassistant | ||
|
||
import ( | ||
// Packages | ||
"github.com/mutablelogic/go-client/pkg/client" | ||
) | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// TYPES | ||
|
||
type Client struct { | ||
*client.Client | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// LIFECYCLE | ||
|
||
func New(endPoint, apiKey string, opts ...client.ClientOpt) (*Client, error) { | ||
// Add a final slash to the endpoint | ||
if endPoint[len(endPoint)-1] != '/' { | ||
endPoint += "/" | ||
} | ||
|
||
// Create client | ||
client, err := client.New(append(opts, client.OptEndpoint(endPoint), client.OptReqToken(client.Token{ | ||
Scheme: client.Bearer, | ||
Value: apiKey, | ||
}))...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Return the client | ||
return &Client{client}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package homeassistant_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
// Packages | ||
opts "github.com/mutablelogic/go-client/pkg/client" | ||
homeassistant "github.com/mutablelogic/go-client/pkg/homeassistant" | ||
assert "github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_client_001(t *testing.T) { | ||
assert := assert.New(t) | ||
client, err := homeassistant.New(GetEndPoint(t), GetApiKey(t), opts.OptTrace(os.Stderr, true)) | ||
assert.NoError(err) | ||
assert.NotNil(client) | ||
t.Log(client) | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// ENVIRONMENT | ||
|
||
func GetApiKey(t *testing.T) string { | ||
key := os.Getenv("HA_API_KEY") | ||
if key == "" { | ||
t.Skip("HA_API_KEY not set") | ||
t.SkipNow() | ||
} | ||
return key | ||
} | ||
|
||
func GetEndPoint(t *testing.T) string { | ||
key := os.Getenv("HA_API_URL") | ||
if key == "" { | ||
t.Skip("HA_API_URL not set") | ||
t.SkipNow() | ||
} | ||
return key | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package homeassistant | ||
|
||
import "github.com/mutablelogic/go-client/pkg/client" | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// TYPES | ||
|
||
type Event struct { | ||
Event string `json:"event"` | ||
Listeners uint `json:"listener_count"` | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// API CALLS | ||
|
||
// Events returns all the events and number of listeners | ||
func (c *Client) Events() ([]Event, error) { | ||
var response []Event | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response, client.OptPath("events")); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Return success | ||
return response, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package homeassistant_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
// Packages | ||
opts "github.com/mutablelogic/go-client/pkg/client" | ||
homeassistant "github.com/mutablelogic/go-client/pkg/homeassistant" | ||
assert "github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_events_001(t *testing.T) { | ||
assert := assert.New(t) | ||
client, err := homeassistant.New(GetEndPoint(t), GetApiKey(t), opts.OptTrace(os.Stderr, true)) | ||
assert.NoError(err) | ||
assert.NotNil(client) | ||
|
||
events, err := client.Events() | ||
assert.NoError(err) | ||
assert.NotNil(events) | ||
|
||
t.Log(events) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package homeassistant | ||
|
||
import "github.com/mutablelogic/go-client/pkg/client" | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// API CALLS | ||
|
||
// ListModels returns all the models | ||
func (c *Client) Health() (string, error) { | ||
// Response schema | ||
type responseHealth struct { | ||
Message string `json:"message"` | ||
} | ||
|
||
// Return the response | ||
var response responseHealth | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response); err != nil { | ||
return "", err | ||
} | ||
|
||
// Return success | ||
return response.Message, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package homeassistant_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
// Packages | ||
opts "github.com/mutablelogic/go-client/pkg/client" | ||
homeassistant "github.com/mutablelogic/go-client/pkg/homeassistant" | ||
assert "github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_health_001(t *testing.T) { | ||
assert := assert.New(t) | ||
client, err := homeassistant.New(GetEndPoint(t), GetApiKey(t), opts.OptTrace(os.Stderr, true)) | ||
assert.NoError(err) | ||
assert.NotNil(client) | ||
|
||
message, err := client.Health() | ||
assert.NoError(err) | ||
assert.NotEmpty(message) | ||
|
||
t.Log(message) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package homeassistant | ||
|
||
import ( | ||
"encoding/json" | ||
"strings" | ||
"time" | ||
|
||
"github.com/mutablelogic/go-client/pkg/client" | ||
) | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// TYPES | ||
|
||
type State struct { | ||
Entity string `json:"entity_id"` | ||
LastChanged time.Time `json:"last_changed"` | ||
State string `json:"state"` | ||
Attributes map[string]any `json:"attributes"` | ||
} | ||
|
||
type Sensor struct { | ||
Type string `json:"type"` | ||
Entity string `json:"entity_id"` | ||
Name string `json:"friendly_name"` | ||
Value string `json:"state,omitempty"` | ||
Unit string `json:"unit_of_measurement,omitempty"` | ||
Class string `json:"device_class,omitempty"` | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// API CALLS | ||
|
||
// States returns all the entities and their state | ||
func (c *Client) States() ([]State, error) { | ||
// Return the response | ||
var response []State | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response, client.OptPath("states")); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Return success | ||
return response, nil | ||
} | ||
|
||
// Sensors returns all sensor entities and their state | ||
func (c *Client) Sensors() ([]Sensor, error) { | ||
// Return the response | ||
var response []State | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response, client.OptPath("states")); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Filter out sensors | ||
var sensors []Sensor | ||
for _, state := range response { | ||
if !strings.HasPrefix(state.Entity, "sensor.") && !strings.HasPrefix(state.Entity, "binary_sensor.") { | ||
continue | ||
} | ||
sensors = append(sensors, Sensor{ | ||
Type: "sensor", | ||
Entity: state.Entity, | ||
Name: state.Name(), | ||
Value: state.State, | ||
Unit: state.UnitOfMeasurement(), | ||
Class: state.DeviceClass(), | ||
}) | ||
} | ||
|
||
// Return success | ||
return sensors, nil | ||
} | ||
|
||
// Actuators returns all button, switch and lock entities and their state | ||
func (c *Client) Actuators() ([]Sensor, error) { | ||
// Return the response | ||
var response []State | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response, client.OptPath("states")); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Filter out buttons, locks, and switches | ||
var sensors []Sensor | ||
for _, state := range response { | ||
if !strings.HasPrefix(state.Entity, "button.") && !strings.HasPrefix(state.Entity, "lock.") && !strings.HasPrefix(state.Entity, "switch.") { | ||
continue | ||
} | ||
sensors = append(sensors, Sensor{ | ||
Type: "actuator", | ||
Entity: state.Entity, | ||
Name: state.Name(), | ||
Value: state.State, | ||
Class: state.DeviceClass(), | ||
}) | ||
} | ||
|
||
// Return success | ||
return sensors, nil | ||
} | ||
|
||
// Lights returns all light entities and their state | ||
func (c *Client) Lights() ([]Sensor, error) { | ||
// Return the response | ||
var response []State | ||
payload := client.NewRequest(client.ContentTypeJson) | ||
if err := c.Do(payload, &response, client.OptPath("states")); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Filter out sensors | ||
var lights []Sensor | ||
for _, state := range response { | ||
if !strings.HasPrefix(state.Entity, "light.") { | ||
continue | ||
} | ||
lights = append(lights, Sensor{ | ||
Type: "light", | ||
Entity: state.Entity, | ||
Name: state.Name(), | ||
Value: state.State, | ||
}) | ||
} | ||
|
||
// Return success | ||
return lights, nil | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// STRINGIFY | ||
|
||
func (s State) String() string { | ||
data, _ := json.MarshalIndent(s, "", " ") | ||
return string(data) | ||
} | ||
|
||
func (s Sensor) String() string { | ||
data, _ := json.MarshalIndent(s, "", " ") | ||
return string(data) | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// METHODS | ||
|
||
func (s State) Name() string { | ||
name, ok := s.Attributes["friendly_name"] | ||
if !ok { | ||
return s.Entity | ||
} else if name_, ok := name.(string); !ok { | ||
return s.Entity | ||
} else { | ||
return name_ | ||
} | ||
} | ||
|
||
func (s State) DeviceClass() string { | ||
class, ok := s.Attributes["device_class"] | ||
if !ok { | ||
return "" | ||
} else if class_, ok := class.(string); !ok { | ||
return "" | ||
} else { | ||
return class_ | ||
} | ||
} | ||
|
||
func (s State) UnitOfMeasurement() string { | ||
unit, ok := s.Attributes["unit_of_measurement"] | ||
if !ok { | ||
return "" | ||
} else if unit_, ok := unit.(string); !ok { | ||
return "" | ||
} else { | ||
return unit_ | ||
} | ||
} |
Oops, something went wrong.