Module netatmo
Netatmo API library to access the REST api.
This library implements the session management and makes it easy to access individual endpoints of the API.
To create a valid session, access and refresh tokens are required. This requires the user to login to the NetAtmo site and authorize the application. The library doesn't provide a full flow, but does provide the necessary functions to obtain the required tokens.
The get_authorization_url method will return the url to navigate to to start the
OAuth2 flow. Once authorized on the NetAtmo site, it will redirect the users webbrowser
to the callback_url
provided when creating the session. This url should be handled
by a webserver, and the authorize method should be called with the results from the callback url.
To manually obtain the tokens, without a webserver, use a callback_url that is certain to fail
(the default will probably do). Then get the url using get_authorization_url
and instruct the user to visit that url and authorize the application. Once authorized,
the user will be redirected to the callback_url
provided when creating the session.
Since it is a bad URL the redirect will fail with an error in the browser.
Instruct the user to collect the required informatiuon ("state" and "code" parameters) from the
url-bar in the browser and use them to call the authorize method.
If you manually retrieved a refresh token, then it can be set by calling set_refresh_token.
The session can be kept alive using the keepalive method. At the time of writing access tokens have a validity of 10800 seconds (3 hours). Internally a clock-skew of 5 minutes is used, so the library considers the token to be expired 5 minutes before the actual expiration time.
Info:
- Copyright: 2017-2023 Thijs Schreijer
- Release: Version 0.2.0, Library to acces the Netatmo API
- License: netatmo.lua is free software under the MIT/X11 license.
- Author: Thijs Schreijer, https://www.thijsschreijer.nl
Tables
netatmo | The module table containing some global settings and constants. |
Generic functions
new (options) | Creates a new Netatmo session instance. |
request (path, method[, headers[, query[, body]]]) | Performs a HTTP request on the Netatmo API. |
rewrite_error ([expected=nil], ...) | Rewrite errors to Lua format (nil+error). |
Session management functions
authorize (state[, code]) | Authorizes the session. |
authorized () | Check if the session has been authorized. |
get_authorization_url () | Gets the authorization url for the user to navigate to to start the OAuth2 flow. |
keepalive ([delay_on_error=60]) | Keeps the session alive. |
logout () | Logs out of the current session. |
set_refresh_token (refresh_token) | Sets the refresh token to use for the session. |
API specific functions
get_modules_data ([device_id[, get_favorites=false[, no_warnings=false]]]) | Gets device data, but returns by (sub)module instead of station. |
get_stations_data ([device_id[, get_favorites=false[, no_warnings=false]]]) | Gets device data. |
Tables
- netatmo
-
The module table containing some global settings and constants.
Fields:
- https
This is a function set on the module table, such that it can
be overridden by another implementation (eg. Copas). The default implementation
uses the LuaSec one (module
ssl.htps
). - log Logger is set on the module table, to be able to override it. Default is the LuaLogging default logger.
- DEVICE_TYPES
A lookup table to convert the type ID to a description.
Eg.
"NAModule4"
->"Additional indoor module"
. - ERR_MUST_AUTHORIZE Error message returned when a call is made without a valid session. This indicates the user must login (again) to authorize the application. See also authorized.
- ERR_REFRESH_IN_PROGRESS Error message returned when a refresh is (possibly implict) attempted, but another refresh is already in progress. This is to prevent multiple refreshes from happening simultaneously. When this happens retry shortly after.
- https
This is a function set on the module table, such that it can
be overridden by another implementation (eg. Copas). The default implementation
uses the LuaSec one (module
Generic functions
- new (options)
-
Creates a new Netatmo session instance.
Only OAuth2
grant_type
supported is "authorization_code", for which the refresh-token is required. Therefresh_token
can be specified in the options table, or set later using the set_refresh_token method. The token can also be retreived by getting the callback url from get_authorization_url and after calling it, pass on the results to the authorize method.Parameters:
- options options table with the following options
- client_id string the client_id to use for accessing the API
- client_secret string the client_secret to use for accessing the API
- refresh_token string the user provided refresh-token to use for accessing the API (optional)
- scope array the scope(s) to use for accessing the API. Defaults to all read permissions. (optional)
- persist
function
callback function called as
function(session, refresh_token)
whenever the refresh token is updated. This can be used to store the refresh token across restarts. NOTE: the refresh_token can be nil, in case of a logout. (optional) - callback_url string the OAuth2 callback url (default "https://localhost:54321")
Returns:
-
netatmo session object
Usage:
local netatmo = require "netatmo" local must_login = false local nasession = netatmo.new { client_id = "abcdef", client_secret = "xyz", scope = { "read_station", "read_thermostat" }, callback_url = "https://localhost:54321/", --> callback called with 'state' and 'code' parameters } user_url = nasession:get_authorization_url() --> have user navigate to this url and authorize -- now authorize using the 'state' and 'code' parameters assert(nasession:authorize(state, code)) local data, err = nasession:get_modules_data() if not data if err == netatmo.ERR_MUST_AUTHORIZE then -- tell user to login again, tokens expired for some reason end -- handle error end
- options options table with the following options
- request (path, method[, headers[, query[, body]]])
-
Performs a HTTP request on the Netatmo API.
It will automatically inject authentication/session data. If the session has
expired it will be renewed.
NOTE: if the response_body is json, then it will be decoded and returned as a Lua table.
Parameters:
- path string the relative path within the API base path
- method string HTTP method to use
- headers table header table (optional)
- query table query parameters (will be escaped) (optional)
- body table or string if set the "Content-Length" will be added to the headers. If a table, it will be send as JSON, and the "Content-Type" header will be set to "application/json". (optional)
Returns:
ok
,response_body
,response_code
,response_headers
,response_status_line
ornil + err
Usage:
local netatmo = require "netatmo" local nasession = netatmo.new { client_id = "abcdef", client_secret = "xyz", refresh_token = "123", scope = { "read_station", "read_thermostat" }, callback_url = "https://localhost:54321/", } local headers = { ["My-Header"] = "myvalue" } local query = { ["param1"] = "value1" } local ok, response_body, status, headers, statusline = nasession:request("/api/attributes", "GET", headers, query, nil) if not ok then if response_body == netatmo.ERR_MUST_AUTHORIZE then -- tell user to login again, tokens expired for some reason end -- handle error end
- rewrite_error ([expected=nil], ...)
-
Rewrite errors to Lua format (nil+error).
Takes the output of the request function and validates it for errors;
- nil+err
- body with "error" field (json object)
- mismatch in expected status code (a 200 expected, but a 404 received)
This reduces the error handling to standard Lua errors, instead of having to validate each of the situations above individually.
If the status code is a 401 or 403, then the access token will be cleared.
Parameters:
- expected number expected status code, if nil, it will be ignored (default nil)
- ... same parameters as the request method
Returns:
-
nil+err or the input arguments
Usage:
local netatmo = require "netatmo" local nasession = netatmo.new { client_id = "abcdef", client_secret = "xyz", refresh_token = "123", scope = { "read_station", "read_thermostat" }, callback_url = "https://localhost:54321/", } -- Make a request where we expect a 200 result local ok, response_body, status, headers, statusline = nasession:rewrite_error(200, nasession:request("/some/thing", "GET")) if not ok then if response_body == netatmo.ERR_MUST_AUTHORIZE then -- tell user to login again, tokens expired for some reason end -- handle error -- a 404 will also follow this path now, since we only want 200's end
Session management functions
- authorize (state[, code])
-
Authorizes the session.
This method is called from the callback url, and should be provided with the
state
andcode
values from the callback url. It will fetch the initial tokens, resulting in an access and refresh token if successful. It can also be called with a single argument which is then the request(line) from the callback url. It will then extract thestate
andcode
values from the url.Parameters:
- state string the state value from the callback url, or the callback request, request-url including query args, or the first request line.
- code
string
the code value from the callback url (required if
state
is not request-data) (optional)
Returns:
true
ornil + err
(this function will never return thenetatmo.ERR_MUST_AUTHORIZE
error) - authorized ()
-
Check if the session has been authorized.
Returns:
-
boolean
true
if authorized,false + netatmo.ERR_MUST_AUTHORIZE
otherwise - get_authorization_url ()
-
Gets the authorization url for the user to navigate to to start the OAuth2 flow.
The resulting "code" and "state" values from the callback url can be used
to call the authorize method.
Returns:
-
string
the url to navigate to
- keepalive ([delay_on_error=60])
-
Keeps the session alive.
Keeps the session alive by refreshing the access token. If frequent calls are made, then the
refresh should be automatic. If for long periods no calls are made, then this function
can be used to keep the session alive.
Call this function in a loop, delaying each time by the returned number of seconds. It will only refresh the token if required (other calls can also refresh the token making an explicit refresh unnecessary).
Parameters:
- delay_on_error number the number of seconds to delay when an error occurs (to prevent a busy loop). (default 60)
Returns:
-
number of seconds to delay until the next call, or
delay_on_error+err
Usage:
local keepalive_thread = create_thread(function() while true do local delay, err = nasession:keepalive() sleep(delay) end end)
- logout ()
-
Logs out of the current session.
There is no real logout option with this API. Hence this only deletes
the locally stored tokens. This will make any new calls fail, until the user logs in again.
Returns:
true
- set_refresh_token (refresh_token)
-
Sets the refresh token to use for the session.
Parameters:
- refresh_token string the refresh token to use
API specific functions
- get_modules_data ([device_id[, get_favorites=false[, no_warnings=false]]])
-
Gets device data, but returns by (sub)module instead of station.
The returned table is both an array of all modules, as well as a hash-table
in which the same modules are indexed by their ID's for easy lookup.
Parameters:
- device_id string the id (mac-address) of the station (optional)
- get_favorites bool set to true to get the favorites (default false)
- no_warnings bool set to true to skip generating warnings in the logs (default false)
Returns:
-
module list, or nil+err
Usage:
local netatmo = require "netatmo" local nasession = netatmo.new("abcdef", "xyz", "myself@nothere.com", "secret_password") local modules = nasession:get_modules_data() local module = modules["03:00:00:04:89:50"]
- get_stations_data ([device_id[, get_favorites=false[, no_warnings=false]]])
-
Gets device data.
Parameters:
- device_id string the id (mac-address) of the station (optional)
- get_favorites bool set to true to get the favorites (default false)
- no_warnings bool set to true to skip generating warnings in the logs (default false)
Returns:
-
device list + full response, or nil+err
Usage:
local netatmo = require "netatmo" local nasession = netatmo.new("abcdef", "xyz", "myself@nothere.com", "secret_password") local stations, full_response = nasession:get_stations_data()