Действительно полезное приложение для 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 не будет опубликован. Обязательные поля помечены *