LUA HTTP Request with Content-Type: application/x-www-form-urlencoded

Hi all,

I’m currently in the process of coding a Lua sequence to fetch data from my Netatmo Weather Station (Smart Rain Gauge | Netatmo).

Unfortunately it is a bit tricky to implement, since the requests have to be made with a proper oauth accesstoken.

As long as I do have a valid access token I’m able to fetch data from the Netatmo API.

Since the Netatmo accesstoken at somepoint expires, I need to fetch a new access token via the refreshtoken. See documentation: Netatmo Connect | Authentication

For this api call I have to send a POST Request. Unfortunately this is the problematic part I’m currently stuck.

I’m unable to set the http content-type to:

Content-Type: application/x-www-form-urlencoded;charset=UTF-8

Question: Could it be that the http function only supports application/json?

My code

should fetch a new accesstoken in case the regular requests no more deliver data:

device_id = "xyz"
request_url = "https://api.netatmo.com/api/getstationsdata?device_id=" .. device_id .. "&get_favorites=false"

netatmo_accesstoken = env("netatmo_accesstoken")

-- fake access token to force, wrong token...
--netatmo_accesstoken = "sdfasfasdf"

netatmo_refreshtoken = env("netatmo_refreshtoken")

netatmo_clientid = env("netatmo_clientid")
netatmo_clientsecret = env("netatmo_clientsecret")

toast(netatmo_refreshtoken)
toast(netatmo_accesstoken)
toast(netatmo_clientid)
toast(netatmo_clientsecret)

env("netatmo_rain_mm_24h")

toast("Getting data from " .. request_url)
response, error = http({
    url=request_url,
    headers={
    Authorization='Bearer ' .. netatmo_accesstoken,
    Accept="application/json"
  }
})
if error then
  send_message("info", "Unknown error: " .. inspect(error))
else
  -- The `response` object has three properties:
    -- `status`: Number               - Response code. Example: 200.
    -- `body`: String                 - Response body. Always a string.
    -- `headers`: Map<String, String> - Response headers.
end

toast(response.status)
toast(inspect(response.headers))


-- toast(payload)

if response.status > 400 then

  --send_message("warning", "Netatmo api response was error 401, accesstoken probably expired. Fetching a new one.")
  response, error = http({
      url='https://api.netatmo.com/oauth2/token',
      method="POST",
      headers={
        Content-Type="application/x-www-form-urlencoded;charset=UTF-8"
      },
      body="client_id=xyz&client_secret=xyz&grant_type=refresh_token&refresh_token=xyz"
  })
  if error then
    -- The `error` object is reserved for non-HTTP errors.
    -- Example: missing URL.
    -- `error` will be `nil` if no issues are found.
    send_message("info", "Use of refreshtoken failed, error: " .. inspect(error))
  else

  end
  toast(response.status)
  toast(response.headers)
  toast(response.body)
  refreshtoken_response = json.decode(response.body)

  send_message("info", "Refreshtoken response: " .. refreshtoken_response)
  env("netatmo_refreshtoken", refreshtoken_response.refresh_token)
  env("netatmo_accesstoken", refreshtoken_response.access_token)



else
  toast("Access token is still valid")
end


netatmo_data = json.decode(response.body)

rain_mm_24h = netatmo_data.body.devices[1].modules[3].dashboard_data.sum_rain_24

toast("Netatmo messured rain in the last 24hours: " .. tostring(rain_mm_24h) .. "mm")
send_message("info", "Netatmo messured rain in the last 24hours: " .. tostring(rain_mm_24h) .. "mm")

-- Store it in Variable
env("netatmo_rain_mm_24h", tostring(rain_mm_24h))

I’ve tried to set the Content-Type in the headers… But this produces a Lua Error:

Failed to execute command: Lua code is possibly invalid: [{51, :luerl_parse, [~c"syntax error before: “, ~c”‘=’"]}]

For reference:

Fetching a new accesstoken via Postman works:

Fetching it in Postman with application/json will as expected fail too:

Thanks for some inputs in advance :nerd_face:

:smiley: :smiley: :smiley:
After lots of try and error - searching through the docs I was able to find the solution.

Declaring the header differently and Lua accepted the code :slight_smile:
Inspired from sample code: Identify Plants | FarmBot Developer Documentation

  headers = {}
  headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8"

    send_message("error", "Netatmo api response was error " .. tostring(response.status) .. ", accesstoken probably expired. Fetching a new one.")
    response, error = http({
        url='https://api.netatmo.com/oauth2/token',
        method="POST",
        headers=headers,
        body="client_id=" .. netatmo_clientid .. "&client_secret=" .. netatmo_clientsecret .. "&grant_type=refresh_token&refresh_token=" .. netatmo_refreshtoken
    })

I’m still a bit curious why my initial Lua code did not work. Is it because of the dash in “Content-Type”?

1 Like

@farmS1m good job !!

From the Lua 5.3 Ref. Man.

  • Names (also called identifiers) in Lua can be any string of letters, digits, and underscores, not beginning with a digit and not being a reserved word.