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.

Husqvarna lawn robot mover API
#1
Hi @all,

I want to connect my Husqvarna lawn robot to LM. 

There is an existing API which is documented here: https://developer.husqvarnagroup.cloud/a...onnect-api

I think I'm not fare away from the goal to start and stop the robot via LM but I struggling with the payload. 

After I registrated to Husqvarna development portal I created a script to find out the mover-id:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'XXX' local clientSecret = 'XXX -- Husqvarna Automower Connect API local mowersEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers' local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         print('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Abrufen der Mower-IDs function getMowerIds()     local response_body = {}     local _, code, _, _ = https.request{         url = mowersEndpoint,         method = 'GET',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna'         },         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)     log(response)         local jsonResponse = json.decode(response)                 for _, mower in ipairs(jsonResponse.data) do             local mowerId = mower.id             -- Verarbeiten Sie die Mower-ID hier nach Bedarf             print('Mower-ID:', mowerId)                     log(mowerId)         end     else         -- Fehlerbehandlung         print('Fehler beim Abrufen der Mower-IDs:', code)     end end -- Beispielaufrufe getAccessToken() getMowerIds()

In the second step I tried to write a script to start and stop the robot:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx local clientSecret = 'xxx' local moverID = 'xxx' -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/ba15739b-0449-411a-9eb9-9ad866362075/actions' local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Senden des Startbefehls function sendStartCommand()     local requestBody = '{"action":"start"}'     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 200 then         -- Erfolgreich gestartet         log('Automower wurde gestartet.')     else         -- Fehlerbehandlung         log('Fehler beim Starten des Automowers:', code)     end end -- Funktion zum Senden des Stopbefehls function sendStopCommand()     local requestBody = '{"name": "stop"}'     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 200 then         -- Erfolgreich gestoppt         log('Automower wurde gestoppt.')     else         -- Fehlerbehandlung         log('Fehler beim Stoppen des Automowers:', code)     end end -- Beispielaufrufe getAccessToken() sendStartCommand()


If I call the script to start the robot I getting the following failures:

Code:
12345678910111213141516
Automover  10.06.2023 15:12:33 * arg: 1   * number: 400 * arg: 2   * nil * arg: 3   * nil * arg: 4   * table:    [1]     * string: {"errors":[{"id":"f211fb25-ae93-44e7-820f-ccb35adb2484","status":"400","code":"illegal.argument","title":"Illegal argument","detail":"Unsupported Media Type. Likely an invalid Content-Type header value. Use 'application/vnd.api+json' or 'application/json' depending on endpoint."}]} Automover  10.06.2023 15:12:33 * arg: 1   * string: Fehler beim Starten des Automowers: * arg: 2   * number: 400

Do you have any idea what I making wrong and need to change? 

Many thanks for your help!

Best Regards
Steffen
Reply
#2
Have you tried doing what the error message suggests - set application/vnd.api+json content-type header? Also check request data format: https://developer.husqvarnagroup.cloud/a...api#readme
Use json.encode instead of raw JSON string or you will run into syntax errors.
Reply
#3
Hi Admin,

many thanks for your help and feedback.

I tried to implement your hints:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx' local clientSecret = 'xxx' -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/ba15739b-0449-411a-9eb9-9ad866362075/actions' local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Senden des Startbefehls function sendStartCommand()     local requestBody = '{"type: 'Start', attributes: { duration: 24 // minutes}'     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 200 then         -- Erfolgreich gestartet         log('Automower wurde gestartet.')     else         -- Fehlerbehandlung         log('Fehler beim Starten des Automowers:', code)     end end -- Funktion zum Senden des Stopbefehls function sendStopCommand()     local requestBody = '{"name": "stop"}'     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 200 then         -- Erfolgreich gestoppt         log('Automower wurde gestoppt.')     else         -- Fehlerbehandlung         log('Fehler beim Stoppen des Automowers:', code)     end end -- Beispielaufrufe getAccessToken() sendStartCommand()

Unfortunately I getting the following failure:

Code:
1
string: {"errors":[{"id":"e9f44309-1dfb-48bf-a0b3-dcab8111951d","status":"500","code":"internal.error","title":"Internal error","detail":""}]}

I think the body might be in the wrong format?!

Code:
1
    local requestBody = '{"type: 'Start', attributes: { duration: 24 // minutes}'

Any ideas how to improve?

Many thanks for your help!

Best Regards
Steffen
Reply
#4
Your payload is not a valid JSON, try : {"type": "Start", "attributes":[{"duration":"24 // minutes"}]}

You can test your payload at https://jsoneditoronline.org/

Are you sure the duration is like "24 // minutes" and not just the value 24 like  {"type": "Start", "attributes":[{"duration":24}]}?
Reply
#5
Try this:
Code:
12345678
local requestBody = json.encode({   data = {     type = 'Start',     attributes = {       duration = 24     }   } })
Reply
#6
Hi Erwin,
hi Admin,

many thanks for your help. This lead into a working solution.

For all guys who like to control (start/stop) the Husqvarna Automower via LM/W4K and a KNX button here is the description:
  • Register for Husqvarna Developer account: https://developer.husqvarnagroup.cloud 
  • Generate an application inside the portal to get the client ID and security ID
  • Extract the Mower ID with the following script:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx local clientSecret = 'xxx' -- Husqvarna Automower Connect API local mowersEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers' local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         print('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Abrufen der Mower-IDs function getMowerIds()     local response_body = {}     local _, code, _, _ = https.request{         url = mowersEndpoint,         method = 'GET',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna'         },         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)     log(response)         local jsonResponse = json.decode(response)                 for _, mower in ipairs(jsonResponse.data) do             local mowerId = mower.id             -- Verarbeiten Sie die Mower-ID hier nach Bedarf             print('Mower-ID:', mowerId)                     log(mowerId)         end     else         -- Fehlerbehandlung         print('Fehler beim Abrufen der Mower-IDs:', code)     end end -- Beispielaufrufe getAccessToken() getMowerIds()

  • Generate an scheduled script every 5 minutes (due to 10000 requests per month restriction) to request the status of the robot:

Code:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx' local clientSecret = 'xxx local moverID = 'xxx -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID..'/actions' local connectEndpointstatus = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung      --   log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Abrufen des Status function statusrequest()     local response_body = {}     local res, code = https.request{         url = connectEndpointstatus,         method = 'GET',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }      local data = json.decode(table.concat(response_body))        status_mower = data.data.attributes.mower.state   log (code, status, headers, response_body, data, status_mower)         if code == 200 then         -- Erfolgreich abgefragt         --log('Automower Status abgefragt.')     else         -- Fehlerbehandlung         --log('Fehler beim Starten des Automowers:', code)     end end --Aufruf der Befehle getAccessToken() statusrequest() -- Status auswerten und ans KNX senden if status_mower == "STOPPED" or status_mower == "RESTRICTED" then      grp.checkwrite('12/4/1', false) end     if status_mower == "IN_OPERATION" then      grp.checkwrite('12/4/1', true)    end
  • Generate an event based script corresponding to the buttons group address:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx' local clientSecret = 'xxx local moverID = 'xxx -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID..'/actions' local connectEndpointstatus = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         --log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Senden des Startbefehls function sendStartCommand()     local requestBody = json.encode({         data = {            type = 'Start',            attributes = {            duration = 400                }                }               })     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 202 then         -- Erfolgreich gestartet         --log('Automower wurde gestartet.')         grp.checkwrite('12/4/1', true)     else         -- Fehlerbehandlung         --log('Fehler beim Starten des Automowers:', code)     end end -- Funktion zum Senden des Stopbefehls function sendStopCommand()     local requestBody = json.encode({       data = {         type = 'ParkUntilNextSchedule',       }     })     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 202 then         -- Erfolgreich gestoppt         --log('Automower wurde gestoppt.')         grp.checkwrite('12/4/1', false)     else         -- Fehlerbehandlung         --log('Fehler beim Stoppen des Automowers:', code)     end end --Aufruf Starten des Automover mover_command = grp.getvalue('12/4/0') if mover_command == true then     getAccessToken()     sendStartCommand() end     --Aufruf Stoppen des Automover mover_command = grp.getvalue('12/4/0')     if mover_command == false then     getAccessToken()     sendStopCommand() end


I hope this is helping somebody. If you have questions please let me know.

Best Regards
Steffen
Reply
#7
Hi @all,

during testing I found an issue with this string compare:

Code:
123
if status_mower == "STOPPED" or status_mover == "RESTRICTED" then      grp.checkwrite('12/4/1', false) end

Can you please help me and give me a hint how to improve it?

Many thanks!

Best Regards 
Steffen
Reply
#8
You have two different variables there - status_mower and status_mover. The docs state that there's a 10000 request limit per month. This means that you should retrieve the status not more often than once in 5 minutes.
Reply
#9
(19.06.2023, 08:00)admin Wrote: You have two different variables there - status_mower and status_mover. The docs state that there's a 10000 request limit per month. This means that you should retrieve the status not more often than once in 5 minutes.

Many thanks, it is working now. I corrected the script and the description in post #6.
Reply
#10
(18.06.2023, 19:34)pioneersteffen Wrote: Hi Erwin,
hi Admin,

many thanks for your help. This lead into a working solution.

For all guys who like to control (start/stop) the Husqvarna Automower via LM/W4K and a KNX button here is the description:
  • Register for Husqvarna Developer account: https://developer.husqvarnagroup.cloud 
  • Generate an application inside the portal to get the client ID and security ID
  • Extract the Mower ID with the following script:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx local clientSecret = 'xxx' -- Husqvarna Automower Connect API local mowersEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers' local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         print('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Abrufen der Mower-IDs function getMowerIds()     local response_body = {}     local _, code, _, _ = https.request{         url = mowersEndpoint,         method = 'GET',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna'         },         sink = ltn12.sink.table(response_body)     } log (code, status, headers, response_body)     if code == 200 then         local response = table.concat(response_body)     log(response)         local jsonResponse = json.decode(response)                 for _, mower in ipairs(jsonResponse.data) do             local mowerId = mower.id             -- Verarbeiten Sie die Mower-ID hier nach Bedarf             print('Mower-ID:', mowerId)                     log(mowerId)         end     else         -- Fehlerbehandlung         print('Fehler beim Abrufen der Mower-IDs:', code)     end end -- Beispielaufrufe getAccessToken() getMowerIds()

  • Generate an scheduled script every 5 minutes (due to 10000 requests per month restriction) to request the status of the robot:

Code:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx' local clientSecret = 'xxx local moverID = 'xxx -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID..'/actions' local connectEndpointstatus = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung      --   log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Abrufen des Status function statusrequest()     local response_body = {}     local res, code = https.request{         url = connectEndpointstatus,         method = 'GET',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }      local data = json.decode(table.concat(response_body))        status_mower = data.data.attributes.mower.state   log (code, status, headers, response_body, data, status_mower)         if code == 200 then         -- Erfolgreich abgefragt         --log('Automower Status abgefragt.')     else         -- Fehlerbehandlung         --log('Fehler beim Starten des Automowers:', code)     end end --Aufruf der Befehle getAccessToken() statusrequest() -- Status auswerten und ans KNX senden if status_mower == "STOPPED" or status_mower == "RESTRICTED" then      grp.checkwrite('12/4/1', false) end     if status_mower == "IN_OPERATION" then      grp.checkwrite('12/4/1', true)    end
  • Generate an event based script corresponding to the buttons group address:

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
local https = require('ssl.https') local json = require('json') -- Husqvarna Authentication API local authEndpoint = 'https://api.authentication.husqvarnagroup.dev/v1/oauth2/token' local clientId = 'xxx' local clientSecret = 'xxx local moverID = 'xxx -- Husqvarna Automower Connect API local connectEndpoint = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID..'/actions' local connectEndpointstatus = 'https://api.amc.husqvarna.dev/v1/mowers/'..moverID local accessToken -- Funktion zum Erhalten eines Zugriffstokens function getAccessToken()     local requestBody = 'grant_type=client_credentials'         .. '&client_id=' .. clientId         .. '&client_secret=' .. clientSecret     local response_body = {}     local _, code, _, _ = https.request{         url = authEndpoint,         method = 'POST',         headers = {             ['Content-Type'] = 'application/x-www-form-urlencoded',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     if code == 200 then         local response = table.concat(response_body)         local jsonResponse = json.decode(response)         accessToken = jsonResponse.access_token     else         -- Fehlerbehandlung         --log('Fehler beim Abrufen des Zugriffstokens:', code)     end end -- Funktion zum Senden des Startbefehls function sendStartCommand()     local requestBody = json.encode({         data = {            type = 'Start',            attributes = {            duration = 400                }                }               })     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 202 then         -- Erfolgreich gestartet         --log('Automower wurde gestartet.')         grp.checkwrite('12/4/1', true)     else         -- Fehlerbehandlung         --log('Fehler beim Starten des Automowers:', code)     end end -- Funktion zum Senden des Stopbefehls function sendStopCommand()     local requestBody = json.encode({       data = {         type = 'ParkUntilNextSchedule',       }     })     local response_body = {}     local _, code, _, _ = https.request{         url = connectEndpoint,         method = 'POST',         headers = {             ['Authorization'] = 'Bearer ' .. accessToken,             ['X-Api-Key'] = clientId,             ['Authorization-Provider'] = 'husqvarna',             ['Content-Type'] = 'application/vnd.api+json',             ['Content-Length'] = #requestBody         },         source = ltn12.source.string(requestBody),         sink = ltn12.sink.table(response_body)     }     log (code, status, headers, response_body)     if code == 202 then         -- Erfolgreich gestoppt         --log('Automower wurde gestoppt.')         grp.checkwrite('12/4/1', false)     else         -- Fehlerbehandlung         --log('Fehler beim Stoppen des Automowers:', code)     end end --Aufruf Starten des Automover mover_command = grp.getvalue('12/4/0') if mover_command == true then     getAccessToken()     sendStartCommand() end     --Aufruf Stoppen des Automover mover_command = grp.getvalue('12/4/0')     if mover_command == false then     getAccessToken()     sendStopCommand() end


I hope this is helping somebody. If you have questions please let me know.

Best Regards
Steffen

Hi Steffen,
When i try to get the Mower ID i got nothing back as you can see below. Do you know if i need to take some extra steps or something?

* arg: 1
  * number: 200
* arg: 2
  * nil
* arg: 3
  * nil
* arg: 4
  * table:
  [1]
    * string: {"data":[]}

the access token part is working fine for me.
Best regards and thanks in advance,
Joep
Reply
#11
I tried a lot so far but without luck. I hope someone can tell me what i do wrong as it seems to be a pairing issue but i have no idea how to solve it. The mower is registered from my account and i have created an application. I can also receive the Accesstoken so that part works.

["errors"]
* table:
[1]
* table:
["detail"]
* string: No pairing between the user and the mower product.
["title"]
* string: No mower pairing
["id"]
* string: 20c27a49-29be-4dd5-b055-b5bc8f5ef82f
["status"]
* string: 404
["code"]
* string: no.mower.pairing
Reply


Forum Jump: