Code Conundrum

Waiting for the Turkey…

I’ve written some code that is supposed to check the status of my Hunter Douglas window shades, but it doesn’t seem to work properly.

The first time I run the code, the results are not correct. The status line returns , a string which contains old information, results that do not reflect the current status of the shade.

Without changing the position of the shade or otherwise modifying the code, the second time I run the code, the results are correct.

When I rewrite the code to include two calls to the “getstatus” function, both results come back with the old information, not the current status.

Why would this be??
How can I fix this?

thx

Here’s the code

‘’’
– test
shadeNUM = “42654”
luup.call_delay(“getstatus”, 1, shadeNUM)
print (result0)

return true

function getstatus (fnshadeNUM)
timeout = 5
testnum = tonumber(fnshadeNUM)
status, result0 = luup.inet.wget(“http://XXX.XXX.XXX.XXX/api/shades/” … testnum, timeout)

return result0
end
‘’’

Can’t help with the code, but Happy Thanksgiving!

C

The problem here is that call_delay returns immediately–it does not pause after scheduling the “getstatus” function, so your code then goes on to print result0, which at that point is undefined/nil (getstatus is now scheduled but hasn’t yet run).

Move your print statement into the getstatus() function before the return, and it should print more predictable results.

Edit: with modified code.

– test
shadeNUM = “42654”
luup.call_delay(“getstatus”, 1, shadeNUM)
return true

function getstatus (fnshadeNUM)
timeout = 5
testnum = tonumber(fnshadeNUM)
status, result0 = luup.inet.wget(“[http://XXX.XXX.XXX.XXX/api/shades/](http://xxx.xxx.xxx.xxx/api/shades/)” … testnum, timeout)
print(result0)
return result0
end

Thanks rigpapa, but it didn’t work.

When I put the print statement in the function I get nothing in the output console of ALTUI.

Turkey is up to 160f so I have a little while to wrestle with this. No big deal, dinner doesn’t depend on this!

Try using luup.log("The result is "..tostring(result0)) instead, and then look in the LuaUPnP.log file for that message.

Turkey was a little dry, but lots of gravy worked. I guess I should have respected the temp probe and not the printed instructions!

So, I ran the code with your suggestion, with a slight modification. In the code I placed two commands, one outside the function after it has been called -
(luup.log("The result outside the function is "…tostring(result0))
and one inside the function
(luup.log("The result inside the function is "…tostring(result0))

Saw both entries in the log and they were different! The output from inside the function reflected the true status, the output from outside the function showed the previous status.

So, for some reason, the correct results from the function are not being returned from the function immediately, but are mysteriously being returned to show up the next time I run the code.

A conundrum. My wife’s murder mystery books have nothing on code debugging!

PS, I changed the name of the variable from to just in case “result” is one of those reserved words. Didn’t make a difference.

There’s no conundrum here, at least not for the code. :slight_smile: You’ve asked it to delay the function. It did. The log statement you placed immediately after the call_delay will run immediately after that API schedules the delayed call (not after it runs). Then, after the delay expires, the getstatus function is called, and it prints within itself the result it created.

The result of that is that each time your code executes, it will first print the previous result, and five seconds later print the new result.

OK, I get it. I see from the time stamps in the log that the “outside” log ran before the “inside” log. The program continues executing the code and then runs the second GetPOSvalue at the specified delay period. I did not know that, I thought the code would pause the delay period before continuing. (an assumption that is a throw back to my much (much) earlier experiences with BASIC programming.

Maybe I should change the title of the discussion to “code WRITER conundrum”!

So, I’m barking up the wrong tree I guess. Any suggestions on how to proceed? What I want to do is get the status of the shade, move the shade, then get the new status and compare the two.

I guess I could not use a function call, just duplicate the function commands, once before I move the shade and once after the shade, but that precludes using the code for multiple shades, or at least make the LUA code so enormous I’m afraid I’d be testing the limits of memory in the V+

I tried using a sleep function for 10 seconds to pause the run of the code after the movement commands, but before the second call to the GetPOSvalue function, but that only returns the original status. And I’ve been warned here that invoking this could screw up other things going on with the V+

The question here really is “and then what?”. Presumably you’re going to do something with that information/comparison result?

You can call your getstatus() function yourself. That would work totally fine…

function getstatus (fnshadeNUM)
    timeout = 5
    testnum = tonumber(fnshadeNUM)
    status, result0 = luup.inet.wget(“http://xxx.xxx.xxx.xxx/api/shades/)” … testnum, timeout)
    print(result0)
    return result0
end

startStatus = getstatus( shadeNUM ) -- call directly
luup.call_delay( "getstatus", 5, shadeNUM) -- called by luup after delay

So in this case, you are using the same code to get the value before and after, but, the delayed call isn’t doing anything with the value other than printing it and returning it to Luup, which just discards it–you can’t return a value from a delay call into the code that scheduled the delay, at least, not easily.

I suspect we’re now into a bit of an X-Y Problem here. That is, you want to do something X, and you’ve decided step Y is part of getting that done, but you didn’t know how to do step Y, so you’ve asked here and we’re trying to help. But Y may not be the best way, or may even make it harder, to solve your bigger problem and meet your goal X. What I think we really need at this point is for you to step back and tell us what X you are really trying to accomplish.

So here’s the “big” picture.

I’ve got 3 motorized shades that are funky (they are older models). Sometimes they lose their calibration and refuse to respond to commands until they are re-calibrated. I discovered this while troubleshooting the failed shades. I can manually recalibrate the shades via a button on the shade or via the shades APP. This defeats the purpose of motorized, automatic shades. (If I have to get up at sunrise to check my shades for operation I might was well open the damn shades manually!)

The failures occur randomly. One day they work fine, another day one drops out, another day another drops out, etc. Sometimes they don’t fail for 3 days. Sometimes twice in the same day. The timing of the failure is also random, so a shade could work in the morning but not at night. The manufacturer gave me replacements for the failed shades, so all I’m doing is trying to reuse the failed shades in “non-critical” locations.

Investigation led me to find commands that I can use in LUA to determine the position of the shades, move the shades, calibrate the shades. (Contributors, including you rigpapa, to this forum have been very helpful) But no command will tell me that the shade has failed. Once it had failed, all I can only get is the shade’s last status. I looked at the variables returned from the shade, such as battery level, signal level, and position. Once the shade fails, all these variables are frozen. I cannot change signal levels or battery levels via software. I can, however, issue orders to move the shade.

So I have to try to move the shade and find out if it moved or not to determine the status of the shade. It it moves, great, all is OK, I can proceed to move the shade to the desired position. If it doesn’t move then it needs recalibration before I can move the shade to where I want it.

I created a scene in Vera to run a recalibration on each shade at 5am. That works great, but does not “catch” a failure during the day.

So, I need to check the status (failed/not failed) of each shade before I send commands to open/close/move or whatever I want it to do at particular times.

My plan is to check the functionality of the shade when I want to move it, by doing the following -
–Get status of shade #1
–save that status as
–then send a command to move the shade,
–Get the status of #1 after the move
–compare the two status checks
–If they are different, then the shade moved
–If they are the same, the shade did not move
–If it doesn’t move, re-calibrate the shade to bring it back into operation
–then do the original move command.

Then do the same check with shade 2 and shade 3 when they need to be moved.

So, there you have it - the “big” picture. If I still had hair, I’d be pulling it out.

A couple of details I left out,

1 - I use the original shade position to determine which way to move the shade, ie, if the shade reports it’s closed, move the shade upwards. If the shade reports it is open, move the shade downwards.

2 - when I send a command to the shades to move, there is a lag of a couple of seconds before the shade finishes it’s move, so I have to wait a few seconds to check it’s status.

inflatable-crowd

Vera Users’ Convention, 2019, colorized

OK. With all due seriousness, this is actually a fairly involved programming project, not because it’s hard in concept, but just because of the way you have to structure it around the delays. Here’s my attempt, which of course I cannot run since I don’t have your hardware, so likely errors to be found, but hopefully it’s close. This goes in Startup Lua (Apps > Develop apps > Edit Startup Lua – but I recommend considering installing the LuaView plugin and using it).

-- This global array will store the last known position of every shade.
shadeStatus = {}

function getShadeStatus(fnshadeNUM)
	local timeout = 5
	local testnum = tonumber(fnshadeNUM)
	local _, res = luup.inet.wget("http://xxx.xxx.xxx.xxx/api/shade/" .. testnum, timeout)
	luup.log("getShadeStatus() shade "..testnum.." status "..tostring(res))
	local json = require "dkjson"
	local st = json.decode( res )
	shadeStatus[testnum] = st.position1 or -1 -- save current position
	return st.position1
end

-- Move shade to desired position. This is the function you should call from your scene Lua, Reactor, PLEG, etc.
function setShadePosition( shadeNum, posNum )
	shadeNum = tonumber( shadeNum ) or error "Invalid shade number "..tostring(shadeNum)
	posNum = tonumber( posNum ) or error "Invalid position "..tostring(posNum)

	-- If shade is not already in that position, move it.
	if shadeStatus[shadeNum] ~= posNum then
		-- Start shade in motion to desired position. 
		local json = require "dkjson"
		local st = { shade={ positions= { posKind1=1, posKind2=2, position1=posNum, position2=posNum } } }
		os.execute( [[curl -X PUT -H "Content-type: application/json" -d ']] ..
			json.encode( st ) .. "' http://xxx.xxx.xxx.xxx/api/shade/" .. shadeNum )
		luup.call_delay( 'checkShade', 5, shadeNum .. ":" .. ( shadeStatus[shadeNum] or -1 ) .. ":" .. posNum )
	end
end

-- Recalibrate shade (utility function).
function recalibrateShade( shadeNum )
	os.execute( [[curl -X PUT -H "Content-type: application/json" -d '{ "shade": { "motion": "recalibrate" } }' http://xxx.xxx.xxx.xxx/api/shade/]]
		.. shadeNum )
	-- Do not remove the line below (but if the shade position is known after recal, change the value)
	shadeStatus[shadeNum] = -1
end

-- This is the timer callback to check the shade
function checkShade( parg )
	local shadeNum,startPos,targetPos = parg:match("^(%d+):(%d+):(%d+)")
	shadeNum = tonumber( shadeNum )
	startPos = tonumber( startPos )
	targetPos = tonumber( targetPos )
	local newPos = getShadeStatus( shadeNum )
	if startPos == newPos then
		-- Shade has not moved after 5 seconds! Recalibrate.
		recalibrateShade( shadeNum )
		luup.call_delay( 'retryShade', shadeNum .. ":" .. targetPos, 10 ) -- adjust delay here for however long calibration takes, plus a bit
	else
		shadeStatus[shadeNum] = targetPos -- assume success
	end
end

-- This is the timer callback to retry positioning the shade after recalibration.
function retryShade( parg )
	local shadeNum,targetPos = parg:match("^(%d+):(%d+)")
	setShadePosition( shadeNum, targetPos )
end

To move your shades, you need to call setShadePosition( shadeNum, posNum ) from your scene Lua, Reactor actions (Run Lua), PLEG, etc.

For theory of operation, let’s take it a chunk at a time. First, this goes in Startup Lua, so all of this is defined and set up at Luup startup. It runs once, and mostly just defines functions to be used later.

The first part:

-- This global array will store the last known position of every shade.
shadeStatus = {}

function getShadeStatus(fnshadeNUM)
	local timeout = 5
	local testnum = tonumber(fnshadeNUM)
	local _, res = luup.inet.wget("http://xxx.xxx.xxx.xxx/api/shade/" .. testnum, timeout)
	luup.log("getShadeStatus() shade "..testnum.." status "..tostring(res))
	local json = require "dkjson"
	local st = json.decode( res )
	shadeStatus[testnum] = st.position1 or -1 -- save current position
	return st.position1
end

Most of this should look familiar, with two additions:

  1. The first line defines a global variable (array/table) called shadeStatus that we’re going to use to hold the last known position of each shade. No preconfiguration is needed; each shade will be initialized/learned as you use it (i.e. when you call setShadePosition())
  2. The request returns a JSON string that has to be parsed, and the position value extracted from it.
  3. I’ve added a line before the return to save the retrieved position in the new array.
-- Move shade to desired position. This is the function you should call from your scene Lua, Reactor, PLEG, etc.
function setShadePosition( shadeNum, posNum )
	shadeNum = tonumber( shadeNum ) or error "Invalid shade number "..tostring(shadeNum)
	posNum = tonumber( posNum ) or error "Invalid position "..tostring(posNum)

	-- If shade is not already in that position, move it.
	if shadeStatus[shadeNum] ~= posNum then
		-- Start shade in motion to desired position. 
		local json = require "dkjson"
		local st = { shade={ positions= { posKind1=1, posKind2=2, position1=posNum, position2=posNum } } }
		os.execute( [[curl -X PUT -H "Content-type: application/json" -d ']] ..
			json.encode( st ) .. "' http://xxx.xxx.xxx.xxx/api/shade/" .. shadeNum )
		luup.call_delay( 'checkShade', 5, shadeNum .. ":" .. ( shadeStatus[shadeNum] or -1 ) .. ":" .. posNum )
	end
end

This is setShadePosition(). You pass it the shade device number and position you want. If the shade is not already in that position, it will issue the command to move it. I just guessed on what that might look like for your shade API–you will need to fix that to make it right. But the idea is that it sets the shade in motion if needed, and then schedules the checkShade function for five seconds out. It needs to pass three values to checkShade–since Luup delay callbacks can only receive one string argument, it creates a string of the three values, separated by colons (:s), which checkShade will need to parse and turn back into separate values. A shortcoming of the Luup delay API, but at least it passes something.

-- Recalibrate shade (utility function).
function recalibrateShade( shadeNum )
	os.execute( [[curl -X PUT -H "Content-type: application/json" -d '{ "shade": { "motion": "recalibrate" } }' http://xxx.xxx.xxx.xxx/api/shade/]]
		.. shadeNum )
	-- Do not remove the line below (but if the shade position is known after recal, change the value)
	shadeStatus[shadeNum] = -1
end

We recalibrate the shade. Make sure you don’t remove the last line–we must tell the other functions that the shade position is unknown after recalibration. We use -1 to indicate that. If it happens that the shades are always closed (0) or open (65535) after recalibration, you can put that value in there instead of -1.

-- This is the timer function to check the shade
function checkShade( parg )
	local shadeNum,startPos,targetPos = parg:match("^(%d+):(%d+):(%d+)")
	shadeNum = tonumber( shadeNum )
	startPos = tonumber( startPos )
	targetPos = tonumber( targetPos )
	local newPos = getShadeStatus( shadeNum )
	if startPos == newPos then	
		-- Shade has not moved after 5 seconds! Recalibrate.
		recalibrateShade( shadeNum )
		luup.call_delay( 'retryShade', shadeNum .. ":" .. targetPos, 10 ) -- adjust delay here for however long calibration takes, plus a bit
	else
		shadeStatus[shadeNum] = targetPos -- assume success
	end
end

Here’s checkShade, which is called five seconds after (hopefully) starting the shade moving. It starts by unpacking the string that setShadePosition had to create back into three values: the shade number, the starting position when setShadePosition was called, and the target shade position. It then gets the current shade position, and checks to see if starting position is different from current. If it is, we’re done here–the shade is moving or has moved and all is well. Otherwise, it calls recalibrateShade to get that process rolling, and then schedules retryShade to try to get it to the target position again. You may need to adjust the delay there so that it’s always a few seconds longer than recalibration can take worst-case.

Finally, here’s retryShade

-- This is the timer function to retry positioning the shade after calibration. It is only called
-- after calibration is used.
function retryShade( parg )
	local shadeNum,targetPos = parg:match("^(%d+):(%d+)")
	setShadePosition( shadeNum, targetPos )
end

This just unpacks its argument (we need two values, but delay callbacks can only receive one, so again, we made a string of the two values with a colon between, and we have to split that back up). Then it calls setShadePosition and hopefully gets the shade positioned correctly.

There will likely be additional specifics to work out here, but that’s my first attempt at something that might work for you, as a starting point.

Edit: Updated for (hopefully close) API docs I found here: https://github.com/drbrain/indigo-powerview/blob/master/PowerView%20API.md

Edit 2: There’s an HD plugin. Maybe it’s author would be willing to incorporate the movement check and recalibration attempt?

3 Likes

“Vera Users’ Convention, 2019, colorized”

Haha :rofl:

rigpapa,

gotta say, my first response was “Holy c… you put in a lot of work on this”! I can’t thank you enough.

I will have to digest what you’ve created, I think it will be a major learning experience that will be worth the effort.

The pix could also be a crowd of 7.0.30 /VeraPlus owners.!

3 Likes

Found the HD app here on Vera, but it will only work with older HD shades.

The API document you found was the basis for my search to control the shades with a little more specificity. Did some educated guesses and found some more commands.

One thing I can see right away is that I will have to change the setShadePosition function.

Here’s why. The setShadePosition function as written (or as I think it’s written), sends the shade to a specific position, which will work if the shade is OK. Unfortunately, if a shade has lost it’s calibration, it will accept a new position# for the shade, but will not actually move it. It will report it’s status as being in the new position, but it would, in fact, still be in the old position. (Took me a while to figure out this little quirk!)
So what I need to do is -
A, determine where the shade is initially (It does report it’s final correct position, because that is where it was when the shade lost its calibration), and then
B, command the shade to start moving, either up or down depending on where it originally was. Then immediately command the shade to stop movement. Between the time the two commands are processed the shade will move a very small amount, but will report it’s new position, IF IT MOVES (ie it still retains its calibration). Then
C, if the move command does not result in any movement, then the shade needs to be calibrated. It’s not really relevant how much the shade moves, just that it has moved.
I also need to retain that original position, so that I can, once the shade is determined to be working, return the shade to that original position.

To start attempting to understand what you had written, I dropped the initial code into Startup Lua (via LUAview, which I already had installed). Yes, I filled in the correct IP address of the HD hub.

I then reloaded the LUUP engine, making the assumption that changing the Startup LUA would require a restarting of the engine.

I then did a simple call (in ATLUI) to the function “getShadeStatus” as follows:

‘’’
shadeNUM = 42654

getShadeStatus (shadeNUM)

print (shadeStatus[shadeNUM])

‘’’

Hoping I would see the value of <shadeStatus[42654]> but all I got was the following error message

“[string “function scene_8()…”]:2711: attempt to index local ‘st’ (a nil value)”

Not sure what that is referring to, but I looked at my scene #8 (in LUA View) and there is no reference to a variable named ‘st’, it is a scene that blinks lights when the smoke alarm goes off.

So, I’ll give my brain a rest and see what fresh perspective I have tomorrow. Thanks for giving me the benefit of your knowledge.

rigpapa,

I feel as if I have a little bit of egg on my face.

After digesting some of what you wrote and having my first exploration of your code being roadblocked by my ignorance, I dreamed up a couple of programming scenarios to try to get around this time delay issue while I wrestle with your code.

What I discovered was that the HD shades and hub do not have a robust comunication link. I was using the “move” shade command to move the shade. Sounds logical, right? But what I came to realize is that the move command, while it does move the shade, it does not update the shade’s status with the hub. It appears that the hub needs to inquire to the shade, not the other way around. So when I “move” the shade, it doesn’t tell the hub, hence no update to it’s position. I guess this makes sense, there’s no need to inform the hub that the shade has been moved.

Instead of “move”, which works directly with the shade, I tried calling a “scene” (an HD scene, not a Vera Scene). This call goes to the hub, which then tells the shade to move to a certain location. It then updates the status of the shade (at the hub). I can issue a “stop shade” command, and that will update the hub with the shade’s then current (hopefully changed) position, which I can then use in my LUA code.

Keep in mind these funky shades are older models of HD’s newer shades. Not sure if these discoveries hold true for their newer shades, which seem to be working great.

So, I got my code to do what I wanted it to do. I even threw in some day/night time checking to prevent the scenario where the shade goes out of calibration during the day (it would be open), checking it’s status at night and not returning the shade to it’s position it was when it went out of calib, but rather just close the shade (after forcing a calibration)

But your help was not, in any way, wasted. There were quite a few coding “tidbits” I learned from the exercise.

Things I learned from this, so far… in no particular order -

Passing multiple variables to a luup.call_delay function

which means I need to perform String concentation:
luup.call_delay( ‘checkShade’, 5, shadeNum … “:” … ( shadeStatus[shadeNum] or -1 ) … “:” … posNum )

and string disassembly:
local shadeNum,startPos,targetPos = parg:match(“^(%d+):(%d+):(%d+)”)
(took me a while to determine what the %d+ and the ^ refers to, but now I’ve got another set of tools!)

Error checking:
shadeNum = tonumber( shadeNum ) or error "Invalid shade number "…tostring(shadeNum)

Something NOT equal:
if shadeStatus[shadeNum] ~= posNum then

Variable values can be changed directly, no more using another variable name just to change format
shadeNum = tonumber( shadeNum )

Use of the startup LUA
Creating data arrays

Not really sure about what this does:
local json = require “dkjson”
but it hints of a larger set of abilities

Thanks again for sharing your knowledge! I just hope that by the time I feel reasonably confident in my elementary LUA skills, VERA or Ezlo will not abandon the ability to use LUA to personalize the HA experience.

Excellent. If you walk away from the conversation with more tools and an improved experience with the product, my effort is worth every minute spent.

This line loads the “dkjson” module, which is an installed extra product in the Lua ecosystem that lets you parse and create JSON-formatted data. The shade commands receive and respond with JSON, so this library does the conversion between Lua tables and those JSON strings. This line specifically tells Lua to load the module and keep a reference to it in the local variable json, so you can use the module’s decode() function, for example, by calling json.decode( string ). This is probably one of the most commonly-used modules when interfacing with other APIs. The socket library and its related modules are also big for interfacing, but that’s a topic for another thread… :slight_smile:

I could sure use the ability to pick out one piece of data from my shade status results! That’s how I find the original position of the shade.

Not understanding the structure of the string I get when I query the status of a shade, I’ve written a function that finds the value of the variable (which happens to be Position1) by finding the starting point of the actual text “Position1” in the shade status string, then get the value of the information contained at “starting point” plus 11

here is what I’ve written (with comments) to do this:

‘’’

function GetPOSvalue (fnshadeNUM)
– gets the shades original position

timeout = 5

local status, result0 = luup.inet.wget(“http://XXX.XXX.XXX.XXX/api/shades/” … fnshadeNUM, timeout)
– gets the full original status report in string format (result0)

ORIGresult = result0
– saves the whole original status string for comparison later in the code

local result1 = result0.gsub(result0,“%p”," ")
– removes all punctuation from string

– the position of the needed value string inside is not static, sometimes its last, sometimes not, so I need to do a search for it, rather than a straight position call.

local FINDPOS = “position1”
– the header (?) of the data that shows the current position of the shade.

local result2 = string.find(result1, “position1”)
– the numeric position of the beginning of the phrase

STRINGstart = (result2 + 11)
– the start of the data pertaining to

STRINGend = (STRINGstart+5)
– the end of the data pertaining to , assuming the data is 5 characters long.
– Sometimes the data = zero, a single character, which causes an issue

local value1 = (string.sub(result1, STRINGstart, STRINGend))
– gets the character reading of the shade position data

ORIGpos = tonumber(value1)
– converts to a number for comparison

if ORIGpos == nil then ORIGpos = 0 end
– this is for the instance that the data is zero and is only one character in length.

return ORIGpos, ORIGresult
end
–End of Function---------------------------------
‘’’

Here’s the original string result that I get from my status call to the shade -

{“shade”:{“id”:42654,“type”:6,“batteryStatus”:3,“batteryStrength”:177,“roomId”:858,“firmware”:{“revision”:1,“subRevision”:8,“build”:1944},“name”:“V0JSIFdO”,“groupId”:13164,“signalStrength”:4,“motor”:{“revision”:0,“subRevision”:0,“build”:223},“aid”:10,“capabilities”:0,“positions”:{“posKind1”:1,“position1”:0}}}

Is this a standard JSON format? If so, does the json “dkjson” module deconstruct the string into an array of data? Does it use the titles (in quotes) as the identifier in that array? If so, how would assign a variable to the value in that constructed array that I’m looking for?

Something like
x= arrayname.position1
?

Yes, my earlier code did this for you…

function getShadeStatus(fnshadeNUM)
	local timeout = 5
	local testnum = tonumber(fnshadeNUM)
	local _, res = luup.inet.wget("http://xxx.xxx.xxx.xxx/api/shade/" .. testnum, timeout)
	luup.log("getShadeStatus() shade "..testnum.." status "..tostring(res))
	local json = require "dkjson"
	local st = json.decode( res )
	shadeStatus[testnum] = st.position1 or -1 -- save current position
	return st.position1
end

You seem to be using the /shades/ API here. Don’t if you can avoid it… use /shade/id (singular shade, not shades, where id is the number of the shade). That will give you the shorter response as my code shows.

This was one of the stumbling blocks I put off dealing with
When I ran the code as written with the singular “shade” I get the following response -

“[string “ALTUI - LuaRunHandler”]:9: attempt to index global ‘st’ (a nil value)”

I took your snippet, changed it to the plural “shades” and I added a little to the code to ‘print’ the results. Also, at line 5 you wrote “local _, res”. Is this functionally the same as writing “local status, res”? It does seem to work either way.

‘’’

shadeStatus = {}
function getShadeStatus(fnshadeNUM)
local timeout = 5
local testnum = tonumber(fnshadeNUM)
local _, res = luup.inet.wget(“http://XXX/api/shades/” … testnum, timeout)
luup.log("getShadeStatus() shade “…testnum…” status "…tostring(res))
local json = require “dkjson”
st = json.decode (res)
shadeStatus[testnum] = st.position1 or -1 – save current position
ORIGpos = st.position1
return st, ORIGpos, res
end

getShadeStatus (42654)
print (ORIGpos, st, st.position1, res)

‘’’

Running that adjusted code results in the following in the Console Output:

nil
table: 0x1230ed8
nil
{“shade”:{“id”:42654,“type”:6,“batteryStatus”:3,“batteryStrength”:177,“roomId”:858,“firmware”:{“revision”:1,“subRevision”:8,“build”:1944},“name”:“V0JSIFdO”,“groupId”:13164,“signalStrength”:4,“motor”:{“revision”:0,“subRevision”:0,“build”:223},“aid”:10,“capabilities”:0,“positions”:{“posKind1”:1,“position1”:0}}}

So, I can see that the json decode is making a table of the status result (res), I can see that the status result (res) has the correct information. It just seems to be having a hard time retrieving the data at the array position called “position1”