Jump to content

[TUTORIAL} Introduction to API calls and http requests


Recommended Posts

Introduction to API and http requests

Like the other 'Introduction to' posts I'm h0ping this may help some of the people begining their HC2 and lua journey

Corrections and suggestions to improve always welcome.

 

What is an API
An API can be used to execute commands but the majority are used to request a response or status. Most API’s are interrogated through a http or https request. Some API’s require authentication in the form of a username or a password or an API key. There are public API’s and private API’s. There are a number of API types and the one of the most widely used is a called RESTAPI. This type of API is supported by the HC2 but there are others such MQTT, SOAP, etc


The following are API’s that you may have come across in this forum and there are many others

 

Some organisations like AWS or Google have multiple API’s that are used for different purposes

 

Structure
All API’s has a set of web methods with a set structure. An API call or request can be either a GET, DELETE, POST, PUT and for this introduction we will focus on the GET
This means that the data format is predictable and as such is easy to traverse and extract responses. Most RESTAPI responses have a format that conforms to json standard. Json is a way to store information in an organized, easy-to-access manner. It gives us a human-readable collection of data that we can access in a really logical manner. All public API’s are documented like the ones outlined above so a under will know what to expect when they issue a http request.
A simple json example would be something like 

 

local jStr = {"age" : "24", “hometown" : "Missoula, MT", "gender" : "male" };



A slightly more complicated json string could be be 

 

{"coord":{"lon":15.98,"lat":45.81},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"stations","main" :{"temp":10.86,"pressure":1018.82, "humidity":66,"temp_min":10.86,"temp_max":10.86,"sea_level":1035.43,"grnd_level":1018.82},"wind":{"speed":1.06,"deg":281.501},"clouds":{"all":32},"dt":1487346966,"sys":{"message":0.0038,"country":"HR","sunrise":1487310847,"sunset":1487348795},"id":6618983,"name":"Zagreb - Centar","cod":200}


This could also be referreed to as a tbale because of it's structured format. Fortunately there are websites like http://jsonprettyprint.com/ that make these json strings easier to read

 

{
  "coord": {
    "lon": 15.98,
    "lat": 45.81
  },
  "weather": [
    {
      "id": 802,
      "main": "Clouds",
      "description": "scattered clouds",
      "icon": "03d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 10.86,
    "pressure": 1018.82,
    "humidity": 66,
    "temp_min": 10.86,
    "temp_max": 10.86,
    "sea_level": 1035.43,
    "grnd_level": 1018.82
  },
  "wind": {
    "speed": 1.06,
    "deg": 281.501
  },
  "clouds": {
    "all": 32
  },
  "dt": 1487346966,
  "sys": {
    "message": 0.0038,
    "country": "HR",
    "sunrise": 1487310847,
    "sunset": 1487348795
  },
  "id": 6618983,
  "name": "Zagreb - Centar",
  "cod": 200
}


To access an api you need 2 things

 

  • ip address, port, etc to make the http/https request
  • Structure/format of the response so you know how to traverse it and extract what you want


The easiest way to explain and learn is by example. As it is highly likely that everybody reading this will have a HC2 we can use to demonstrate the principles. 
The HC2 RESTAPI is documented on the Fibaro developer site at https://developer.fibaro.com/docs/fghc-restapi
The one we will look at is devices. This the first one under the General category at the developers website above. The developer website shows the various parameters that are returned with this call. The API can be used to request information or complete an action.
We will look at the request for information


If you go to http://,hc2-ip./docs you will see the interactive or inline documentation for web API
image 1.PNG


Go to the section on devices and select deviceid
image 2.PNG


 enter a device number ( I selected device 176 which is a dimmer module ) and click Try It. 
You will see the json formatted or encoded response for that API call. You will also see the full url for the call.


image 3.PNG

 

If you copy the url and paste it into a browser you will get the same thing (except as one long string)

 

{"id":176,"name":"kitchenPendant","roomID":7,"type":"com.fibaro.multilevelSwitch","baseType":"com.fibaro.binarySwitch","enabled":true,"visible":true,"isPlugin":false,"parentId":175,"remoteGatewayId":0,"interfaces":["deviceGrouping","fibaroFirmwareUpdate","levelChange","light","power","zwave","zwaveConfiguration","zwaveSceneActivation"],"properties":{"parameters":[{"id":1,"lastReportedValue":255,"lastSetValue":255,"size":1,"value":255},{"id":6,"lastReportedValue":0,"lastSetValue":0,"size":1,"value":0},{"id":7,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":8,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":9,"lastReportedValue":5,"lastSetValue":5,"size":1,"value":5},{"id":10,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":11,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":12,"lastReportedValue":99,"lastSetValue":99,"size":1,"value":99},{"id":13,"lastReportedValue":2,"lastSetValue":2,"size":1,"value":2},{"id":14,"lastReportedValue":2,"lastSetValue":2,"size":1,"value":2},{"id":15,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":16,"lastReportedValue":1,"lastSetValue":1,"size":1,"value":1},{"id":17,"lastReportedValue":0,"lastSetValue":0,"size":1,"value":0},{"id":18,"lastReportedValue":0,"lastSetValue":0,"size":1,"value":0},{"id":19,"lastReportedValue":0,"lastSetValue":0,"size":1,"value":0},{"id":20,"lastReportedValue":110,"lastSetValue":110,"size":1,"value":110},{"id":30,"lastReportedValue":3,"lastSetValue":3,"size":1,"value":3},{"id":39,"lastReportedValue":600,"lastSetValue":600,"size":2,"value":600},{"id":41,"lastReportedValue":0,"lastSetValue":0,"size":1,"value":0}],"pollingTimeSec":0,"zwaveCompany":"Fibargroup","zwaveInfo":"3,3,52","zwaveVersion":"2.2","configured":"true","dead":"false","deviceControlType":"23","deviceGroup":"[]","deviceGroupMaster":"0","deviceIcon":"15","emailNotificationID":"0","emailNotificationType":"0","endPointId":"0","firmwareUpdate":"{\"info\":\"\",\"progress\":0,\"status\":\"UpToDate\",\"updateVersion\":\"2.2\"}","isLight":"true","liliOffCommand":"","liliOnCommand":"","log":"","logTemp":"","manufacturer":"","markAsDead":"false","model":"","nodeId":"49","parametersTemplate":"235","power":"0.00","powerConsumption":"42","productInfo":"1,15,1,0,16,10,2,2","pushNotificationID":"0","pushNotificationType":"0","remoteGatewayId":"0","saveLogs":"true","sceneActivation":"0","serialNumber":"","showEnergy":"true","smsNotificationID":"0","smsNotificationType":"0","updateVersion":"","useTemplate":"true","userDescription":"","value":"0"},"actions":{"associationGet":1,"associationSet":2,"getParameter":1,"reconfigure":0,"setParameter":2,"setValue":1,"startLevelDecrease":0,"startLevelIncrease":0,"stopLevelChange":0,"turnOff":0,"turnOn":0,"updateFirmware":1},"created":1486747960,"modified":1486747960,"sortOrder":13}

 

EXAMPLE - API Call from a Virtual Device
The following is a http request to the HC2 api. Copy the following in a button or main loop in a virtual device
 

local device = fibaro:getSelfId()
localhost = '127.0.0.1'
local port = 11111
diag = Net.FHttp(localhost, port);
response = diag:GET("/api/devices/176")
result = json.decode(response);
fibaro:debug("name: "..result.name)
fibaro:debug("properties.value: "..result.properties.value)


run the vd and open the debug window

image 5.PNG


You’ll see the name and value of the device you selected. If you look through the json response from further up in the tutorial you’ll see the correlation between the results and the json 

There are some examples of how to iterate through an array in another one of the tutorials I posted. Please see my signature…

 

One last one in case you have difficulty sleeping :-) and the if the above was too simple


This function iterates through all the virtual devices on your HC2 and looks for one called 'LAN Network 2.0'
The 'LAN Network 2.0' virtual device has multiple labels

 

  1. The function iterates through the virtual device and counts all the labels ( in the json for the virtual device these are called rows) 
  2. For each row above it looks for an element type ‘Label’
  3. It then iterates through all the labels checking that they match the default format of Label1, Lable2, etc.
string.match(jT[i].properties.rows[r].elements[e].name, "Label")) == "Label"

The result is a loop inside a loop inside another loop, iterating and checking at each step

 

full code for function to pick through when you have some time.

 

function labelCheck()
    
    fibaro:log("vdCheck mode enabled")
    local device, localhost, port = fibaro:getSelfId(), '127.0.0.1', 11111; 
    diag = Net.FHttp(localhost, port);
    response = diag:GET("/api/virtualDevices")
    jT = json.decode(response);
    for i = 1, #jT do
      if jT[i].name == "LAN Monitor 2.0" then
        if #jT[i].properties.rows == #jN then labelCount = "true" else labelCount = "false" end
        for r = 1, #jT[i].properties.rows do 
          for e = 1, #jT[i].properties.rows[r].elements do 
            if (string.match(jT[i].properties.rows[r].elements[e].name, "Label")) == "Label"
              then
                labelMatch = labelMatch else labelMatch = "false"
            end  
          end 
        end     
      end
    end
    if  labelCount == "true" and labelMatch ~= "false" then
        fibaro:debug("Quantity and format of labels is correct") else
          if labelCount == "false" then fibaro:debug("Quantity of labels incorrect")  fibaro:log("Quantity of labels incorrect") end
          fibaro:sleep(3000)
          if labelMatch == "false" then fibaro:debug("Label format is incorrect")     fibaro:log("Label format is incorrect") end
    end
end

 

Happy coding and suggestions to improve/correct always welcome

 

-f


 

Edited by AutoFrank
  • Like 5
  • Thanks 1
Link to post
Share on other sites
  • 2 weeks later...

can I make a Net.FHttp call from a Scene instead of a Device? I have lifted the code out of a device and it is really angry at me trying to index 'Net'(a nil value)

Link to post
Share on other sites
1 hour ago, shaunfrost said:

can I make a Net.FHttp call from a Scene instead of a Device? I have lifted the code out of a device and it is really angry at me trying to index 'Net'(a nil value)

 

Hi @shaunfrost

 

For some reason the http calls are different between scene and vd

 

Here is an example of a http call from a scene

This call is a POST and creates a table through the HC2 api but the principle is the same of you want to access another api

function updateTable()
  if fibaro:getGlobalValue("NetworkTable") == nil then
    local http = net.HTTPClient()
    http:request("http://127.0.0.1:11111/api/globalVariables", {
        options = { method = 'POST', headers = {}, data = '{"name":"NetworkTable","value":""}', timeout = 2000 },
          success = function(status)
            setTimeout(popTable, 3000)
          fibaro:debug(status.status)
          if status.status ~= 200 and status.status ~= 201 then print("failed"); end
          --print(status.data);
        end,
        error = function(err)
          print('[ERROR] ' .. err)
        end
      })
    else
      popTable()
    end
end

Does this help ?

-f

Link to post
Share on other sites
2 hours ago, AutoFrank said:

 

Hi @shaunfrost

 

For some reason the http calls are different between scene and vd

 

Here is an example of a http call from a scene

This call is a POST and creates a table through the HC2 api but the principle is the same of you want to access another api

function updateTable()
  if fibaro:getGlobalValue("NetworkTable") == nil then
    local http = net.HTTPClient()
    http:request("http://127.0.0.1:11111/api/globalVariables", {
        options = { method = 'POST', headers = {}, data = '{"name":"NetworkTable","value":""}', timeout = 2000 },
          success = function(status)
            setTimeout(popTable, 3000)
          fibaro:debug(status.status)
          if status.status ~= 200 and status.status ~= 201 then print("failed"); end
          --print(status.data);
        end,
        error = function(err)
          print('[ERROR] ' .. err)
        end
      })
    else
      popTable()
    end
end

Does this help ?

-f

 

I will play around with it.. it was such a simple thing when it started..

I just wanted to turn this below into a scene!

-- Initialistation
aeonID = 213;
apiKey = "xxxxxxxxxx"
feed = "Watt"
local Emon = Net.FHttp("emoncms.org")
local payload = "?json={"
local key = "&apikey="..apiKey

payload = payload..feed..":"..fibaro:getValue(aeonID, "power")
payload = payload.."}"

tsresponse, tsstatus, tserrorCode = Emon:POST("/input/post.json"..payload..key , "" ) ;
fibaro:debug(tsresponse)
Link to post
Share on other sites
On 2/27/2017 at 7:06 PM, AutoFrank said:

Here is an example of a http call from a scene

 

Hi @AutoFrank

 

I'm sure you're aware of this, but for the purpose of this tutorial, another way to do this in a scene, if the requirement is to simply call the HC2 API, is to use the built-in api function.  I find this easier to use than net.HTTPClient, as it doesn't require the use of the asynchronous success and error callback functions.  Here's an example of a function that creates a global variable:

 

function createGlobalVariable(variableName, variableValue)

	local created = false

	local data = {name = variableName, value=tostring(variableValue)}
	response, status = api.post("/globalVariables", data)
	
	-- If creation successful, then return created=true
	if (status == 201) then
		fibaro:debug("Global variable " .. variableName .. " created")
		created = true
	else
		fibaro:debug("Failed to create global variable " .. variableName .. "!")
	end
	
	return created
end

Dave

Link to post
Share on other sites
2 minutes ago, Dave Harrison said:

 

Hi @AutoFrank

 

I'm sure you're aware of this, but for the purpose of this tutorial, another way to do this in a scene, if the requirement is to simply call the HC2 API, is to use the built-in api function.  I find this easier to use than net.HTTPClient, as it doesn't require the use of the asynchronous success and error callback functions.  Here's an example of a function that creates a global variable:

 

function createGlobalVariable(variableName, variableValue)

	local created = false

	local data = {name = variableName, value=tostring(variableValue)}
	response, status = api.post("/globalVariables", data)
	
	-- If creation successful, then return created=true
	if (status == 201) then
		fibaro:debug("Global variable " .. variableName .. " created")
		created = true
	else
		fibaro:debug("Failed to create global variable " .. variableName .. "!")
	end
	
	return created
end

Dave

 

HI @Dave Harrison

 

Thanks !!, I've never see that direct an approach.

Thanks for adding to the post and I must have a play around with that....

 

-f

 

 

Link to post
Share on other sites

One question here.

What if there is a need to make few calls one after another but in between HC2 needs to wait for successful execution of the predecessor? I was struggling with it for some time and did not found any smart solution...

Link to post
Share on other sites
36 minutes ago, jcichon01 said:

One question here.

What if there is a need to make few calls one after another but in between HC2 needs to wait for successful execution of the predecessor? I was struggling with it for some time and did not found any smart solution...

 

 

@jcichon01

 

I think it depends on where you are making the api calls to ?.. ( internal to the hc2 or external to it )

I have a scene that uses the HC2 api to create 5 variables and they are effectively created them in parallel because of the way the HC2 operates. The api seems to be predictable and has no issue processing these requests.

 

external to the system is a different story and it depends on the api

There is a setTimeout(function, duration) that may help here

 

@petergebruers posted a post on setTimeout that you may find useful - https://forum.fibaro.com/index.php?/topic/18752-example-of-settimeout/#comment-67605

 

 

 

 

 

 

 

 

 

  • Like 1
Link to post
Share on other sites
10 godzin temu, AutoFrank napisał:

 

@jcichon01

 

I think it depends on where you are making the api calls to ?.. ( internal to the hc2 or external to it )

I have a scene that uses the HC2 api to create 5 variables and they are effectively created them in parallel because of the way the HC2 operates. The api seems to be predictable and has no issue processing these requests.

 

external to the system is a different story and it depends on the api

There is a setTimeout(function, duration) that may help here

 

@petergebruers posted a post on setTimeout that you may find useful - https://forum.fibaro.com/index.php?/topic/18752-example-of-settimeout/#comment-67605

 

@AutoFrank
Thank you for quick answer and sorry that I did not provide the details before.

 

Actually I'm calling node-sonos-http-api's say function. In my setup I'm using google TTS engine which does have some limitations on an input text's length. When I'm calling say api with the text which is longer then x characters it does not produce any TTS output so what I had to do is to cut the output into few independent strings. After that I'm calling the TTS engine few times. If there is no "wait until successes" implemented TTS outputs only the content of the last string which was sent... 

If I remember correctly I did try setTimeout between HTTP requests but it did not work. HC2 is somehow "caching" the requests before sending it out...

Link to post
Share on other sites
37 minutes ago, jcichon01 said:

 

@AutoFrank
Thank you for quick answer and sorry that I did not provide the details before.

 

Actually I'm calling node-sonos-http-api's say function. In my setup I'm using google TTS engine which does have some limitations on an input text's length. When I'm calling say api with the text which is longer then x characters it does not produce any TTS output so what I had to do is to cut the output into few independent strings. After that I'm calling the TTS engine few times. If there is no "wait until successes" implemented TTS outputs only the content of the last string which was sent... 

If I remember correctly I did try setTimeout between HTTP requests but it did not work. HC2 is somehow "caching" the requests before sending it out...

 

@petergebruers is a http expert so his advise is probably the best

 

Another approach would be have a timer that runs in parallel with http request and dynamically adjust it based on the number of chars you're trying to announce.

This may allow you make a best 'guestimate' as to how long the TTS takes to finish 

 

Link to post
Share on other sites
On 27-2-2017 at 8:06 PM, AutoFrank said:

 

Hi @shaunfrost

 

For some reason the http calls are different between scene and vd

 

Here is an example of a http call from a scene

This call is a POST and creates a table through the HC2 api but the principle is the same of you want to access another api

function updateTable()
  if fibaro:getGlobalValue("NetworkTable") == nil then
    local http = net.HTTPClient()
    http:request("http://127.0.0.1:11111/api/globalVariables", {
        options = { method = 'POST', headers = {}, data = '{"name":"NetworkTable","value":""}', timeout = 2000 },
          success = function(status)
            setTimeout(popTable, 3000)
          fibaro:debug(status.status)
          if status.status ~= 200 and status.status ~= 201 then print("failed"); end
          --print(status.data);
        end,
        error = function(err)
          print('[ERROR] ' .. err)
        end
      })
    else
      popTable()
    end
end

Does this help ?

-f

Hi Frank, 

Thanks! i got this working but how do I get the variable status.data outside the http:request.. so after }). I cant get it there.

 

Are the variable 'succes' and 'error' cant be renamed?

Link to post
Share on other sites

Hi Frank, 

Thanks! i got this working but how do I get the variable status.data outside the http:request.. so after }). I cant get it there.

 

Are the variable 'succes' and 'error' cant be renamed?

 

Thanks!

Martijn

Link to post
Share on other sites
On 3/9/2017 at 10:50 PM, mdejager said:

Hi Frank, 

Thanks! i got this working but how do I get the variable status.data outside the http:request.. so after }). I cant get it there.

 

Are the variable 'succes' and 'error' cant be renamed?

 

Thanks!

Martijn

 

Hi @mdejager

 

I've added a little more to the http post the creates a global variable.

One way is to assign it to a global variable and but it depends on what you're trying to so with the parameter.

I added some extra code to to print out some extra inforamtion

function createVar()
  if fibaro:getGlobalValue("TestTable13") == nil then
    local http = net.HTTPClient()
    http:request("http://127.0.0.1:11111/api/globalVariables", {
        options = { method = 'POST', headers = {}, data = '{"name":"TestTable","value":"text stored in global variable"}', timeout = 2000 },
          success = function(status)
          print(status.status)
          if status.status ~= 200 and status.status ~= 201 then print("failed"); end
          print(status.data);
          local results = json.decode(status.data)
          for k,v in pairs(results) do print(k, v) end
          print(results.name)
          varName = results.name
          
        end,
        error = function(err)
          print('[ERROR] ' .. err)
        end
      })
    end
end

 

print(status.status)
if status.status ~= 200 and status.status ~= 201 then print("failed"); end

this is the response code 201/200 if it all went okay, 400/500 if there was a problem (roughly)

 

print(status.data);

This is the actual response to the http request and is returned mostly in the form is a table that contains a json encoded string

 

local results = json.decode(status.data)

- this means you need to decode it and select different parts depending on what you want to do

 

for k,v in pairs(results) do print(k, v) end

This reads through the key / value pairs and displays them (this helps show the structure of the response)

 

in this case the output is 

 

{"name":"testVar","value":"text stored in global variable","readOnly":false,"isEnum":false,"created":1489314403,"modified":1489314403}

 

after you find the part of the response you';re interested in you can print it out or assign it to a variable

 

print(results.name)
varName = results.name

You can then use this within the scene or of you want to use it outside you can save the value in a global variable using <fibaro:setGlobal> ad retrieve from there

 

if you run the above it will create a global variable and the debug window will display the following in the debug window

[DEBUG] 10:26:43: 201
[DEBUG] 10:26:43: {"name":"testVar","value":"text stored in global variable","readOnly":false,"isEnum":false,"created":1489314403,"modified":1489314403}
[DEBUG] 10:26:43: readOnly	false
[DEBUG] 10:26:43: value	text stored in global variable
[DEBUG] 10:26:43: isEnum	false
[DEBUG] 10:26:43: name	testVar
[DEBUG] 10:26:43: modified	1489314403
[DEBUG] 10:26:43: created	1489314403
[DEBUG] 10:26:43: testVar

Apologies for the long generic answer but I wasn't too sure what you wanted to do specifically

hope some of it is helpful

-f

 

 

 

 

 

 

 

 

 

 

Link to post
Share on other sites
  • T.Konopka featured this topic
  • 1 year later...
  • 2 weeks later...

Hi

 

Thanks a lot for the tutorial!

I'd like to access data of my Fibaro Home Center 2 from a cloud. Does someone know how to get remote access?

Port forwarding is not really an option, since I'm using the Home Center for demo purposes at different locations.

 

Thanks,

Cornelia

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...