31.07.2023, 14:40
(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