Справді корисний додаток для Digium телефонів

image

Вітаю, хабрасообщество.

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

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


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

Сьогодні ми вирішили повернутися до цієї теми, і поділитися іншим, на наш погляд набагато більш цікавим додатком, яке відображає виклик на екрані телефону, якщо користувачі знаходяться в одній пікап групі і дозволяє його перехопити.

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


Зважаючи на те, що телефон сам не може звернутися до астериску по 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 file json
var phonePrefix = config.settings.prefix; //App name from file json
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 list uids
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 request completed 
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 when the open 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.

Наприклад, ви можете використовувати наш робочий диалплан

розширеннями => _7XX,1,NoOp(Call from ${CALLERID(num)} до ${РОЗШИРЕННЯМИ})
same => n,Set(CallGroup=${SIPPEER(${РОЗШИРЕННЯМИ},callgroup)})
same => n,NoOp(Callgroup = ${CallGroup})
same => n,Set(PickupGroup=${SIPPEER(${РОЗШИРЕННЯМИ},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»:"${РОЗШИРЕННЯМИ}"}' 192.168.1.254:8000/put)
same => n,Dial(SIP/${РОЗШИРЕННЯМИ},60,Tt)
same => n,Set(CallGroup=${SIPPEER(${РОЗШИРЕННЯМИ},callgroup)})
same => n,Hangup()

розширеннями => h,1,NoOp(END of App)
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»:"${TARGETNO}"}' 192.168.1.254:8000/del)
same => n,Hangup()

Додаток в дії



При вхідному дзвінку на екрані телефону спливає наш додаток, яке показує, дзвінки в даній колл-групі і дозволяє його перехопити. Ніякої іншої сигналізації немає (наприклад мелодії або світлової індикації)

За номерами можна переміщатися за допомогою кнопок «вгору» і «вниз», можна перехопити виклик натисненням на кнопку «ОК», тоді перехватится виділений номер, або з допомогою цифр і *, #.

Запускаєте додаток, звертаєш його і чекаєш, коли подзвонять). При новому дзвінку додаток розгорнеться. Можна знову згорнути його.



Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.