LogicMachine Forum
Husqvarna lawn robot mover API - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Husqvarna lawn robot mover API (/showthread.php?tid=4824)



Husqvarna lawn robot mover API - pioneersteffen - 10.06.2023

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/apis/automower-connect-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:
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:
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:
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


RE: Husqvarna lawn robot mover API - admin - 12.06.2023

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/apis/automower-connect-api#readme
Use json.encode instead of raw JSON string or you will run into syntax errors.


RE: Husqvarna lawn robot mover API - pioneersteffen - 15.06.2023

Hi Admin,

many thanks for your help and feedback.

I tried to implement your hints:

Code:
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:
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:
    local requestBody = '{"type: 'Start', attributes: { duration: 24 // minutes}'

Any ideas how to improve?

Many thanks for your help!

Best Regards
Steffen


RE: Husqvarna lawn robot mover API - Erwin van der Zwart - 16.06.2023

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}]}?


RE: Husqvarna lawn robot mover API - admin - 16.06.2023

Try this:
Code:
local requestBody = json.encode({   data = {     type = 'Start',     attributes = {       duration = 24     }   } })



RE: Husqvarna lawn robot mover API - pioneersteffen - 18.06.2023

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:
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:
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:
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


RE: Husqvarna lawn robot mover API - pioneersteffen - 19.06.2023

Hi @all,

during testing I found an issue with this string compare:

Code:
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


RE: Husqvarna lawn robot mover API - admin - 19.06.2023

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.


RE: Husqvarna lawn robot mover API - pioneersteffen - 21.06.2023

(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.


RE: Husqvarna lawn robot mover API - Joep - 31.07.2023

(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:
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:
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:
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


RE: Husqvarna lawn robot mover API - Joep - 06.09.2023

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