asterisk ari calling stuck in ringing with python - python

I'm quite new with ARI scripting for Asterisk, and I've been trying to make some script to handle a 1 to 1 communication with ari-py in python. I've been following the example that provided in the asterisk wiki and so far so good. But when I try to create a call, the recipient always keep ringing, even if I have answered it. Is there something wrong with how I handle the call? Here's my script
def stasis_start_cb(self, channel, ev):
"""Handler for StasisStart event"""
chan = channel.get('channel')
chan.answer()
print "Channel %s has entered the application" % chan.json.get('name')
outgoing = client.channels.originate(endpoint="SIP/1002", extension='1002', callerId='Tes', app='channel-dump', appArgs='dialed')
I tried using OOP to simplify the function usage, are there anything wrong with that script? And here's another script trying to make a call by using a bridge:
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
def outgoing_start(self, bri, channel):
channel.answer()
self.addChan(channel, bridge)
def stasis_start(self, channel, ev):
chan = channel.get('channel')
name = chan.json.get('name')
"""ars = ev.get('args')
if not ars:
print "Error: {} didn't provide any arguments!".format(name)
return
if ars and ars[0] != 'inbound':
return
if len(ars) != 2:
print "Error: {} didn't tell us who to dial".format(name)
chan.hangup()"""
print "Channel {} entered the application".format(name)
chan.ring()
self.outgoing_call(chan)
self.outgoing_start(bridge, chan)
Both the client is able to be added in the bridge, and I can make a call, but the problem still persist, the recipient keep saying they are ringing despite I have answered the call

Turns out, the problem is in here
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
As the dialed number answer the call, they uses the same script, so they ended up calling themselves again. A simple if condition to make the dialed number not call to itself again is all that is needed

Related

How do I handle different errors when working with an API?

I am working a project in Python were I am gathering rain data and in this case temperature data from the Netatmo weather stations (Its basically just a private weather station you can set up in you garden and it will collect rain data, temperature, wind etc.)
When using the patatmo API you need a user with credential, a client. This client then has 500 requests pr. Hour which can be used on different requests, among these are the client.GetPublicdata request and the client.Getmeassure request. The Getmeassure request requires a station id and a module id, which I get from the the Getpuclicdat request. If I run out of requests I will catch that error using the except ApiResponseError: and I will then change the client credentials since I am not alone in this project and have credentials from two other people. My issues are:
If a station or module ID is not found using the Getmeassure request it will return a different type of ApiResponseError, which in my current code also is caught in the previously mentioned except, and this results in an endless loop where the code just changes credential all the time.
The code looks like this:
from patatmo.api.errors import ApiResponseError
...
...
...
a_test1 = 0
while a_test1 == 0:
try:
Outdoor_data = client.Getmeasure(
device_id = stations_id ,
module_id = modul_ID ,
type = typ ,
real_time = True ,
date_begin = Start ,
date_end = End
)
time.sleep(p)
a_test1 = 1
except ApiResponseError:
credentials = cred_dict[next(ns)]
client = patatmo.api.client.NetatmoClient()
client.authentication.credentials = credentials
client.authentication.tmpfile = 'temp_auth.json'
print('Changeing credentials')
credError = credError + 1
if credError > 4:
time.sleep(600)
credError = 0
pass
except:
print('Request Error')
time.sleep(p)
The documentation for the error.py script, that was made by someone else, looks like this:
class ApiResponseError(BaseException):
pass
class InvalidCredentialsError(ApiResponseError):
pass
class InvalidApiInputError(BaseException):
pass
class InvalidRegionError(InvalidApiInputError):
def __init__(self):
message = \
("'region' required keys: "
"lat_ne [-85;85], lat_sw [-85;85], "
"lon_ne [-180;180] and lon_sw [-180;180] "
"with lat_ne > lat_sw and lon_ne > lon_sw")
super().__init__(message)
class InvalidRequiredDataError(InvalidApiInputError):
def __init__(self):
message = "'required_data' must be None or in {}".format(
GETPUBLICDATA_ALLOWED_REQUIRED_DATA)
super().__init__(message)
class InvalidApiRequestInputError(BaseException):
pass
class InvalidPayloadError(InvalidApiRequestInputError):
pass
API_ERRORS = {
"invalid_client": InvalidCredentialsError("wrong credentials"),
"invalid_request": ApiResponseError("invalid request"),
"invalid_grant": ApiResponseError("invalid grant"),
"Device not found": ApiResponseError("Device not found - Check Device ID "
"and permissions")
}
What I want to do is catch the error depending what type of error I get, and I just doesn't seem to have any luck doing so
Which of those exceptions subclasses do you get on request overrun? Use only that one to drive the auth swap. If it’s not a particular one but is shared with other bad events, you will need to examine the exception’s variables. Also you will need to figure out which exception to work around - bad station id might mean someone is offline so ignore and try later. Vs logic flaws in your program, abend on those, fix, retry.
Exception handling is on a first-catch, first-handled basis. Your most specific classes have to do the “except” first to match first, else a generic one would grab it and handle it. Watch your APIs exception hierarchy carefully!
maxexc = 1000
countexc = 1
while ...
# slow your loop
time.sleep(p)
try:
... what you normally do
# dont use the too generic ApiResponseError here yet
except requestoverrunexception as e:
... swap credendentials
countexc+=1
except regionexception as e:
... ignore this region for a while
countexc += 1
# whoops abend and fix
except ApiResponseError as e:
print vars(e)
raise
except Exception as e:
print(e)
raise
I would bail after too many exceptions, thats what countexc is about.
Also 500 requests an hour seems generous. Don’t try to fudge that unless you have a real need. Some providers may even have watchdogs and get rid of you if you abuse them.

nfcpy: How to get on-release event correctly with NFCPY?

I try to listen to different RFID ID cards with a ACR122 reader and the nfcpy python library.
I would like to have the card's ID when the user connect it (without recognized it over and over) and get an event when user release it. Ideally in a loop, in order to listen to the next card when the user take his card away.
Below is my code, but the on-release event is fired even if the card is still on the reader. What is the correct way to
Get on-connect without recognizing over and over ?
Get on-release when user the card is away ?
import nfc
def on_startup(targets):
return targets
def on_connect(tag):
uid = str(tag.identifier).encode("hex").upper()
print(uid)
return True
def on_release(tag):
print('Released')
return tag
rdwr_options = {
'on-startup': on_startup,
'on-connect': on_connect,
'on-release': on_release,
'beep-on-connect': False,
}
with nfc.ContactlessFrontend('usb') as clf:
tag = clf.connect(rdwr=rdwr_options)
You might need to set an interval in your ContactlessFrontend config. Try this example:
import nfc
import ndef
tags = set()
rec = ndef.UriRecord("https://google.com")
def on_connect(tag):
if tag.identifier not in tags:
tags.add(tag.identifier)
fmt = tag.format()
if fmt is None:
print("Tag cannot be formatted (not supported).")
elif fmt is False:
print("Tag failed to be formatted (for some reason).")
else:
tag.ndef.records = [rec]
if __name__ == "__main__":
clf = nfc.ContactlessFrontend()
if not clf.open('usb'):
raise RuntimeError("Failed to open NFC device.")
while True:
config = {
'interval': 0.35,
'on-connect': on_connect
}
ret = clf.connect(rdwr=config)
if ret is None:
pass
elif not ret:
print ("NFC connection terminated due to an exception.")
break
else:
pass
clf.close()
https://gist.github.com/henrycjc/c1632b2d1f210ae0ff33d860c7c2eb8f
This discussion helped me figuring out how to solve this.
When reading the docs (‘on-release’ : function(tag)) very carefully – yes, it took me some loops – it becomes apparent that on-release is called as soon as on-connect returns True.
This function is called when the presence check was run (the ‘on-connect’ function returned a true value) and determined that communication with the tag has become impossible, or when the ‘terminate’ function returned a true value. The tag object may be used for cleanup actions but not for communication.
It seems that on-release must not be understood in a physical way, but rather in a communicative way (Released from communications, you may now remove the card).
To solve this issue, one needs to determine whether a card is present after it connected (or more precisely, after it was released – more about that later). The following code does the trick:
import nfc
import time
import logging
import inspect
logging.basicConfig(format="[%(name)s:%(levelname).4s] %(message)s")
logging.getLogger().setLevel(logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
def on_startup(targets):
logger.debug(inspect.currentframe().f_code.co_name)
for target in targets:
target.sensf_req = bytearray.fromhex("0012FC0000")
return targets
def on_discover(target):
logger.debug(inspect.currentframe().f_code.co_name)
logger.info(target)
return True
def on_connect(tag):
logger.debug(inspect.currentframe().f_code.co_name)
logger.info(tag)
return True
def on_release(tag):
logger.debug(inspect.currentframe().f_code.co_name)
# Loop while card is present
while True:
time.sleep(1)
if not clf.sense(*[nfc.clf.RemoteTarget(target) for target in rdwr_options["targets"]]):
logger.info("Card removed")
break
return True
rdwr_options = {
"targets": ("106A", "106B", "212F"),
"on-startup": on_startup,
"on-discover": on_discover, # Here just for completeness :)
"on-connect": on_connect,
"on-release": on_release,
}
if __name__ == "__main__":
logger.debug(inspect.currentframe().f_code.co_name)
with nfc.ContactlessFrontend() as clf:
if not clf.open("usb"):
raise RuntimeError("Failed to open NFC device.")
while True:
ret = clf.connect(rdwr=rdwr_options)
if not ret:
break
Now is later: If we wait for removal of the card during the on-connect state, we run into trouble as on-release expects to retrieve information from the card (tag argument), which it cannot get anymore as communication is not possible with the card removed.
PS: The above mentioned discussion reads that the behavior of on-release depends on the type of card one is using.
So, I have a need to register when a card is present and when it leaves again. I have some Type2 tags for this.
Given this code:
def connected(tag):
print(tag)
return True def released(tag):
print("Bye")
tag = clf.connect(rdwr={'on-connect': connected, 'on-release':
released})
I would expect it to echo out the Tag ID when I present it to the
reader, and the echo "Bye" once I remove it. This works as expected on
a Type4 tag I have..
I'm afraid those cards are Mifare Classic 1K not supported by nfcpy. It is possible to read the UID but any other command requires to first authenticate and use the Mifare Crypto scheme. This should be doable with NXP reader ICs [...]. And NXP has a good selection of NFC Forum compatible Type 2 Tags that work just great.

Not receiving a return message in python 2 code

I'm pretty new to python and just learning to ropes. In the code bellow I have a function taking several inputs from a json string. I'm attempting to have a return output in the specified strings. Problem? when I run the file I get nothing... I'm sure I'm missing something incredibly simply, but for the life of me I can't figure out what. I've attempted to use return as well as print at the end of the function. No cheese.
Help?
Here's what I've got so far:
import datetime, json
def jeeves(request): #defines the function
message=''
if request['type']=='maintainance':
message='Thank you tenant at unit'+str(request['unit'])+', your request for maintenance to deal with '+'"'+str(request['issue'])+'"'+' has been received #2 input'
elif request['type']=='purchase':
message='Thank you tenant at unit'+str(request['unit'])+'your request to purchase a'+str(request['commodity'])+ ' has been received'
elif request['type']=='reservation':
startTime=request['date'].split(" ")[1]
startTime=startTime.split('')
time=0;
num=[]
for item in startTime:
if isdigit(item):
num.append(item)
for index in range(len(num)):
time+=num[index]*10**(len(num)-index)
endTime=0
daySplit=''.join(startTime[-2:])
if time+int(request['duration'].split(' ')[0])>12:
endTime=time+int(request['duration'].split(' ')[0])-12
if daySplit=='AM':
endTime=str(endTime)+'PM'
else:
endTime=str(endTime)+'AM'
else:
endTime=endTime+int(request['duration'].split(' ')[0])
endTime=str(endTime)+daySplit
message='Thank you tenant at unit'+str(request['unit'])+'your request to reserve our '+str(request['location'])+' on '+str(request['date'].split(' ')[0])+' from '+str(request['date'].split(' ')[1])+' to '+ endTime+' has been received'
elif request['type']=='complaint':
message='Thank you tenant at unit'+str(request['unit'])+' we will have someone follow up on '+'"'+request['issue']+'"'+' in regards to our '+request['location']
return message
print message
json.dumps(jeeves({"type":"maintenance", "unit":221, "issue":"Air filter needs replacing"}))
ps: I'm new to coding in general. If there is a better, more productive way for me to ask questions, I'm open to feedback. Thank you in advanced.
You have to put return before the print function because when you use return it ends a function. You might also want to check out what return actually does here

BLE subscribe to notification using gatttool or bluepy

I am writing a program using bluepy that listen for a characteristic sent by a bluetooth device. I can also use any library or language, the only constraint is to run on Linux and not in mobile environment (it seems is widely used only in mobile devices, no one use BLE with desktop).
Using bluepy I register the delegate and after trying to register for notification calling write('\x01\x00') as described in the bluetooth rfc.
But it doesn't work, any notification for the characteristic is received.
Maybe I am wrong in writing the message for subscribing.
Is there an error in the small snippet I wrote? Thank you so much.
class MyDelegate(btle.DefaultDelegate):
def __init__(self, hndl):
btle.DefaultDelegate.__init__(self)
self.hndl=hndl;
def handleNotification(self, cHandle, data):
if (cHandle==self.hndl):
val = binascii.b2a_hex(data)
val = binascii.unhexlify(val)
val = struct.unpack('f', val)[0]
print str(val) + " deg C"
p = btle.Peripheral("xx:xx:xx:xx", "random")
try:
srvs = (p.getServices());
chs=srvs[2].getCharacteristics();
ch=chs[1];
print(str(ch)+str(ch.propertiesToString()));
p.setDelegate(MyDelegate(ch.getHandle()));
# Setup to turn notifications on, e.g.
ch.write("\x01\x00");
# Main loop --------
while True:
if p.waitForNotifications(1.0):
continue
print "Waiting..."
finally:
p.disconnect();
I was struggling with this myself, and jgrant's comment really helped. I'd like to share my solution, if it could help anyone.
Note that I needed indication, hence the x02 rather than x01.
If it were possible to read the descriptors using bluepy, I would do that, but it doesn't seem to work (bluepy v 1.0.5). The method in the service class appears to be missing, and the method in the peripheral class gets stuck when I try to use it.
from bluepy import btle
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
def handleNotification(self, cHandle, data):
print("A notification was received: %s" %data)
p = btle.Peripheral(<MAC ADDRESS>, btle.ADDR_TYPE_RANDOM)
p.setDelegate( MyDelegate() )
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID( <UUID> )
ch = svc.getCharacteristics()[0]
print(ch.valHandle)
p.writeCharacteristic(ch.valHandle+1, "\x02\x00")
while True:
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
print("Waiting...")
# Perhaps do something else here
It looks like the problem is that you're trying to write \x01\x00 to the characteristic itself. You need to write it to the Client Characteristic Configuration descriptor that proceeds it (0x2902). The handle is likely 1 greater than the characteristic (but you may want to confirm by reading the descriptors).
ch=chs[1]
cccd = ch.valHandle + 1
cccd.write("\x01\x00")
What was confusing for me was that in https://ianharvey.github.io/bluepy-doc/notifications.html
the part that enabled the notifications was in comments, so it didn't look obligatory to me.
the bare minimum (given you know the MAC-adress already an you included everything and declared the Delegateclass) for me is
p1 = Peripheral(<MAC>)
ch1 = p1.getCharacteristics()[3]
p1.setDelegate(MyDelegate())
p1.writeCharacteristic(ch1.valHandle + 1, b"\x01\x00")
Note that I already knew I wanted to get notifications from characteristic#3.
Also, without the 'b'-bytesprefix infront of "\x0\x00", it wouldn't work for me.
bluepy classes docs and samples are crazy, and not complete. To get more details, just checkout bluepy source (it is not big and easy to read)
But, as starting point you can use this notifications code sample, working with from Heart Rate Service (tested on bluepy 1.3.0)
Don't forget to replace device MAC to your own in Peripheral!
from bluepy import btle
from bluepy.btle import AssignedNumbers
import binascii
class MyDelegate(btle.DefaultDelegate):
def __init__(self, handle):
btle.DefaultDelegate.__init__(self)
self.handle = handle
print "Created delegate for handle", self.handle
# ... more initialise here
def handleNotification(self, cHandle, data):
if(cHandle == self.handle):
print "handleNotification for handle: ", cHandle, "; Raw data: ", binascii.b2a_hex(data)
#Found somewhere. Not tested is this working, but leave here as decode example
#val = binascii.b2a_hex(data)
#val = binascii.unhexlify(val)
#val = struct.unpack('f', val)[0]
#print str(val) + " deg C"
print "Connecting..."
dev = btle.Peripheral("c8:2b:96:a3:d4:76")
try:
print "Device services list:"
for svc in dev.services:
print str(svc)
HRService = dev.getServiceByUUID(AssignedNumbers.heartRate)
print "HRService", HRService
print "HRService characteristics list: "
for char in HRService.getCharacteristics():
print "HRService char[", char.getHandle(), "]: ", char
HRMeasurementChar = HRService.getCharacteristics(AssignedNumbers.heart_rate_measurement)[0] #Notice! Check is characteristic found before usage in production code!
print "HRMeasurementChar", HRMeasurementChar, HRMeasurementChar.propertiesToString();
# Assign delegate to target characteristic
dev.setDelegate(MyDelegate(HRMeasurementChar.getHandle()));
# We need to write into org.bluetooth.descriptor.gatt.client_characteristic_configuration descriptor to enabe notifications
# to do so, we must get this descriptor from characteristic first
# more details you can find in bluepy source (def getDescriptors(self, forUUID=None, hndEnd=0xFFFF))
desc = HRMeasurementChar.getDescriptors(AssignedNumbers.client_characteristic_configuration);
print "desc", desc
print "Writing \"notification\" flag to descriptor with handle: ", desc[0].handle
dev.writeCharacteristic(desc[0].handle, b"\x01\x00")# Notice! Do not use [0] in production. Check is descriptor found first!
print "Waiting for notifications..."
while True:
if dev.waitForNotifications(1.0):
# handleNotification() was called
continue
finally:
dev.disconnect();

Using pyUSB to read data from ELM327 OBDII to USB device

I am having problems using the pyUSB library to read data from an ELM327 OBDII to USB device. I know that I need to write a command to the device on the write endpoint and read the received data back on the read endpoint. It doesn't seem to want to work for me though.
I wrote my own class obdusb for this:
import usb.core
class obdusb:
def __init__(self,_vend,_prod):
'''Handle to USB device'''
self.idVendor = _vend
self.idProduct = _prod
self._dev = usb.core.find(idVendor=_vend, idProduct=_prod)
return None
def GetDevice(self):
'''Must be called after constructor'''
return self._dev
def SetupEndpoint(self):
'''Must be called after constructor'''
try:
self._dev.set_configuration()
except usb.core.USBError as e:
sys.exit("Could not set configuration")
self._endpointWrite = self._dev[0][(0,0)][1]
self._endpointRead = self._dev[0][(0,0)][0]
#Resetting device and setting vehicle protocol (Auto)
#20ms is required as a delay between each written command
#ATZ resets device
self._dev.write(self._endpointWrite.bEndpointAddress,'ATZ',0)
sleep(0.002)
#ATSP 0 should set vehicle protocol automatically
self._dev.write(self._endpointWrite.bEndpointAddress,'ATSP 0',0)
sleep(0.02)
return self._endpointRead
def GetData(self,strCommand):
data = []
self._dev.write(self._endpintWrite.bEndpointAddress,strCommand,0)
sleep(0.002)
data = self._dev.read(self._endpointRead.bEndpointAddress, self._endpointRead.wMaxPacketSize)
return data
So I then use this class and call the GetData method using this code:
import obdusb
#Setting up library,device and endpoint
lib = obdusb.obdusb(0x0403,0x6001)
myDev = lib.GetDevice()
endp = lib.SetupEndpoint()
#Testing GetData function with random OBD command
#0902 is VIN number of vehicle being requested
dataArr = lib.GetData('0902')
PrintResults(dataArr)
raw_input("Press any key")
def PrintResults(arr):
size = len(arr)
print "Data currently in buffer:"
for i in range(0,size):
print "[" + str(i) + "]: " + str(make[i])
This only ever prints the numbers 1 and 60 from [0] and [1] element in the array. No other data has been return from the command. This is the case whether the device is connected to a car or not. I don't know what these 2 pieces of information are. I am expecting it to return a string of hexadecimal numbers. Does anyone know what I am doing wrong here?
If you don't use ATST or ATAT, you have to expect a timeout of 200ms at start, between every write/read combination.
Are you sending a '\r' after each command? It looks like you don't, so it's forever waiting for a Carriage Return.
And a hint: test with 010D or 010C or something. 09xx might be difficult what to expect.
UPDATE:
You can do that both ways. As long as you 'seperate' each command with a carriage return.
http://elmelectronics.com/ELM327/AT_Commands.pdf
http://elmelectronics.com/DSheets/ELM327DS.pdf (Expanded list).
That command list was quite usefull to me.
ATAT can be used to the adjust the timeout.
When you send 010D, the ELM chip will wait normally 200 ms, to get all possible reactions. Sometimes you can get more returns, so it waits the 200 ms.
What you also can do, and it's a mystery as only scantools tend to implement this:
'010D1/r'
The 1 after the command, specifies the ELM should report back, when it has 1 reply from the bus. So it reduces the delay quite efficiently, at the cost of not able to get more values from the address '010D'. (Which is speed!)
Sorry for my english, I hope send you in the right direction.

Categories

Resources