Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions energysite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package tesla

import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)

// this represents site_info endpoint
type EnergySite struct {
ID string `json:"id"`
SiteName string `json:"site_name"`
BackupReservePercent int64 `json:"backup_reserve_percent,omitempty"`
DefaultRealMode string `json:"default_real_mode,omitempty"`

productId int64
c *Client
}

type EnergySiteStatus struct {
ResourceType string `json:"resource_type"`
SiteName string `json:"site_name"`
GatewayId string `json:"gateway_id"`
EnergyLeft float64 `json:"energy_left"`
TotalPackEnergy uint64 `json:"total_pack_energy"`
PercentageCharged float64 `json:"percentage_charged"`
BatteryType string `json:"battery_type"`
BackupCapable bool `json:"backup_capable"`
BatteryPower int64 `json:"battery_power"`

c *Client
}

type SiteInfoResponse struct {
Response *EnergySite `json:"response"`
}

type SiteStatutsResponse struct {
Response *EnergySiteStatus `json:"response"`
}

// SiteCommandResponse is the response from the Tesla API after POSTing a command.
type SiteCommandResponse struct {
Response struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"response"`
}

// return fetches the energy site for the given product ID
func (c *Client) EnergySite(productID int64) (*EnergySite, error) {
siteInfoResponse := &SiteInfoResponse{}
if err := c.getJSON(c.baseURL+"/energy_sites/"+strconv.FormatInt(productID, 10)+"/site_info", siteInfoResponse); err != nil {
return nil, err
}
siteInfoResponse.Response.c = c
siteInfoResponse.Response.productId = productID
return siteInfoResponse.Response, nil
}

func (s *EnergySite) EnergySiteStatus() (*EnergySiteStatus, error) {
siteStatusResponse := &SiteStatutsResponse{}
if err := s.c.getJSON(s.statusPath(), siteStatusResponse); err != nil {
return nil, err
}
siteStatusResponse.Response.c = s.c
return siteStatusResponse.Response, nil
}

func (s *EnergySite) basePath() string {
return strings.Join([]string{s.c.baseURL, "energy_sites", strconv.FormatInt(s.productId, 10)}, "/")
}

func (s *EnergySite) statusPath() string {
return strings.Join([]string{s.basePath(), "site_status"}, "/")
}

func (s *EnergySite) SetBatteryReserve(percent uint64) error {
url := s.basePath() + "/backup"
payload := fmt.Sprintf(`{"backup_reserve_percent":%d}`, percent)
body, err := s.sendCommand(url, []byte(payload))
if err != nil {
return err
}

response := SiteCommandResponse{}
if err := json.Unmarshal(body, &response); err != nil {
return err
}

if response.Response.Code != 201 {
return fmt.Errorf("batteryReserve failed: %s", response.Response.Message)
}

return nil
}

// Sends a command to the vehicle
func (s *EnergySite) sendCommand(url string, reqBody []byte) ([]byte, error) {
body, err := s.c.post(url, reqBody)
if err != nil {
return nil, err
}
if len(body) > 0 {
response := &CommandResponse{}
if err := json.Unmarshal(body, response); err != nil {
return nil, err
}
if !response.Response.Result && response.Response.Reason != "" {
return nil, errors.New(response.Response.Reason)
}
}
return body, nil
}
73 changes: 73 additions & 0 deletions examples/show_energy_site_status/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"context"
"flag"
"fmt"
"os"

"github.com/bogosj/tesla"
)

var tokenPath = flag.String("token", "", "path to token file")
var reservePercentage = flag.Int64("backupPercentage", -1, "backup percentage to set")

// example that demos fetching of site information and optionally setting the battery reserve percentage for the site
func main() {
flag.Parse()

if *tokenPath == "" {
fmt.Println("--token must be specified")
os.Exit(1)
}

if err := run(context.Background(), *tokenPath, *reservePercentage); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func run(ctx context.Context, tokenPath string, reservePercentage int64) error {
c, err := tesla.NewClient(ctx, tesla.WithTokenFile(tokenPath))
if err != nil {
return err
}

prods, err := c.Products()
if err != nil {
return err
}

for i, p := range prods {
if i > 0 {
fmt.Println("----")
}
fmt.Printf("ID: %s\n", p.ID)
fmt.Printf("ResourceType %s\n", p.ResourceType)
if p.EnergySiteId != 0 {
fmt.Printf("EnergySiteId: %d\n", p.EnergySiteId)

es, err := c.EnergySite(p.EnergySiteId)
if err != nil {
fmt.Printf("error fetching site info: %+v\n", err)
os.Exit(1)
}
fmt.Printf("EnergySite: %+v\n", *es)

esi, err := es.EnergySiteStatus()
if err != nil {
fmt.Printf("error fetching site status: %+v\n", err)
os.Exit(1)
}
fmt.Printf("EnergySiteInfo: %+v\n", *esi)

if reservePercentage != -1 {
if err := es.SetBatteryReserve(uint64(reservePercentage)); err != nil {
fmt.Printf("error setting battery reserve: %+v\n", err)
os.Exit(1)
}
}
}
}
return nil
}
36 changes: 36 additions & 0 deletions products.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tesla

type Product struct {
EnergySiteId int64 `json:"energy_site_id,omitempty"`
ResourceType string `json:"resource_type"`
ID string `json:"id"`
AssetSiteId string `json:"asset_site_id,omitempty"`
GatewayId string `json:"gateway_id,omitempty"`
WarpSiteNumber string `json:"warp_site_number,omitempty"`
EnergyLeft float64 `json:"energy_left,omitempty"`
TotalPackEnergy uint64 `json:"total_pack_energy,omitempty"`
PercentageCharged float64 `json:"percentage_charged,omitempty"`
BatteryType string `json:"battery_type,omitempty"`
BackupCapable bool `json:"backup_capable,omitempty"`
BatteryPower int64 `json:"battery_power,omitempty"`

c *Client
}

// ProductResponse contains the product details from the Tesla API.
type ProductsResponse struct {
Response []*Product `json:"response"`
Count int `json:"count"`
}

// Products fetches the products associated to a Tesla account via the API.
func (c *Client) Products() ([]*Product, error) {
productsResponse := &ProductsResponse{}
if err := c.getJSON(c.baseURL+"/products", productsResponse); err != nil {
return nil, err
}
for _, v := range productsResponse.Response {
v.c = c
}
return productsResponse.Response, nil
}