Действительно полезное приложение для Digium телефонов

—

от автора

image

Приветствую, хабрасообщество.

Чуть более года назад мы разрабатывали приложения для Digium телефонов. Несмотря на то, что планы были обширными, мы остановились только на следующих вариациях:

  • Погода с сайта гисметео
  • Курс валют с сайта центробанка
  • RSS лента с новостных порталов

Данные приложения были написаны, чтобы ознакомить сообщество с API и примерами, даже больше just for fun. Cофт, если так можно его назвать, не несет себе никакого уникального применения, которое было бы полезно реальному бизнесу.

Сегодня мы решили вернуться к этой теме, и поделиться другим, на наш взгляд намного более интересным приложением, которое отображает вызов на экране телефона, если пользователи находятся в одной пикап группе и позволяет его перехватить.

За подробностями — > хабракат

Ввиду того, что телефон сам не может обратиться к астериску по HTTP, была выбрана структура клиент-сервер. Сервер на питоне, который «ловит» от астериска CURL запросы при звонке, и javascript на телефоне, который с некоторой периодичностью опрашивает о наличии новых записей.

server.py

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *