LUA (ESP8266) How to call/enter a module command from a string - string

Please forgive me, I don't even know if what I am asking is the correct terminology.
So...here goes.
I built a custom firmware of Nodemcu Dali ( from Hackerspace Stutgart.) This includes a dali lighting control "flavour" as they refer to. I had to modify it to work with the most recent LUA version. Anyway that works and the MODULE is built into the firmware.
From the LUA commandline / Interpretor (Esplorer interface, I can call the module and it all works fine.
To use the module you enter:
dali.arc(address_Mode,0,parameter)
or
dali.send(Address_Mode,Command,Address,parameter)
Address_mode can be: dali.SLAVE , dali.GROUP
Command can be dali.UP_200MS , dali.IMMEDIATE_OFF , dali.GO_TO_SCENE --... about 50 commands.
An Example command to send the light level 128 to all drivers would be as follows:
dali.arc(dali.BROADCAST,0,128) -- direct arc mode ( all lights,*ignored*,50% dimmed)
I want to use MQTT to control this thing.
I could use MQTT topics:
dali_topic/arc_broadcast -- for dali.arc(dali.BROADCAST,var1,var2)
dali_topic/group -- for dali.arc(dali.GROUP,var1,var2)
dali_topic/slave -- for dali.arc(dali.SLAVE,var1,var2)
and my payload string would only have to 2 variables,comma seperated eg. 0,128.
This I can all do day long but now I want to make it "better"...
I want to be able to rather send the message " dali.BROADCAST,0,128" which the code should then sort into a table with elements:
table[1] = dali.BROADCAST
table[2] = 0
table[3] = 128
and call dali.arc(table[1],table[2],table[3])
The table creation works,but I cannot get dali.BROADCAST passed to the module /function? call. First off because it is a string and second because it cannot be converted to a number or whatever substitute is required.
If this can be done them the Command field could aslo be sent with the MQTT payload rather than needing 50 MQTT topics.
I suppose I could also just try a lot of if statements or search a lookup table, but perhaps there is a simple way to just insert the command field in to function/module call?
Any assistance greatly appreciated
Edit here is some LUA output:
table ={"dali.BROADCAST",0,128}
dali.arc(table[1],table[2],table[3])
result:
Lua error: stdin:1: bad argument #1 to 'arc' (number expected, got boolean)
since if you print("dali.BROADCAST") you get nil
However
table[4] = dali.BROADCAST
dali.arc(table[4],table[2],table[3])
result works fine.
print( type(dali.BROADCAST ))
gives number
so how to pass my mqtt string dali.BROADCAST which is received as "dali.BROADCAST" and convert it to just dali.BROADCAST?
note I am not sending "" the message is however sent by MQTT as a CSV string.
From the Firmware source for the dali module.. in the module folder: dali.c
LROT_BEGIN(dali, NULL, 0)
LROT_FUNCENTRY( setup, dali_setup )
LROT_FUNCENTRY( arc, dali_arc )
LROT_FUNCENTRY( send, dali_send )
LROT_NUMENTRY( BROADCAST, BROADCAST )
LROT_NUMENTRY( SLAVE, SLAVE )
LROT_NUMENTRY( GROUP, GROUP )
LROT_NUMENTRY( IMMEDIATE_OFF, DALI_IMMEDIATE_OFF)
LROT_NUMENTRY( GO_TO_SCENE, DALI_GO_TO_SCENE)
The shackspace github link is the correct one, it is simply based on LUA1.45 or something low like that. I only had to modify dali.c in Modules to work with the lastest LUA.
The relevant dali files in the firmware is located in
app/modules
app/include
app/dali
folders

EDIT: Thinking about it, you probably always end up indexing dali, in which case you can do so directly by just structuring your table like this:
table[1] = "arc"
table[2] = "BROADCAST"
table[3] = 0
table[4] = 128
This way you can get to dali.BROADCAST by doing dali[table[2]] and to dali.arc by doing dali[table[1]].
HINT: You should probably still keep a whitelist of what is allowed where because someone could send any string and your program shouldn't just blindly index the dali table with that and return it.
You probably want something like this
Here's the relevant code:
function deepindex(tab, path)
if type(path)~="string" then
return nil, "path is not a string"
end
local index, rest = path:match("^%.?([%a%d]+)(.*)")
if not index then
index, rest = path:match("^%[(%d+)%](.*)")
index = tonumber(index)
end
if index then
if #rest>0 then
if tab[index] then
return deepindex(tab[index], rest)
else
return nil, "full path not present in table"
end
else
return tab[index]
end
else
return nil, "malformed index-path string"
end
end
Homework: this function also works with [] indexing for numbers, which you don't need. It should be easy to simplify the function to only do string-indexing with .
You would use that on the global environment to index it with a single string:
deepindex(_G, "dali.BROADCAST")
-- Which is the same as
_G.dali.BROADCAST
-- And, unless dali is a local, also
dali.BROADCAST
Keep in mind though, that this lets you remote-index _G with anything, which is a huge security nightmare. Better do this:
local whitelist = {}
whitelist.dali = dali
deepindex(whitelist, "dali.BROADCAST") -- this works
deepindex(whitelist, "some.evil.submodule") -- This does nothing

Was looking for a Wiki entry with more details, but I found none. If you happen to know where documentation is that specifies more about the available Lua commands, please include a link in your question.
It appears that there may be other functions to approach the same outcome, but I'm not certain how they are worded in your particular build.
https://github.com/shackspace/nodemcu-firmware-dali/blob/master/app/dali/dali_encode.c
What's returned when you print( type( dali.BROADCAST )) ?
I was guessing it might be raw C userdata, the specific case-switch for that arc command, however, I just found a similar Lua project that lists it as hexadecimal 255
https://github.com/a-lurker/Vera-Plugin-DALI-Planet/blob/master/Luup_device/L_DaliPlanet1.lua
Yea, it's likely just sending hexadecimal numbers.
https://en.wikipedia.org/wiki/Digital_Addressable_Lighting_Interface
try sending dali.arc( 0xFF, 0x00, 0x80 )
or dali.arc( 0xFE, 0x80 )
They make it sound like '1111 1110' ( 0xFE ) is directly followed by the brightness value, so that second command might light.
I'm not sure why it doesn't appear to be sending the correct codes when you place them in a table. What you've written appears to be correct, but it's likely a one-way broadcast, so you don't receive back any error messages...
If you can't get the arc command to work with tables, possibly you'll have better luck with the dali.send() command. Might just be a flaw in that app. If you can't get it resolved, submit a bug report to their GitHub page.

Related

How Can I Check For The Existence of a UUID

I am trying to check for the existence of a UUID as a primary key in my Django environment...and when it exists...my code works fine...But if it's not present I get a "" is not a Valid UUID...
Here's my code....
uuid_exists = Book.objects.filter(id=self.object.author_pk,is_active="True").first()
I've tried other variations of this with .exists() or .all()...but I keep getting the ['“” is not a valid UUID.'] error.
I did come up with a workaround....
if self.object.author_pk is not '':
book_exists = Book.objects.filter(id=self.object.author_pk,is_active="True").first()
context['author_exists'] = author_exists
Is this the best way to do this? I was hoping to be able to use a straight filter...without clarifying logic....But I've worked all afternoon and can't seem to come up with anything better. Thanks in advance for any feedback or comments.
I've had the same issue and this is what I have:
Wrapping it into try/except (in my case it's a View so it's supposed to return a Response object)
try:
object = Object.objects.get(id=object_id)
except Exception as e:
return Response(data={...}, status=status.HTTP_40...
It gets to the exception (4th line) but somehow sends '~your_id~' is not a valid UUID. text instead of proper data. Which might be enough in some cases.
This seems like an overlook, so might as well get a fix soon. I don't have enough time to investigate deeper, unfortunately.
So the solution I came up with is not ideal either but hopefully is a bit cleaner and faster than what you're using rn.
# Generate a list of possible object IDs (make use of filters in order to reduce the DB load)
possible_ids = [str(id) for id in Object.objects.filter(~ filters here ~).values_list('id', flat=True)]
# Return an error if ID is not valid
if ~your_id~ not in possible_ids:
return Response(data={"error": "Database destruction sequence initialized!"}, status=status.HTTP_401_UNAUTHORIZED)
# Keep working with the object
object = Objects.objects.get(id=object_id)

AND occasionally produces wrong result on shell and node native driver

I’ve built a dynamic query generator to create my desired queries based on many factors, however, in rare cases it acted weird. After a day on reading logs I found a situation that can be simplified in this:
db.users.find({att: ‘a’, att: ‘b’})
What I expect is that mongodb by default uses AND, so above query’s result should be an empty array. However, it’s not!
But when I use AND explicitly, the result is an empty array
db.users.find({$and: [{att: 'a'}, {att: ‘b'}]})
In javascript object’s keys should be unique, otherwise the value will be replaced by the latest value
(also mongodb shell is based on js, so it follows some rules of js)
const t = {att: 'a', att: 'b'};
console.log(t);
So in your case your query is acting like this:
db.users.find({att: ‘b’})
You’ve to handle this situation on your code if you want the result be empty in the mentioned condition

In Gatling, how can I generate a random number each time a call is executed? (not using feeder)

I need to find a way to generate a random number each time the REST call is executed.
I have the following GET call:
exec(http("Random execution")
.get("/randomApi")
.queryParam("id", getRandomId()))
}
Obviously it doesn't work as the random number is only generated once and I end up with the same
number whenever this call is executed. I cant use the feeder option as my feeder is already huge and is generated by a 3rd party for each test.
.queryParam takes Expressions as its arguments, and since Expression is an alias for a session function, you can just do...
.queryParam("id", session => getRandomId())
You could also define a second feeder that uses a function to generate the values - no need to update your existing feeder or add another csv file. This would be useful if you had more complicated logic for getting / generating an Id
val idFeeder = Iterator.continually(Map("id" -> Random.nextInt(999999)))
//in your scenario...
.feed(idFeeder)
.exec(http("Random execution")
.get("/randomApi")
.queryParam("id", "${id}")
)
In the spirit of having options, another option you have is to store an object in the session that support toString, which generates whatever you need. It's a nifty trick that you can use for all kinds of things.
object RANDOM_ID {
toString() { return RandomId().toString() }
}
...
exec( _.set( "RANDOM_ID", RANDOM_ID ) )
...
.exec(
http("Random execution")
.get("/randomApi")
.queryParam( "id", "${RANDOM_ID}" )
)
You can apply the same principle to generating random names, addresses, telephone numbers, you name it.
So, which is the better solution? The feeder, or the object in session?
Most of the time, it'll be the feeder, because you control when it is updated. The object in session will be different every time, whereas the feeder solution, you control when the value updates, and then you can reference it multiple times before you change it.
But there may be instances where the stored object solution results in easier to read code, provided you are good with the value changing every time it is accessed. So it's good to know that it is an option.

AQL: (why) are document functions like HAS, UNSET not working?

I try to use document functions like HAS, UNSET etc. (hopefyully) like they are described in the documentation. Unofortunately the lead to Syntax error 1501. I also see that they do NOT get highlighted in the AQL editor like the other signal words do.
Here is one example (which I also tested on the tutorial server):
FOR u IN users
LIMIT 1
UNSET(u, "birthday")
RETURN u
Does anybody sees what's wrong?
An AQL function cannot appear on the top-level of an AQL. The only things allowed on the top-level are statements such as FOR, FILTER, RETURN, LET, COLLECT, SORT, INSERT etc.
If a function should be executed, it's return value should be captured inside a LET statement for further processing, or, if no further processing is required, the function can be called in a RETURNs expression, e.g.
FOR u IN users
LIMIT 1
RETURN UNSET(u, "birthday")
OK, OK ... after writing this I got it: One has to assign this to something. e.g.
FOR u IN users
LIMIT 1
LET tmp = UNSET(u, "birthday")
RETURN tmp
Sorry for posting it ... but I keep it in, maybe other beginners do the same mistake :-)
This may be helpful for other users: The UNSET function does not actually replace the document in the collection. To do this, you need to run
FOR u IN users
LIMIT 1
LET u_new = UNSET(u, "birthday")
REPLACE u WITH u_new IN users

Best practice to pass query conditions in ajax request

I'm writing a REST api in node js that will execute a sql query and send the results;
in the request I need to send the WHERE conditions; ex:
GET 127.0.0.1:5007/users //gets the list of users
GET 127.0.0.1:5007/users
id = 1 //gets the user with id 1
Right now the conditions are passed from the client to the rest api in the request's headers.
In the API I'm using sequelize, an ORM that needs to receive WHERE conditions in a particular form (an object); ex: having the condition:
(x=1 AND (y=2 OR z=3)) OR (x=3 AND y=1)
this needs to be formatted as a nested object:
-- x=1
-- AND -| -- y=2
| -- OR ----|
| -- z=3
-- OR -|
|
| -- x=3
-- AND -|
-- y=1
so the object would be:
Sequelize.or (
Sequelize.and (
{x=1},
Sequelize.or(
{y=2},
{z=3}
)
),
Sequelize.and (
{x=3},
{y=1}
)
)
Now I'm trying to pass a simple string (like "(x=1 AND (y=2 OR z=3)) OR (x=3 AND y=1)"), but then I will need a function on the server that can convert the string in the needed object (this method in my opinion has the advantage that the developer writing the client, can pass the where conditions in a simple way, like using sql, and this method is also indipendent from the used ORM, with no need to change the client if we need to change the server or use a different ORM);
The function to read and convert the conditions' string into an object is giving me headache (I'm trying to write one without success, so if you have some examples about how to do something like this...)
What I would like to get is a route capable of executing almost any kind of sql query and give the results:
now I have a different route for everything:
127.0.0.1:5007/users //to get all users
127.0.0.1:5007/users/1 //to get a single user
127.0.0.1:5007/lastusers //to get user registered in the last month
and so on for the other tables i need to query (one route for every kind of request I need in the client);
instead I would like to have only one route, something like:
127.0.0.1:5007/request
(when calling this route I will pass the table name and the conditions' string)
Do you think this solution would be a good solution or you generally use other ways to handle this kind of things?
Do you have any idea on how to write a function to convert the conditions' string into the desired object?
Any suggestion would be appreciated ;)
I would strongly advise you not to expose any part of your database model to your clients. Doing so means you can't change anything you expose without the risk of breaking the clients. One suggestion as far as what you've supplied is that you can and should use query parameters to cut down on the number of endpoints you've got.
GET /users //to get all users
GET /users?registeredInPastDays=30 //to get user registered in the last month
GET /users/1 //to get a single user
Obviously "registeredInPastDays" should be renamed to something less clumsy .. it's just an example.
As far as the conditions string, there ought to be plenty of parsers available online. The grammar looks very straightforward.
IMHO the main disadvantage of your solution is that you are creating just another API for quering data. Why create sthm from scratch if it is already created? You should use existing mature query API and focus on your business logic rather then inventing sthm new.
For example, you can take query syntax from Odata. Many people have been developing that standard for a long time. They have already considered different use cases and obstacles for query API.
Resources are located with a URI. You can use or mix three ways to address them:
Hierarchically with a sequence of path segments:
/users/john/posts/4711
Non hierarchically with query parameters:
/users/john/posts?minVotes=10&minViews=1000&tags=java
With matrix parameters which affect only one path segment:
/users;country=ukraine/posts
This is normally sufficient enough but it has limitations like the maximum length. In your case a problem is that you can't easily describe and and or conjunctions with query parameters. But you can use a custom or standard query syntax. For instance if you want to find all cars or vehicles from Ford except the Capri with a price between $10000 and $20000 Google uses the search parameter
q=cars+OR+vehicles+%22ford%22+-capri+%2410000..%2420000
(the %22 is a escaped ", the %24 a escaped $).
If this does not work for your case and you want to pass data outside of the URI the format is just a matter of your taste. Adding a custom header like X-Filter may be a valid approach. I would tend to use a POST. Although you just want to query data this is still RESTful if you treat your request as the creation of a search result resource:
POST /search HTTP/1.1
your query-data
Your server should return the newly created resource in the Location header:
HTTP/1.1 201 Created
Location: /search/3
The result can still be cached and you can bookmark it or send the link. The downside is that you need an additional POST.

Resources