Приветствую, хабрасообщество.
Чуть более года назад мы разрабатывали приложения для Digium телефонов. Несмотря на то, что планы были обширными, мы остановились только на следующих вариациях:
- Погода с сайта гисметео
- Курс валют с сайта центробанка
- RSS лента с новостных порталов
Данные приложения были написаны, чтобы ознакомить сообщество с API и примерами, даже больше just for fun. Cофт, если так можно его назвать, не несет себе никакого уникального применения, которое было бы полезно реальному бизнесу.
Сегодня мы решили вернуться к этой теме, и поделиться другим, на наш взгляд намного более интересным приложением, которое отображает вызов на экране телефона, если пользователи находятся в одной пикап группе и позволяет его перехватить.
Ввиду того, что телефон сам не может обратиться к астериску по HTTP, была выбрана структура клиент-сервер. Сервер на питоне, который «ловит» от астериска CURL запросы при звонке, и javascript на телефоне, который с некоторой периодичностью опрашивает о наличии новых записей.
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer import urlparse import json import shelve import datetime import sys import os TIME_FORMAT = '%d.%m.%Y %H:%M:%S.%f' DB_NAME = "db.db" class HttpProcessor(BaseHTTPRequestHandler): def do_GET(self): if "/get" in self.path: fields = self.get_fields() if not fields: print "There are no parameters" return callgroup = fields.get("callgroup") pickupgroup = fields.get("pickupgroup") if callgroup is None or pickupgroup is None: print "There are no pickupgroup or no callgroup" self.send_error(404) return None db = shelve.open( DB_NAME) data = [] for key in db.keys(): if "callgroup" in db[key] and "pickupgroup" in db[key] and str(db[key]["callgroup"]) == str(callgroup) and str(db[key]["pickupgroup"]) == str(pickupgroup): entry = db[key] data.append(entry) # Make simple dict for json self.send_json(data) db.close() def do_POST(self): data = self.get_json_data() if "uid" not in data: print "There are no a number" return uid = str(data["uid"]) print data if "/put" in self.path: db = shelve.open( DB_NAME) db[uid] = data db.close() data = {"status": 200, "message": "OK"} self.send_json(data) elif "/del" in self.path: db = shelve.open( DB_NAME) if uid in db: del db[uid] data = {"status": 200, "message": "OK"} self.send_json(data) def send_json(self, data): self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(data)) def get_json_data(self): length = int(self.headers.getheader('content-length')) field_data = self.rfile.read(length) try: data = json.loads(field_data) except: print "JSON error:" + field_data self.send_error(404) return None return data def get_fields(self): fields_data = self.path.split("?") if len(fields_data) < 2: return GET = {} args = fields_data[1].split('&') for arg in args: t = arg.split('=') if len(t) > 1: k, v = arg.split('=') GET[k] = v return GET if __name__ == "__main__": if os.path.exists( DB_NAME): os.remove( DB_NAME) host = "192.168.1.254" port = 8000 serv = HTTPServer((host, port), HttpProcessor) print "Server running at {}:{}".format(host, port) serv.serve_forever()
Не забудьте дать этому файлу права на исполнение (chmod +x), запустить и добавить в автозапуск.
Установка приложения на телефон
Как вы наверняка знаете, или слышали, Digium телефон можно настроить с помощью веб-интерфейса, либо с помощью некоего проприетарного провиженинга DPMA. (Digium Phone Module Asterisk).
Если у вас самая простая настройка (через веб), то вам необходимо перейти в меню телефона System tools и нажать Enable App Development, затем залогиниться на телефон по адресу phone_ip-address/app_dev (по дефолту: Юзер: admin, Пароль: 789) и там нажать большую зеленую кнопку Add App.
Вот собственно файл на скачивание самого приложения: pbxware.ru
var incomingGroupCall = {} var screen = require('screen'); //util for debugging var util = require('util'); var app = require('app'); //we needs to get all info about app app.init(); screen.clear(); //Get config of app(we needs settings) var config = app.getConfig(); var callgroup = config.settings.callgroup; //Get callgroup var pickupgroup = config.settings.pickupgroup; //Get pickupgroup var server = config.settings.server; //server uri, like http://{host}:{port} var app_name = config.settings.id; //App name from json file var phonePrefix = config.settings.prefix; //App name from json file var language = config.settings.language || "ru"; var uids = []; // This list contains all uids server give us at runtime var phonesCount = 0; // This variable needs to watch uids missing from uids list var timer; var currentListPos = 0; var listWidget = new List(0, 0, window.w, window.h); var lang = digium.readFile("app", language + ".json"); language = JSON.parse(lang); incomingGroupCall.show = function () { util.debug("Call show"); if (this.visible) { window.add(listWidget); } this.update(); if (timer) { clearInterval(timer); } timer = setInterval(this.update, 1100); }; incomingGroupCall.showGui = function(message, params) { util.debug("Show gui"); var lastSelected = listWidget.selected; listWidget.clear(); listWidget.set(0,0, message); var i=1; if(!params){ return; } params.forEach(function(entry) { var msg = entry.from + " --> " + entry.to; if(i<=9){ msg = "[" + i + "] " + msg; } else if(i === 10) { msg = "[0] " + msg; } else if(i === 11) { msg = "[*] " + msg; } else if(i === 12) { msg = "[#] " + msg; } listWidget.set(i, 0, msg); listWidget.set(i, 1, entry.to); //container to get value in key handler i++; }); listWidget.select(lastSelected); } incomingGroupCall.update = function() { var request = new NetRequest(); request.open("GET", server + "/get?callgroup="+callgroup+"&pickupgroup="+pickupgroup); request.onreadystatechange = function() { //(readyState === 4) indicates a completed request if (4 === request.readyState) { if (200 === request.status) { try { var data = JSON.parse(request.responseText); if (!data || data.length === 0) { if (!digium.app.inForeground) { return; } incomingGroupCall.showGui(language["NO_CALLS"]); return; } //Remove ended calls var currentUids = data.map( function(item) { return item.uid; }); var needsToRefresh = false; var newEntryAvailable = false; uids.forEach(function(uid) { if(currentUids.indexOf(uid) === -1) { uids.splice(uids.indexOf(uid), 1); needsToRefresh = true; } }); // Add new phones to list data.forEach(function(entry) { if (uids.indexOf(entry.uid) === -1) { needsToRefresh = true; newEntryAvailable = true; uids.push(entry.uid); } }); util.debug("New entry:" + newEntryAvailable); if (!digium.app.inForeground && newEntryAvailable) { digium.foreground(); } if(needsToRefresh) { incomingGroupCall.showGui(language["INCOMING_CALLS"], data); } } catch (e) { util.debug('request error: ' + JSON.stringify(e)); incomingGroupCall.showGui(language["SERVER_UNAVAILABLE"]); } } else { util.debug('request error1: ' + request.status); incomingGroupCall.showGui(language["SERVER_UNAVAILABLE"]); } } }.bind(this); request.setTimeout(1000); request.send(); }; //initialize variables incomingGroupCall.init = function () { this.widgets = {}; //stay open when the app is backgrounded digium.app.exitAfterBackground = false; this.visible = digium.app.inForeground; incomingGroupCall.listeners(); // setInterval(digium.restart, 180000); }; incomingGroupCall.listeners = function () { //show the full window when the app is foregrounded digium.event.observe({ 'eventName' : 'digium.app.foreground', 'callback' : function () { util.debug("app.foregrounded"); window.clear(); this.visible = digium.app.inForeground; this.setButtons(); this.show(); }.bind(this) }); //show the idle window when the idleScreen is shown digium.event.observe({ 'eventName' : 'digium.app.background', 'callback' : function () { util.debug("app.background"); this.visible = digium.app.inForeground; window.clearSoftkeys(); this.show(); }.bind(this) }); }; incomingGroupCall.setButtons = function () { window.onkeyselect = function() { var phone = listWidget.get(listWidget.selected, 1); util.debug("Selected " + phone); digium.phone.dial({ "number": phonePrefix+phone }) } window.onkey = function(e) { //Digits keyboard handler try { var key = e.key; if(e.key == "0") { key = 10; } else if (e.key == "*") { key = 11; } else if (e.key == "#") { key = 12; } var phone = listWidget.get(key, 1); if (phone) { digium.phone.dial({ "number": phonePrefix+phone }) } } catch(e) { util.debug("Error in trying to dial"); } util.debug(JSON.stringify(e)); } window.setSoftkey(4, language['EXIT'], function() { digium.app.exitAfterBackground = true; digium.background(); }.bind(this)); window.setSoftkey(3, language['HIDE'], function() { digium.background(); }.bind(this)); } incomingGroupCall.init(); incomingGroupCall.show();
В настройках нового приложения необходимо указать следующие опции:
callgroup: 1
pickupgroup: 1
server: 192.168.1.254:8000 (ну или любой другой IP, где запущен питоновский скрипт)
prefix: *8 (префик для перехвата звонка)
language: ru (поддерживается en / ru)
Если вы используете DPMA, то необходимо загрузить приложение на телефоны с помощью этой инструкции
Запускаете приложение. Можете оставить открытым, либо свернуть в фон.
Изменение диалплана Asterisk
Итак, приложение мы записали на телефон, ретранслятор на сервере тоже запустили. Осталось внести изменения в диалплан астериска, чтобы он сообщал, что на добавочный номер пришел звонок с нужными нам параметрами callgroup и PickupGroup.
Например, вы можете использовать наш рабочий диалплан
exten => _7XX,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
same => n,NoOp(Callgroup = ${CallGroup})
same => n,Set(PickupGroup=${SIPPEER(${EXTEN},pickupgroup)})
same => n,NoOp(PickupGroup = ${PickupGroup})
same => n,System(curl -i -H «Accept: application/json» -H «Content-Type: application/json» -X POST -d ‘{«uid»:"${UNIQUEID}", «callgroup»:"${CallGroup}", «pickupgroup»:"${PickupGroup}", «from»:"${CALLERID(num)}", «to»:"${EXTEN}"}’ 192.168.1.254:8000/put)
same => n,Dial(SIP/${EXTEN},60,TtXxWw)
Приложение в действии
При входящем звонке на экране телефона всплывает наше приложение, которое показывает, звонки в данной колл-группе и позволяет его перехватить. Никакой другой сигнализации нет (например мелодии или световой индикации)
По номерам можно перемещаться с помощью кнопок «вверх» и «вниз», можно перехватить вызов нажатием на кнопку «ОК», тогда перехватится выделенный номер, либо с помощью цифр и *, #.
Запускаешь приложение, сворачиваешь его и ждешь, когда позвонят). При новом звонке приложение развернется. Можно опять свернуть его.
ссылка на оригинал статьи https://habrahabr.ru/post/281146/
Добавить комментарий