Asterisk Hangup Handler using lua - lua

I am trying to Add a hangup handler in my dial plan to catch unanswered calls from dial with multiple extensions. But unfortunately I my hangup handlers are not executing at all.
Below is my extensions.lua
CONSOLE = "Console/dsp" -- Console interface for demo
IAXINFO = "guest" -- IAXtel username/password
TRUNK = "SIP"
GSMTRUNK = "Dongle/dongle0/"
PRITRUNK = "DAHDI/g0/"
TRUNKMSD = 1
local inspect = require ('inspect')
--from here onwards our logic is there Rajesh
function dialtomobind(number)
app.Dial(GSMTRUNK..number, 50,'egb(add-hangup-handler^'..number..'^1)')
local dialstatus = channel['DIALSTATUS']:get()
app.NoOp('DIAL STATUS:'..dialstatus)
app.Hangup(16)
end
function add_hangup_handler(context, extension)
channel['hangup_handler_push']='hangup-handlers,s,1'
app.NoOp('pre-dial handler, Adding Hangup Handler'..channel['hangup_handler_push']:get())
app.Return()
end
function h_handler(context, extension)
app.NoOp('After Hangup:'..extension)
--app.DumpChan(3)
end
function directdialing(c,e)
app.Answer(5)
app.Playback('tt-monkeys')
dialtomobind(string.sub(e, 4))
app.Hangup(16)
end
function hangup_handler_1(c,e)
--channel['hangup_handler_push']
app.NoOp('Hangup Handler 1 Executed')
app.Return()
end
extensions = {
['from-internal-sip']={
['h']=h_handler;
['_500XXXXXXXXXX']=directdialing;
};
['dongle_incoming']={
['_.']=gsm_dongle_handler;
};
['add-hangup-handler']={
['_.']=add_hangup_handler;
};
['hangup-handlers']={
['_.']=hangup_handler_1;
};
}
ASTERISK CLI OUT PUT:
-- Executing [5008884882772#from-internal-sip:1] Answer("SIP/8884882772-00000047", "5")
> 0x7f14c80072a0 -- Probation passed - setting RTP source address to 192.168.2.30:54032
[Aug 29 18:24:17] NOTICE[26470][C-000000c5]: res_rtp_asterisk.c:4478 ast_rtp_read: Unknown RTP codec 95 received from '192.168.2.30:54032'
-- Executing [5008884882772#from-internal-sip:1] Playback("SIP/8884882772-00000047", "tt-monkeys")
-- <SIP/8884882772-00000047> Playing 'tt-monkeys.gsm' (language 'en')
-- Executing [5008884882772#from-internal-sip:1] Dial("SIP/8884882772-00000047", "Dongle/dongle0/8884882772,50,egb(add-hangup-handler^8884882772^1)")
-- Dongle/dongle0-010000000e Internal Gosub(add-hangup-handler,8884882772,1) start
-- Executing [8884882772#add-hangup-handler:1] NoOp("Dongle/dongle0-010000000e", "pre-dial handler, Adding Hangup Handlerhangup-handlers,s,1")
-- Executing [8884882772#add-hangup-handler:1] Return("Dongle/dongle0-010000000e", "")
== Spawn extension (dongle_incoming, 5008884882772, 1) exited non-zero on 'Dongle/dongle0-010000000e'
-- Dongle/dongle0-010000000e Internal Gosub(add-hangup-handler,8884882772,1) complete GOSUB_RETVAL=
-- Called Dongle/dongle0/8884882772
-- Dongle/dongle0-010000000e is making progress passing it to SIP/8884882772-00000047
== Spawn extension (from-internal-sip, 5008884882772, 1) exited non-zero on 'SIP/8884882772-00000047'
-- Executing [h#from-internal-sip:1] NoOp("SIP/8884882772-00000047", "After Hangup:h")

Hangup handler is dialplan handler.
So if you want use it, start OTHER lua script at submited context/extension on hangup.
Extension hangup-handlers,s,1 have be valid and exist.
You have use
app.Set('CHANNEL(hangup_handler)=hangup-handlers,s,1');
becuase it is not variable, it is function.

hangup_handler_push is not at all a channel variable. So Can't access using below methods.
channel['hangup_handler_push']='hangup-handlers,s,1'
or
channel['hangup_handler_push']='hangup-handlers,s,1'
To setup a hangup handler we have to use dial plan function CHANNEL(https://wiki.asterisk.org/wiki/display/AST/Hangup+Handlers). So below methods will be working.
channel.CHANNEL('hangup_handler_push'):set('hangup-handlers,s,1')
As Asterisk Set Application Can be used to set a channel Variables as well as can call a asterisk Functions also. So below code also working.
app.Set('CHANNEL(hangup_handler)=hangup-handlers,s,1');
Reference:
Dial Plan Functions In Lua
https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=16548029

Related

Changing contents of global variables in a Lua script for Awesome Window Manager?

So I've been trying to configure my Awesome WM config (rc.lua) to detect if my IBM model M13 is connected to my laptop upon login/reset. This is to change what the modkey should be since the M13 doesn't have a super key.
The following code makes sense to me and changes modkey within the function being made for the awful.spawn.easy_async function, but after finishing the modkey changes back to Mod4.
modkey = "Mod4"
awful.spawn.easy_async(
"xinput list",
function(stdout, stderr, reason, code)
local msg = "Regular keyboard Modkey = Super"
-- Debug notification that shows that the modkey is
-- at its default for the superkey Mod4
naughty.notify({
text = modkey,
timeout =7
})
if code ~= 0 then
msg = "Missing xinput to see devices\nModkey = Super"
elseif stdout:match("CHESEN") == "CHESEN" then
-- CHESEN is my PS/2 to USB adapter
msg = "IBM M13 detected\nModkey = Alt"
modkey = "Mod1" -- Sets new modkey to Alt
end
-- Notification message
naughty.notify({
text = msg,
timeout =7
})
end
)
-- Debug notification to verify key but key goes back to Mod4
naughty.notify({
text = modkey,
timeout =7
})
The output can be seen here. It doesn't print the notifications in order but the prints of Mod 4 are both of the debug prints.
Notification Output
I don't use Lua much aside from changing my configs from time to time so I'm having difficulty understanding how my global variable modkey can be changed with out it resetting. Other methods I tried was to have the function defined as a function I called setModKey to be passed as a parameter to easy_async and I tried setting modkey using _G to set it as _G.modkey, but I end up getting the same result.
Am I missing something fundamental to Lua or is this affected by how Awesome WM utilizes Lua? Any help will be very appreciated.
Use io.popen instead of awful.spawn.easy_async. Yes, normally using io.popen is really not recommended, but here the following happens:
Awesome starts
You call easy_async to capture the output of xinput list
Since it is async, your config continues to be loaded, so e.g. all your keybindings are set
easy_async does its job and you set modkey to something else.
This means that any keybinding which will be defined from now on use the new modkey, but all already-existing keybindings are not modified by this. So, basically nothing happens.
And for your debugging calls to naughty.notify: The one after the function is triggered first and only then, later, the inner one triggers. So it does not go back, but instead you first show the old value and only later the new one.

nodemcu lua dofile - how to invoke another lua file?

I have a working init.lua on my nodemcu esp8266:
-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there
dofile("credentials.lua")
function startup()
if file.open("init.lua") == nil then
print("init.lua deleted or renamed")
else
print("Running")
file.close("init.lua")
-- the actual application is stored in 'application.lua'
-- dofile("application.lua")
end
end
print("Connecting to WiFi access point...")
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PASSWORD)
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default
tmr.create():alarm(1000, tmr.ALARM_AUTO, function(cb_timer)
if wifi.sta.getip() == nil then
print("Waiting for IP address...")
else
cb_timer:unregister()
print("WiFi connection established, IP address: " .. wifi.sta.getip())
print("You have 3 seconds to abort")
print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end
end)
It runs without errors and the wireless connection is established.
Now I have written a second bme280_mqtt.lua that I want to run automatically:
alt=45 -- altitude of the measurement place
bme280.init(3, 4)
P, T = bme280.baro()
-- convert measure air pressure to sea level pressure
QNH = bme280.qfe2qnh(P, alt)
ldk=string.format("Ld=%d.%03d ", QNH/1000, QNH%1000)
H, T = bme280.humi()
if T<0 then
temp=string.format("T=-%d.%02d°C ", -T/100, -T%100)
else
temp=string.format("T=%d.%02d°C ", T/100, T%100)
end
luftf=string.format("Lf=%d%% ", H/1000, H%1000)
D = bme280.dewpoint(H, T)
if D<0 then
taupu=string.format("Tp=-%d.%02d°C ", -D/100, -D%100)
else
taupu=string.format("Tp=%d.%02d°C ", D/100, D%100)
end
m = mqtt.Client("wetterstation", 120)
m:connect("192.168.1.116", 1883)
m:publish("wetterstation",temp .. taupu .. luftf .. ldk,0,0)
node.dsleep(10*1000000)
Called by hand in ESPlorer via Send to ESP Button everything works fine.
But with
dofile(bme280_mqtt.lua)
I get:
dofile('bme280_mqtt.lua')
bme280_mqtt.lua:25: not connected
stack traceback:
[C]: in function 'publish'
bme280_mqtt.lua:25: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
What is the mistake here? And how do I call the bme280_mqtt.lua from init.lua correctly?
Kind regards
how do I call the bme280_mqtt.lua from init.lua correctly?
You do invoke it correctly.
bme280_mqtt.lua:25: not connected
Means that there's an error on/from line 25 from bme280_mqtt.lua.
I didn't count the lines but the problem is right here
m:connect("192.168.1.116", 1883)
m:publish("wetterstation",temp .. taupu .. luftf .. ldk,0,0)
You can only publish once the connection to the broker was established. Look at the example at http://nodemcu.readthedocs.io/en/latest/en/modules/mqtt/#example. You can either use the callback function in the connect function to publish or register an on-connect event handler before you call connect like so:
m:on("connect", function(client)
-- publish here
end)

Lua FIFO / QUEUE file

I'm quite new to Lua and Embedded programming. I'm working on a project:
IoT node that can be abstractedinto two parts: sensor and a board that runs Open WRT with Lua 5.1. I'm writing script that would be called from crontab every minute.
In my script I'm accessing data from the sensor via package written with C. The result of reading data from sensor is 'hexadecimal numbers returned in string:
4169999a4180cccd41c9851f424847ae4508e0003ddb22d141700000418e666641c87ae14248147b450800003dc8b439
Then convert it (string) to values I need and POST it to API.
Problem:
Sometimes API is not reachable due to poor network connection.
So I need to implement system where I would read a data from a sensor and then if API is not responding, I would save it to a FIFO queue (buffer). And then next time when script is called to read it would be sending 'old' records first and the newest one and the end.
local queue_filespec = [[/path/to/your/queue/file]]
-- Initially your "queue file" (regular file!) must contain single line:
-- return {}
local function operation_with_queue(func)
local queue = dofile(queue_filespec)
local result = func(queue)
for k, v in ipairs(queue) do
queue[k] = ("%q,\n"):format(v)
end
table.insert(queue, "}\n")
queue[0] = "return {\n"
queue = table.concat(queue, "", 0)
local f = assert(io.open(queue_filespec, "w"))
f:write(queue)
f:close()
return result
end
function add_to_queue(some_data)
operation_with_queue(
function(queue)
table.insert(queue, some_data)
end
)
end
function extract_from_queue()
-- returns nil if queue is empty
return operation_with_queue(
function(queue)
return table.remove(queue, 1)
end
)
end
Usage example:
add_to_queue(42)
add_to_queue("Hello")
print(extract_from_queue()) --> 42
print(extract_from_queue()) --> Hello
print(extract_from_queue()) --> nil

NodeMCU timeout when using while loop

I have a Lua script that sends an email to myself via SMTP. Everything works fine when uploading to the NodeMCU and saying dofile("sendemail.lua").
-- sendmail.lua
-- The email and password from the account you want to send emails from
MY_EMAIL = "REDACTED"
EMAIL_PASSWORD = "REDACTED"
-- The SMTP server and port of your email provider.
-- If you don't know it google [my email provider] SMTP settings
SMTP_SERVER = "isp.smtp.server"
SMTP_PORT = 25
-- The account you want to send email to
mail_to = "REDACTED"
-- Your access point's SSID and password
SSID = "REDACTED"
SSID_PASSWORD = "REDACTED"
-- configure ESP as a station
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID,SSID_PASSWORD)
wifi.sta.autoconnect(1)
email_subject = ""
email_body = ""
count = 0
local smtp_socket = nil -- will be used as socket to email server
-- The display() function will be used to print the SMTP server's response
function display(sck,response)
print(response)
end
-- The do_next() function is used to send the SMTP commands to the SMTP server in the required sequence.
-- I was going to use socket callbacks but the code would not run callbacks after the first 3.
function do_next()
if(count == 0)then
count = count+1
IP_ADDRESS = wifi.sta.getip()
smtp_socket:send("HELO "..IP_ADDRESS.."\r\n")
elseif(count==1) then
count = count+1
smtp_socket:send("AUTH LOGIN\r\n")
elseif(count == 2) then
count = count + 1
smtp_socket:send("REDACTED".."\r\n")
elseif(count == 3) then
count = count + 1
smtp_socket:send("REDACTED".."\r\n")
elseif(count==4) then
count = count+1
smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n")
elseif(count==5) then
count = count+1
smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n")
elseif(count==6) then
count = count+1
smtp_socket:send("DATA\r\n")
elseif(count==7) then
count = count+1
local message = string.gsub(
"From: \"".. MY_EMAIL .."\"<"..MY_EMAIL..">\r\n" ..
"To: \"".. mail_to .. "\"<".. mail_to..">\r\n"..
"Subject: ".. email_subject .. "\r\n\r\n" ..
email_body,"\r\n.\r\n","")
smtp_socket:send(message.."\r\n.\r\n")
elseif(count==8) then
count = count+1
tmr.stop(0)
smtp_socket:send("QUIT\r\n")
print("msg sent")
else
smtp_socket:close()
end
print(count)
end
-- The connectted() function is executed when the SMTP socket is connected to the SMTP server.
-- This function will create a timer to call the do_next function which will send the SMTP commands
-- in sequence, one by one, every 5000 seconds.
-- You can change the time to be smaller if that works for you, I used 5000ms just because.
function connected(sck)
tmr.alarm(0,5000,1,do_next)
end
-- #name send_email
-- #description Will initiated a socket connection to the SMTP server and trigger the connected() function
-- #param subject The email's subject
-- #param body The email's body
function send_email(subject,body)
count = 0
email_subject = subject
email_body = body
smtp_socket = net.createConnection(net.TCP,0)
smtp_socket:on("connection",connected)
smtp_socket:on("receive",display)
smtp_socket:connect(SMTP_PORT, SMTP_SERVER)
end
-- Send an email
send_email("ESP8266", "[[Hi, How are your IoT projects coming along? Best Wishes,ESP8266]]")
However, I want to use a loop to monitor an analog input value and only send the email when certain analog input values are detected. Therefore, I added this code at the end of the script, after the sendemail() function definition and immediately before the function sendmail('subject', 'body') is called
vp = 0
gpio.mode(vp, gpio.INPUT)
while true do
local v = adc.read(vp)
if v < 840 or v > 870 then
print(v)
break
end
tmr.wdclr()
end
sendmail('subject', 'body')
The while loop works perfectly, waiting indefinitely for input from the analog pin. Once that input is found, it breaks correctly and calls the sendmail function. However, once that function is called, NodeMCU eventually resets. Sometimes it will get as far as successfully authenticating the SMTP credentials with the server, and sometimes it will not even make the HELO before it shuts down. What could possibly be causing this? Why would the sendmail.lua script work fine then suddenly decide not to work when adding this one small while loop that appears to work perfectly fine on its own?
A little quote from the NodeMCU reference:
tmr.wdclr() Feed the system watchdog.
In general, if you ever need to use this function, you are doing it
wrong.
The event-driven model of NodeMCU means that there is no need to be
sitting in hard loops waiting for things to occur. Rather, simply use
the callbacks to get notified when somethings happens. With this
approach, there should never be a need to manually feed the system
watchdog.
Please note the second line. :)
Not sure what your problem is, but why do you use a while loop in the first place? Why not use timer events to poll your ADC regularly?
Maybe the watchdog is triggered because your feed comes to late for some reason. In the if case you don't feed it at all befor you leave the loop.
Even if it may not be the definite answer I post it as such since the comment input is too small.
First, I suggest you use the script I posted for your previous question. This one isn't handling WiFi setup correctly. You need to wait in a timer until the device got an IP before you can continue. Remember, wifi.sta.config is non-blocking. And since it uses auto-connect=true if not set explicitly it'll try to connect to the AP immediately. That's also the reason why wifi.sta.autoconnect(1) is superfluous.
I don't understand the ADC reading code you posted.
vp = 0
gpio.mode(vp, gpio.INPUT)
Seems unnecessary to me because a) you don't do anything with GPIO 0 and b) adc.read only supports 0.
Rather than using a busy loop and constantly feeding the watch dog, which is a very bad sign, I suggest you use an interval based timer. Furthermore, I guess you don't wanna break the loop the first time the condition is met and never come back? So, you need to stay in the loop and keep triggering send mail, no? Something like this maybe (untested):
tmr.alarm(0, 200, tmr.ALARM_AUTO, function()
local v = adc.read(0)
if v < 840 or v > 870 then
node.task.post(function()
send_email("ESP8266", "[[Hi, How are your IoT projects coming along? Best Wishes,ESP8266]]")
end)
end
end)

How do i use socket.select?

I need some help using socket "select" function.
My server code is like this:
while true do
for _,server in pairs(servers) do
local client = server:accept()
client:settimeout(5)
local line, err = client:receive()
if not err then
client:send(line .. "_SERVER_SIDE\n")
else
client:Send("___ERRORPC"..err)
end
client:close()
end
end
But now i want to use the select function instead of make a forever loop like this.
Reading this: http://w3.impa.br/~diego/software/luasocket/socket.html
I know that i can use something simmilar than:
socket.select(servers, nil, 5)
But i don´t know how i can use this on the code above. Can anyone help me?
I will have to use this inside a while true statement?
The reading operation (first parameter) means that i can only make an accept/receive]? And the seconds parameter means that i can only make a send?
As per the documentation, select receives one or two arrays of sockets and returns an array of sockets that can safely be read from without blocking and an array of sockets that can be safely written to without blocking and an array of sockets that can safely be written without blocking. An important point is that the first array is for both server sockets that want you want to call accept on and for client sockets that you want to call receive on.
The seconds parameter is just a timeout for the select. It doesn't have to do with how many operations you can make.
The basic thing you are going to have to change in your code is that when a receive call fails with a timeout, instead or giving an error you should add that socket to the array of sockets that you pass to select. This way you can have select tell you when that socket becomes active again.
From the documentation for select: "calling select with a server socket in the receive parameter before a call to accept does not guarantee accept will return immediately. Use the settimeout method or accept might block forever." This means that you'd need to use settimeout before your accept call, but assuming you have a list of opened connections you can work with in servers table, you can use select in the following way:
local canread = socket.select(servers, nil, 1)
for _,client in ipairs(canread) do
local line, err = client:receive()
if not err then
client:send(line .. "_SERVER_SIDE\n")
else
client:send("___ERRORPC"..err)
end
end
socket.select will block for up to 1 second, but will return sooner if there is a socket from the list you provided that can be read from. You can block indefinitely if you use socket.select(servers, nil, 0); blocking for some short time is useful if you need to do some other work while waiting for the input.
Updated to use ipairs instead of pairs as the returns table is keyed both on numbers as well as on sockets themselves, so if one socket can be read from, the returned array looks like {[1] = sock, [sock] = 1}.
single demo
local server = socket.bind("*",7777)
local client_tab = {}
while true do
-- socket.select first param is a table of connected socket,
-- you want a connected socket,you need to call accept()
-- if you do not want to block,you should call settimeout(seconds)
local recvt = socket.select(client_tab, nil, 1)
server:settimeout(1)
local client = server:accept()
if client then
client_tab[#client_tab+1] = client
end
if #recvt > 0 then
-- read clients in recvt
end
end

Resources