netatmo

package module
v0.0.0-...-89b2a28 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2021 License: MIT Imports: 12 Imported by: 0

README

Netatmo API Golang bindings

Go Reference Go report card

WIP DO NOT USE

Netatmo API version: 1.1.2

This lib is splitted into several packages in order for the user to only import what matters to him. The main package (this one) handles everything linked to oauth2 (authentification and token refreshing): it allows to create a client which automatically handles the authentification workflow but does not know any Netatmo product APIs. This main client is then to be used with sub package specific products API:

The first thing you should do is read the Netatmo developers guidelines and create an app in order to retreive a client id, a client secret but also setup the application redirect URI if you plan to use the standard oauth2 workflow.

Getting started

First you need to know how you will authenticate against the Netatmo API. There is 2 methods supported:

  • The "authorization code" workflow which is the standard but also the most complex. You will need a reachable webserver (your go program ?) in order to catch the callback URL once the user has been authentified on Netatmo website and allowed your app to access your users data on their behalf. When you plan multi-tenancy (handling multiples users) by opening your service on the internet, this is the one you should consider.
  • The "client credentials" workflow, simplier, which allow to get an oauth2 token directly by submiting a user/password combination. Perfect for personnal projects, you should still consider to not use your admin/main account but instead create a secondary account with limited privileges, invite it to your main account and then use the secondary account credentials for your program.

Both methods require an oauth2 configuration which you configure like this:

oauthConfig := netatmo.GenerateOAuth2Config(netatmo.OAuth2BaseConfig{
    ClientID:     ClientID, // retreived in your developers app netatmo page
    ClientSecret: SecretID, // retreived in your developers app netatmo page
    Scopes: []string{
        netatmo.ScopeStationRead,
        netatmo.ScopeThermostatRead,
    }, // see the the scopes.go file for all availables scopes
    RedirectURL: "https://yourdomain.tld/netatmocallback", // set up in your developers app netatmo page
})

If you do not plan to use the authorization code workflow, I still recommend you to setup an arbitraty redirect URL (ideally on a domain you own, even if the endpoint is fake) and set it up on your oauth config as well in order for netatmo to match your app profil on theirs servers (I got silent fails during testing because of it).

Client credentials

Simply init the client with username/password:

client, err := netatmo.NewClientWithClientCredentials(context.TODO(), config, username, password, nil)
if err != nil {
    panic(err)
}

Keep in mind that should never used/store your admin account credentials ! (prefer a low privileges, secondary account)

Authorization code

As described by the Netatmo website, oauth2 three-legged workflow needs several steps.

Getting the authorization URL

To get valid tokens to act as your user, you will need to redirect him on the oauth2 authorization page at netatmo servers:

userRandomID := "somethingrandomthatyouwillstore"
authURL := netatmo.GetUserAuthorizationURL(oac oauth2.Config, userRandomID string)

This will yield you an URL in order to your user to validate on the netatmo servers that it allows your app to act on its behalf for the scopes you declared on your oauth configuration. Unfortunatly, the URL is not reachable by GET but POST only; this POST will then yield the real GETable URL in the Location HTTP header by issuing a 302 response. This can be fine if you want to redirect your user on the netatmo servers using browser side javascript but can be anoying if you need to recover (and for example print) the real GETable URL yourself. This lib contains an helper function which will do it for you if you need it to:

userRandomID := "somethingrandomthatyouwillstore"
authURL, err := netatmo.RetreiveUserRealAuthorizationURL(context.TODO(), oauthConfig, userRandomID, nil)
if err != nil {
    panic(err)
}
fmt.Println(authURL)
Receiving the auth code from Netatmo

Once your user validates the scopes and your app on the Netatmo Oauth2 page, the Netatmo servers will send an authentication code back to your redirect URL along with the userID you setup (allowing you to match which user this auth code belongs to). You will need an HTTP handler to catch them as they are passed as query parameters: [YOUR_REDIRECT_URI]?state=[USER_RANDOM_ID_YOU_PROVIDED]&code=[NETATMO_GENERATED_CODE]

Once retreived, you can use the uniq ID in the state query parameter to match the user you assigned it to and use the auth code to retreive the oauth2 tokens and finally have the authenticated netatmo client:

authedClient, err := netatmo.NewClientWithAuthorizationCode(context.TODO(), oauthConfig, netatmoGeneratedCode, nil)
if err != nil {
    panic(err)
}

The lib will handle the oauth2 process, retreives the oauth2 tokens from this auth code and yield an authenticated client.

Restoring a client from tokens

Once initialized, no matter if it was from the client credentials or authorization code workflow, you can (and should) store the oauth2 tokens somewhere in order for you to re init an auth client without redoing the whole auth process.

To retreive a client's oauth2 tokens simply do:

tokens := authedClient.GetTokens()

This will yield you a struct with JSON tags. Store it anyway you like. Then in order to restore a client, restore this struct and directly init the client:

tokens := retreivedSavedTokens(user)
authedClient, err := netatmo.NewClientWithTokens(context.TODO(), oauthConfig, tokens, nil)
if err != nil {
    panic(err)
}

I have my authenticated client, now what ?

You can now init products API clients using the authedClient that will handle API requests authentication and oauth2 tokens auto refresh.

Documentation

Index

Constants

View Source
const (
	// NetatmoAPIAuthURL is the netatmo oauth2 authorize URL
	NetatmoAPIAuthURL = "https://api.netatmo.com/oauth2/authorize"
	// NetatmoAPITokenURL is the netatmo oauth2 token URL
	NetatmoAPITokenURL = "https://api.netatmo.com/oauth2/token"
)
View Source
const (
	// ScopeStationRead - to retrieve weather station data (Getstationsdata, Getmeasure)
	ScopeStationRead = "read_station"
	// ScopeThermostatRead - to retrieve thermostat data (Homestatus, Getroommeasure...)
	ScopeThermostatRead = "read_thermostat"
	// ScopeThermostatWrite - to set up the thermostat (Synchomeschedule, Setroomthermpoint...)
	ScopeThermostatWrite = "write_thermostat"
	// ScopeCameraRead - to retrieve Smart Indoor Cameradata (Gethomedata, Getcamerapicture...)
	ScopeCameraRead = "read_camera"
	// ScopeCameraWrite - to inform the Smart Indoor Camera that a specific person or everybody has left the Home (Setpersonsaway, Setpersonshome)
	ScopeCameraWrite = "write_camera"
	// ScopeCameraAccess - to access the camera, the videos and the live stream
	ScopeCameraAccess = "access_camera"
	// ScopePresenceRead - to retrieve Smart Outdoor Camera data (Gethomedata, Getcamerapicture...)
	ScopePresenceRead = "read_presence"
	// ScopePresenceAccess - to access the camera, the videos and the live stream
	ScopePresenceAccess = "access_presence"
	// ScopeSmokeDetectorRead - to retrieve the Smart Smoke Alarm informations and events (Gethomedata, Geteventsuntil...)
	ScopeSmokeDetectorRead = "read_smokedetector"
	// ScopeHomeCoachRead - to read data coming from Smart Indoor Air Quality Monitor (gethomecoachsdata)
	ScopeHomeCoachRead = "read_homecoach"
)
View Source
const (

	// NetatmoAPIBaseURL is the base URL for all API calls
	NetatmoAPIBaseURL = "https://api.netatmo.com/api/"
)

Variables

This section is empty.

Functions

func GenerateOAuth2Config

func GenerateOAuth2Config(conf OAuth2BaseConfig) (oac oauth2.Config)

GenerateOAuth2Config generates a complete OAuth2 config for the Netatmo API

func GetUserAuthorizationURL

func GetUserAuthorizationURL(oac oauth2.Config, uniqID string) (userAuthURL string)

GetUserAuthorizationURL returns the Netatmo OAuth2 standard URL for user authorization. Unfortunately, Netatmo requires you to access it with POST, in order for the Netatmo servers to answer back the real URL with a 302. This can be annoying if you want to show/transmit your user a simple GET url: in that case, you can use the RetreiveUserRealAuthorizationURL() function in order to make the POST for you and retrieve the real GET generated URL by the Netatmo servers. See step 1 of https://dev.netatmo.com/apidocumentation/oauth#authorization-code

func RetreiveUserRealAuthorizationURL

func RetreiveUserRealAuthorizationURL(ctx context.Context, oac oauth2.Config, uniqID string,
	customClient *http.Client) (userRealAuthURL string, err error)

RetreiveUserRealAuthorizationURL is an helper to retrieve the real auth URL you must redirect your user to in order for him to allow your app and trigger your redirect URL set in your app profil. THe returned URL is GETabble. customClient can be nil.

Types

type AuthenticatedClient

type AuthenticatedClient interface {
	ExecuteNetatmoAPIRequest(ctx context.Context, method, endpoint string, urlValues url.Values,
		body io.Reader, destination interface{}) (http.Header, RequestStats, error)
	GetTokens() oauth2.Token
}

AuthenticatedClient represents a Netatmo API client needed by the subpackages to query the API. This package provides a reference implementation, see below.

func NewClientWithAuthorizationCode

func NewClientWithAuthorizationCode(ctx context.Context, oac oauth2.Config, authCode string,
	customClient *http.Client) (client AuthenticatedClient, err error)

NewClientWithAuthorizationCode returns an initialized and ready to use Netatmo API client. authCode is generated by Netatmo once the client has accepted the access thru the auth URL (see GetOfflineAuthURL()). Matches to the 4th step of the OAuth2 autorization code grant type: https://dev.netatmo.com/apidocumentation/oauth#authorization-code . customClient can be nil.

func NewClientWithClientCredentials

func NewClientWithClientCredentials(ctx context.Context, oac oauth2.Config, username, password string,
	customClient *http.Client) (client AuthenticatedClient, err error)

NewClientWithClientCredentials returns an initialized and ready to use Netatmo API client. https://dev.netatmo.com/apidocumentation/oauth#client-credential customClient can be nil

func NewClientWithTokens

func NewClientWithTokens(ctx context.Context, oac oauth2.Config, previousTokens *oauth2.Token,
	customClient *http.Client) (client AuthenticatedClient, err error)

NewClientWithTokens allows to restore an already authenticated client with saved tokens. To check how to retrieve a client tokens, check GetTokens(). customClient can be nil

type Controller

type Controller struct {
	// contains filtered or unexported fields
}

Controller can act as a netatmo API Client. Do not instantiate directly, use ExecuteNetatmoAPIReaquest(), NewClientWithClientCredentials() or NewClientWithToken() instead.

func (*Controller) ExecuteNetatmoAPIRequest

func (c *Controller) ExecuteNetatmoAPIRequest(ctx context.Context, method, endpoint string,
	urlValues url.Values, body io.Reader, destination interface{}) (headers http.Header,
	rs RequestStats, err error)

ExecuteNetatmoAPIRequest takes care of all the HTTP logic as well as JSON parsing and error handling

func (*Controller) GetTokens

func (c *Controller) GetTokens() (tokens oauth2.Token)

GetTokens returns a copy of the client tokens. Might not be safe to call to use while the client is used, use to save state once the client is not used any more (ex: stopping/shutdown)

type HTTPStatusGenericError

type HTTPStatusGenericError struct {
	HTTPCode    int    ``
	NetatmoCode int    `json:"code"`
	Message     string `json:"message"`
}

HTTPStatusGenericError is used to represent a non 200 HTTP error

func (HTTPStatusGenericError) Error

func (hsge HTTPStatusGenericError) Error() string

type HTTPStatusOKError

type HTTPStatusOKError struct {
	Code     int    `json:"code"`
	DeviceID string `json:"id"`
}

HTTPStatusOKError represents a single API error while the HTTP request has returned 200

func (HTTPStatusOKError) Error

func (hsoe HTTPStatusOKError) Error() string

type HTTPStatusOKErrors

type HTTPStatusOKErrors []HTTPStatusOKError

HTTPStatusOKErrors represents https://dev.netatmo.com/apidocumentation/general#status-ok

func (HTTPStatusOKErrors) Error

func (hsoes HTTPStatusOKErrors) Error() string

type OAuth2BaseConfig

type OAuth2BaseConfig struct {
	ClientID     string
	ClientSecret string
	Scopes       []string // see the string constants begenning with "ScopeXXX"
	// RedirectURL must match the one set on your application profil on the dev portal
	// mandatory if you are using authorization code workflow, leave empty for client credentials workflow
	RedirectURL string
}

OAuth2BaseConfig contains basic OAuth2 informations allowing to build the full conf with GenerateOAuth2Config().

type RequestStats

type RequestStats struct {
	Status     string
	TimeExec   time.Duration
	TimeServer time.Time
}

RequestStats contains the request stats sent by the netatmo API servers after a query

type RequestStatusOKPayload

type RequestStatusOKPayload struct {
	Body       *json.RawMessage   `json:"body"`
	Errors     HTTPStatusOKErrors `json:"errors"`
	Status     string             `json:"status"`      // ex: "ok"
	TimeExec   float64            `json:"time_exec"`   // ex: 0.1116938591003418
	TimeServer int64              `json:"time_server"` // ex: 1621453691
}

RequestStatusOKPayload is the generic payload received from the netatmo API servers

type UnexpectedHTTPCode

type UnexpectedHTTPCode struct {
	HTTPCode int
	Body     []byte
}

UnexpectedHTTPCode will be used for any unexpected HTTP error codes

func (UnexpectedHTTPCode) Error

func (uhc UnexpectedHTTPCode) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL