SDK Script voice-dispatcher-audio.are

voice-dispatcher-audio.are
/* DESC: This script implements an audio voice dispatcher
 * The first audio Interface (line-in/out) is used for the GSM-Call audio.
 * - Dialing os controlled via digital in 1 (in1)
 *   If it is set high (1), the predefined nummer is called. 
 *   Must be set to high (1) to acknowledge incomming calls.
 *   Any call will be terminated when it goes low (0).
 * - Incomming calls are signaled at digital out 1 (out1). 
 *   It's set to high (1) when call is incomming and statys high during call.
 *   It will go to low (0) when the call is ended.
 *
 * Copyright (C) 2012 NetModule AG, Switzerland
 * 
 */
 
INTERVAL = 1;           /* polling status change every x seconds */
NUMBER = "+123456789";  /* number to call when DI1 = on */
 
template dispatcher {
    in1;
    audio;
 
    int dispatcher ()
    {
        this.in1 = -1;
 
        endpoints = nb_voice_endpoint_list();
        if (is_array(endpoints)) {
            for (i = 0; i < length(endpoints); i++) {
                ep = endpoints[i];
                id = struct_get(ep, "id");
                desc = struct_get(ep, "desc");
                state = struct_get(ep, "state");
 
                nb_syslog("%d: endpoint %d is in state %s (%s)", i, id, state, desc);
 
                /* record first audio endpoint */
                if (is_void(aud) && left(desc, 6) == "aud://") {
                    this.audio = ep;
                }
            }
        }
        if (is_void(this.audio)) {
            nb_syslog("no audio endpoint found");
            return -1;
        } else {
            nb_syslog("using endpoint %s\n", struct_get(this.audio, "desc"));
            return 0;
        }
    }
 
    int checkdio () 
    {
        in1 = nb_dio_get("in1");
 
        if (in1 == this.in1) {
            /* no state change */
            return -1; 
        } else {
            /* update state */
            nb_syslog("updating dio state");
            this.in1 = in1;
            return in1;
        }
    }
 
    int handle_dio_event (int ev)
    {
        /* update endpoint first */
        au = nb_voice_endpoint_get(d.audio);
        if (!is_struct(au)) {
            nb_syslog("updating audio endpoint failed");
            return -1;
        }
        audesc = struct_get(au, "desc");
        austate = struct_get(au, "state");
 
        /* get list of currently active calls */
        calls = nb_voice_call_list();
 
        if (ev == 1) {
            nb_syslog("DI1 has been closed");
 
            if (austate == "available") {
                /* initiate new call */
                call = mkstruct("calling", audesc,
                                "called", NUMBER);
 
                nb_syslog("initiating call to %s from %s", NUMBER, audesc);
 
                if (nb_voice_call_dial(call) == 0) {
                    nb_syslog("unable to initate call to %s", NUMBER);
                }
              	nb_dio_set("out2", 1); /* call is active */
            } else {
                nb_syslog("not calling %s as endpoint %s is not available", NUMBER, audesc);
                return -1;
            }
        } else {
            nb_syslog("DI1 has been opened");
 
            /* terminate any active calls */
            for (i = 0; i < length(calls); i++) {
                call = calls[i];
 
                if (strstr(struct_get(call, "called"), desc)) {
                    nb_syslog("hanging up call %d", struct_get(call, "id"));
                    nb_voice_call_hangup(call);
                }
            }
            nb_dio_set("out2", 0); /* no active call */
        }
 
        return 0;
    }
 
    void handle_voice_event (struct event)
    {
        type = struct_get(event, "type");
        call = struct_get(event, "call");
        id = struct_get(call, "id");
 
        nb_syslog("received voice event '%s' on call %d\n", type, id);
 
        /* update endpoint */
        au = nb_voice_endpoint_get(d.audio);
        if (!is_struct(au)) {
            nb_syslog("updating audio endpoint failed");
            return;
        }
        austate = struct_get(au, "state");
        audesc = struct_get(au, "desc");
 
        switch (type) {
            case "outgoing":
                /* don't touch any locally initiated calls */
                break;
            case "incoming":
                nb_syslog("call %s: %s is calling %s",
                          id, struct_get(call, "calling"), struct_get(call, "called"));
 
                /* route any incoming calls to audio device */
                if (nb_voice_call_route(call, audesc) == 0) {
                    nb_syslog("routing call %d to %s", id, audesc);
                    nb_dio_set("out2", 1); /* ring, incomming call */
                } else {
                    nb_syslog("unable to route call %d to %s", id, audesc);
                    nb_voice_call_hangup(call);
                    /* no signaling */
                }
                break;
            case "dispatched":
                /* accept calls if DI1 closes */
 
                nb_syslog("call %s: %s to %s got dispatched",
                          id, struct_get(call, "calling"), struct_get(call, "called"));
 
                if (struct_get(call, "state") == "alerting") { 
                    if (austate == "available") {
                        nb_syslog("hanging up call %d as audio is busy", id);
                    } else {
                        /* wait until IN1 has been closed */
                        nb_syslog("waiting 10s until IN1 is closed");
                        for (i = 0; i < 10; i++) {
                            in1 = this.checkdio();
                            if (in1 == 1) {
                                nb_syslog("IN1 closed");
 
                                if (nb_voice_call_accept(call) == 0) {
                                    nb_syslog("accepted call %d", id);
                                    /* nb_voice_call_volume(call, 7); increase volume level to max */
                                    return; /* call established */
                                } else {
                                    nb_syslog("unable to accept call %d", id);
                                    break;
                                }
                            }
                            sleep(1);
                        }
                    }
                    /* otherwise hangup */
                    nb_syslog("hanging up call %d", id);
                    nb_voice_call_hangup(call);
                    nb_dio_set("out2", 0); /* error, no active call */
                }
                break;
            default:
                break;
        }
    }
 
    void run ()
    {
        /* wait for calls */
        while (1) {
            event = nb_voice_event(INTERVAL);
            if (is_struct(event)) {
                dump(event);
                this.handle_voice_event(event);
            } 
            event = this.checkdio();
            if (event != -1) {
                this.handle_dio_event(event);
            }
        }
    }
}
 
d = new dispatcher();
nb_dio_set("out1", 0); /* clear signaling */
dump(d);
d.run();
 
exit(0);