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