This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Ondilo spa/pool monitor
#1
Hello,

Please, can anyone help with connecting a Ondilo spa monitor.

Authentication is to difficult for me Wink

Here the api info how to auth: https://interop.ondilo.com/docs/api/cust...entication
And here's the general api info: https://interop.ondilo.com/docs/api/customer/v1/

I have a app username and password but i see nothing in the doc how to get access to the api with that user&pwd
Reply
#2
Ok, i give it a try

my code: 
Code:
local usr = "my@email.com"
local pwd = "mypwd"
local uri = "https%3A%2F%2Fyour.app.url%2Fauthorize" -- there's no url redirect, so how to handle this
local state = "c7d37ee542c7'" -- random

---------------------------- change nothing after this line -------------------------------

local apiHost = "https://interop.ondilo.com"
local apiUrl = apiHost .. "/api/customer/v1"
local endpointToken = "/oauth2/token"
local endpointAuth = "/oauth2/authorize"

-- init
if not init then
  require('socket.http')
  require('ltn12')
  require('json')
  socket.http.TIMEOUT = 5
  init = true
end

function getAuth()
  url = apiHost..endpointAuth..'?client_id=customer_api&response_type=code&redirect_uri='..uri..'&scope=api&state='..state
  method = "POST"
  local response_body = { } 
  local payload = '{"login":"'..usr..'","password":"'..pwd..'","locale":"nl","proceed":"Authorize"}'
 
  local res, code, response_headers, status = socket.http.request
  {
    url = url,
    method = method,
    headers =
    {
      ["Accept"] = "application/json",
      ["Accept-Charset"] = "utf-8",
      ["Accept-Encoding"] = "gzip-deflate",
      ["Content-Type"] = "application/json",
      ["Content-Length"] = payload:len()
    },
    source = ltn12.source.string(payload),
    sink = ltn12.sink.table(response_body)
  }
  log(res, code, response_headers, status)
  log(response_body)
end


getAuth()

My return:

Code:
* arg: 1
  * number: 1
* arg: 2
  * number: 200
* arg: 3
  * table:
   ["server"]
    * string: Apache/2.4.29 (Ubuntu)
   ["content-type"]
    * string: text/html;charset=UTF-8
   ["connection"]
    * string: close
   ["access-control-allow-methods"]
    * string: GET, POST, PUT, PATCH, DELETE, OPTIONS
   ["access-control-allow-headers"]
    * string: X-Requested-With, Content-Type, Accept, Origin, Authorization
   ["cache-control"]
    * string: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
   ["pragma"]
    * string: no-cache
   ["content-length"]
    * string: 4047
   ["date"]
    * string: Wed, 21 Oct 2020 17:45:44 GMT
   ["vary"]
    * string: Accept-Encoding
   ["access-control-allow-origin"]
    * string:
   ["access-control-allow-credentials"]
    * string: true
* arg: 4
  * string: HTTP/1.1 200 OK

but how to get the code that is normally send to the url given in the var uri (see above) or is my code wrong?

documentation is here: https://interop.ondilo.com/docs/api/cust...entication

if i do the same in a browser, my return url looks like:
https://your.app.url/authorize?code=6440...a88c&state=c7d37ee542c7
Reply
#3
What do you get in response body? You should also remove Accept-Encoding header otherwise body will be compressed with gzip instead of plain text.
Reply
#4
(22.10.2020, 06:30)admin Wrote: What do you get in response body? You should also remove Accept-Encoding header otherwise body will be compressed with gzip instead of plain text.

I see the plain website, i think the form is not filled and executed. But how to do this.
same source as: 
https://interop.ondilo.com/oauth2/author...d44ef532c5
Code:
* table:
[1]
  * string:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Ondilo authorization page</title>
        <style>
            * {
                margin: 10px 4px 10px 4px;
            }
            .box {
                width: 340px;
                margin: auto;
                border: 1px solid rgb(41, 165, 155);
            }
            .banner {
                margin: 0px;
                background-color: rgb(41, 165, 155);
                height: 50px;
                position: relative;
            }
            .ico {
                position: absolute;
                top: 3px;
                left: 4px;
            }
            .ondilo {
                position: absolute;
                top: -1px;
                right: 4px;
            }
            strong {
                margin: 0px;
            }
            label {
                margin-top: 8px;
                margin-bottom: 0px;
                display: block;
                font-size: 0.8em;
                color: gray;
            }
            input, select {
                font-size: 1.2em;
            }
            input[type=text], input[type=password], select {
                width: 312px;
            }
            input[type=submit] {
                width: 322px;
                display: block;
                margin-bottom: 25px;
            }
            .authorize {
                color: white;
                background-color: rgb(41, 165, 155);
                margin-bottom: 10px;
                margin-top: 25px;
                border-color: rgb(41, 165, 155);
            }
            p {
                text-align: center;
                margin-top: 25px;
                margin-bottom: 25px;
            }
            .warning p, .warning ul {
                margin: 0 0 0 0;
                line-height: 15px;
            }
        </style>
    </head>
    <body>
        <div class="box">
            <div class="banner">
                <img src="/images/ico.png" alt="ICO" title="ICO" class="ico"/
[2]
  * string: >
                <img src="/images/ondilo.png" alt="Ondilo" title="Ondilo" class="ondilo"/>
            </div>
                        <form method="post">
                <p>Sign in to allow <strong>customer_api</strong> to connect to your <strong>Ondilo</strong> account and access the related data</p>
                <label for="login">Your email address</label><input type="text" name="login" ><br/>
                <label for="pasword">Your password</label><input type="password" name="password">
                <label for="locale">Your language</label><select id="locale" name="locale">
                    <option value="en">English</option>
                    <option value="fr">Français</option>
                    <option value="es">Español</option>
                    <option value="de">Deutsch</option>
                    <option value="pt">Português</option>
                    <option value="nl">Nederlands</option>
                    <option value="it">Italiano</option>
                    <option value="cs">Čeština</option>
                    <option value="hu">Magyar</option>
                    <option value="sv">Svenska</option>
                    <option value="ro">Română</option>
                </select>
                <div class="warning">
                    <p>
                        We remind that ICO takes measures every hour.</br>
                        In order to avoid excessive load of our servers, the requests to the Ondilo Customer API are limited to the following per user quotas :
                        <ul>
                            <li>5 requests per second</li>
                            <li>30 requests per hour</li>
                        </ul>
                    </p>               
                </div>
                <input type="submit" name="proceed" value="Authorize" class="authorize">
                <input type="submit" name="proceed" value="Cancel">
            </form>
        </div>
    </body>
</html>
Reply
#5
For auth you need to use standard POST, not JSON:
Code:
function encodepost(t)
  local res = {}
  local esc = require('socket.url').escape

  for k, v in pairs(t) do
    res[ #res + 1 ] = esc(k) .. '=' .. esc(v)
  end

  return table.concat(res, '&')
end

payload = encodepost({
  login = usr,
  password = pwd,
  locale = "nl",
  proceed = "Authorize",
})

For this request Accept/Content-Type headers are not needed. Add redirect = false to the request table to catch the redirect URL instead of following it.
Reply
#6
(22.10.2020, 07:14)admin Wrote: For auth you need to use standard POST, not JSON:

For this request Accept/Content-Type headers are not needed. Add redirect = false to the request table to catch the redirect URL instead of following it.
 
mmm same result with changed code

Code:
function getAuth()
  function encodepost(t)
    local res = {}
    local esc = require('socket.url').escape
    for k, v in pairs(t) do
      res[ #res + 1 ] = esc(k) .. '=' .. esc(v)
    end
    return table.concat(res, '&')
  end
 
  url = apiHost..endpointAuth..'?client_id=customer_api&response_type=code&redirect_uri='..uri..'&scope=api&state='..state
  method = "POST"
  local response_body = {} 
  local payload = encodepost({
    login = usr,
    password = pwd,
    locale = "nl",
    proceed = "Authorize",
  })
  local res, code, response_headers, status = socket.http.request
  {
    url = url,
    method = method,
    redirect = false,
    headers =
    {
      ["Content-Length"] = #payload
    },
    source = ltn12.source.string(payload),
    sink = ltn12.sink.table(response_body)
  }
  if res and code == 200 then
    log(response_body)
    log(res, code, response_headers, status)
  else
    log(res, code, response_headers, status)
  end
end
Reply
#7
Header is missing:
Code:
["Content-Type"] = "application/x-www-form-urlencoded"
Reply
#8
(22.10.2020, 08:23)admin Wrote: Header is missing:
Code:
["Content-Type"] = "application/x-www-form-urlencoded"

 yes, now i get the authorization code (authCode).

Next is exchaging the authorization code for an access token, see my code try below.
Doc: https://interop.ondilo.com/docs/api/cust...n-exchange
Code:
-- exchange the authorization code for an access token
  if authCode then
    url = apiHost..endpointToken..'?code='..authCode..'&grant_type=authorization_code&client_id=customer_api&redirect_uri='..uri
    method = "POST"
    local response_body = {}
    local res, code, response_headers, status = socket.http.request
    {
      url = url,
      method = method,
      headers =
      {
        ["Content-Type"] = "application/x-www-form-urlencoded"
      },
      source = ltn12.source.string(''),
      sink = ltn12.sink.table(response_body)
    }
    log(res, code, response_headers, status)
  end

but as usually i received an error  Big Grin
Code:
* arg: 1
  * number: 1
* arg: 2
  * number: 400
* arg: 3
  * table:
   ["server"]
    * string: Apache/2.4.29 (Ubuntu)
   ["content-type"]
    * string: application/json
   ["connection"]
    * string: close
   ["access-control-allow-methods"]
    * string: GET, POST, PUT, PATCH, DELETE, OPTIONS
   ["access-control-allow-headers"]
    * string: X-Requested-With, Content-Type, Accept, Origin, Authorization
   ["cache-control"]
    * string: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
   ["pragma"]
    * string: no-cache
   ["date"]
    * string: Thu, 22 Oct 2020 11:14:32 GMT
   ["content-length"]
    * string: 97
   ["access-control-allow-origin"]
    * string: *
   ["access-control-allow-credentials"]
    * string: true
* arg: 4
  * string: HTTP/1.1 400 Bad Request
Reply
#9
You must provide "Content-Length" header for POST requests. Move your GET variables to POST payload:
Code:
url = apiHost..endpointToken
payload = encodepost({
  code = authCode,
  grant_type = 'authorization_code',
  client_id = 'customer_api',
  redirect_uri = uri,
})
Reply
#10
many thanks to admin for the support.

here my first version to get the latest sensor readings for one pool/spa.

We remind that ICO takes measures every hour.
In order to avoid excessive load of our servers, the requests to the Ondilo Customer API
are limited to the following per user quotas :
5 requests per second
30 requests per hour

Access tokens have a lifetime of one hour, while refresh tokens are non-expiring.

Attached Files
.lua   ondilo.lua (Size: 4.25 KB / Downloads: 28)
Reply
#11
One small improvement, you should use the whole table instead of only the first element. If the JSON response is large enough then the response table will have more than 1 element. Use this instead:
Code:
data = json.decode(table.concat(response_body))
Reply
#12
Hello

Could you give some instructions with examples how to get sensors data please  Shy
I am not familiar with scripting, but can multiply solution from example.

Thank you
Reply
#13
First you need to set usr and pwd variables in the script. Then run this function to get the ID of your pool:
Code:
log( getPoolInfo() )

Then you can request the sensor state by calling getSensorInfo:
Code:
data = getSensorInfo()
log(data)
When the data format is known the script can be modified to write sensor state to objects.
Reply
#14
(24.05.2021, 09:38)admin Wrote: First you need to set usr and pwd variables in the script. Then run this function to get the ID of your pool:
Code:
log( getPoolInfo() )

Then you can request the sensor state by calling getSensorInfo:
Code:
data = getSensorInfo()
log(data)
When the data format is known the script can be modified to write sensor state to objects.

Hello

I have got sensors data, thank you, could you make example of a function for the PUT method?

Thank you
Reply
#15
PUT is the same as POST, just change the method field accordingly
Reply


Forum Jump: