INIZIO blog

Android aplikace – monitoring VOIP hovorů v Ionic2 a Firebase

V tomto receptu si ukážeme praktické využití real-time databáze Firebase od Google. Vytvoříme velmi rychle sexy android aplikaci pro monitoring vytížení VOIP ústředny.

Ingredience

  • účet na google (pro zřízení databáze Firebase)
  • IONIC2 dev stack
  • projekt na Symfony 3.x
  • data z VOIP ústředny (aktuální hovory, zmeškané hovory, stav jednotlivých VOIP zařízení apod.)

Postup přípravy

  • nejprve si připravíme Firebase databázi pro náš nový projekt (do 100 konkurenčních spojení je zdarma) na firebase.google.com a https://console.firebase.google.com
  • upravíme zabezpečení tak, aby bylo možné se připojit a zapisovat bez autorizace uživatelem (v této aplikaci nehrají uživatelé roli, postačí základní JSON klíč):
  • tím máme prostor pro výměnu dat hotovou
  • připravíme si tedy cron na kmení dat, v SF klasický command + klienta na připojení k Firebase
  • použijeme tedy například „composer require kreait/firebase-php“
  • a vytvoříme si vlastní service:
    <?php
    
    namespace FirebaseBundle\Service;
    
    use Kreait\Firebase;
    
    class Client
    {
        private $firebase;
        private $database;
    
        public function __construct()
        {
            $this->firebase = (new Firebase\Factory())
                ->withCredentials(__DIR__.'/../../../app/config/firebase/APP_KEY.json')
                ->withDatabaseUri('https://APP_PREFIX.firebaseio.com/')
                ->create();
            $this->database = $this->firebase->getDatabase();
        }
    
        public function getReference(string $path)
        {
            return $this->database->getReference($path);
        }
    }
  • nyní nakrmíme novou databázi z dat ústředny, v našem případě například:
    public function saveStats(TPStats $TPStats)
    {
        $data = [
            'actualCalls' => ['count' => $TPStats->getActualCalls()->getCount()],
            'freeOperators' => ['count' => $TPStats->getFreeOperators()->getCount()],
            'missedCalls' => [
                'count' => $TPStats->getMissedCalls()->getCount(),
                'oldestDelta' => $TPStats->getMissedCalls()->getDelta(),
            ],
            'recordedCalls' => [
                'count' => $TPStats->getRecordedCalls()->getCount(),
                'oldestDelta' => $TPStats->getRecordedCalls()->getDelta(),
            ],
            'waitingCalls' => [
                'count' => $TPStats->getWaitingCalls()->getCount(),
                'oldestDelta' => $TPStats->getWaitingCalls()->getDelta(),
            ],
            'busyCalls' => [
                'count' => $TPStats->getBusyCalls()->getCount(),
                'oldestDelta' => $TPStats->getBusyCalls()->getDelta(),
            ],
            'operatorActualLog' => [
                'log' => $TPStats->getOperatorsActualLog(),
            ],
        ];
        $this->client->getReference('/actualData')->set($data);
    }
  • výsledkem je potom JSON struktura ve Firebase:

Použití stringu v proměnných delta je záměrné, usnadňujeme si tak práci pro výpis přímo v IONIC app a díky tomu, že je celá aplikace real-time, nevzniká žádné zpoždění.
Ty žluté vyznačené části není chyba ani warning, ale ukázka reálné aktualizace dat, které se zrovna změnili. Mimochodem geniální věc pro debug, kdy okamžitě vidíme, zda-li nám plnění databáze funguje, jak má.

  • nyní už zbývá jen IONIC2 app pro Android (případně OS X pro fajnšmekry)
  • vytvoříme si tedy novou blank aplikaci
    ionic start af2-lists blank –v2
  • nainstalujeme angularfire:
    npm install angularfire2 --save
  • v app.module.ts si zavedeme config:
    // AF2 Settings
    export const firebaseConfig = {
      apiKey: "AIzaSyDnAX0CQbbsMYuOTJ66ox_F0GwzPM4XPXY",
      authDomain: "angularfire2-list-example.firebaseapp.com",
      storageBucket: "",
      messagingSenderId: "609067141823"
    };
  • no a tím pádem můžeme pracovat s firebase databází, například takto:
    import { Component } from '@angular/core';
    import { NavController } from 'ionic-angular';
    import {AngularFire, FirebaseObjectObservable} from 'angularfire2';
    
    @Component({
      selector: 'page-home',
      templateUrl: 'home.html',
    })
    export class HomePage {
    
      data: FirebaseObjectObservable<any>;
      actualOperatorsList: FirebaseObjectObservable<any>;
    
      constructor(public navCtrl: NavController, af: AngularFire) {
        this.data = af.database.object('/actualData');
        af.database.list('/actualData/operatorActualLog/log')
        //Convert array into Observable and flatten it
            //Transform the value
            .map((x) => x)
            //Subscribe to the stream
            .subscribe(x => this.actualOperatorsList = x);
      }
    }
  • vypíšeme data:
    <ion-header>
      <ion-navbar>
        <ion-title>
          CALL MONITOR
        </ion-title>
      </ion-navbar>
    </ion-header>
    
    <ion-content>
      <ion-grid>
        <ion-row>
          <ion-col col-6>
            <actualOperatorsLog [actualOperatorsLog]="actualOperatorsList"></actualOperatorsLog>
          </ion-col>
          <ion-col col-6>
              <ion-item>
                <button ion-button icon-left>
                  <ion-icon name="call"></ion-icon>
                  Aktuální hovory
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.actualCalls?.count }}</ion-badge>
              </ion-item>
              <ion-item>
                <button ion-button icon-left>
                  <ion-icon name="cafe"></ion-icon>
                  Volní operátoři
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.freeOperators?.count }}</ion-badge>
              </ion-item>
              <ion-item>
                <button ion-button icon-left>
                  <ion-icon name="bonfire"></ion-icon>
                  Zmeškané hovory za 30 min
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.busyCalls?.count }} <span>/ {{ (data | async)?.busyCalls?.oldestDelta }}</span></ion-badge>
              </ion-item>
              <ion-item>
                <button ion-button icon-left>
                    <ion-icon name="bus"></ion-icon>
                  Čekající hovory
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.waitingCalls?.count }} <span>/ {{ (data | async)?.waitingCalls?.oldestDelta }}</span></ion-badge>
              </ion-item>
              <ion-item>
                <button ion-button icon-left>
                    <ion-icon name="contacts"></ion-icon>
                  Změškané hovory ve frontě
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.missedCalls?.count }} <span>/ {{ (data | async)?.missedCalls?.oldestDelta }}</span></ion-badge>
              </ion-item>
              <ion-item>
                <button ion-button icon-left>
                    <ion-icon name="cash"></ion-icon>
                  Záznamník
                </button>
                <ion-badge color="dark" class="value" item-right>{{ (data | async)?.recordedCalls?.count }} <span>/ {{ (data | async)?.recordedCalls?.oldestDelta }}</span></ion-badge>
              </ion-item>
          </ion-col>
        </ion-row>
      </ion-grid>
      <p>
    
      </p>
      <p>
    
      </p>
    </ion-content>
  • ještě jedna komponenta pro výpis aktuálního stavu:
    import {Component, Input} from '@angular/core';
    import 'rxjs/add/operator/map';
    import {OperatorLog} from "../entity/OperatorLog";
    
    @Component({
        selector: 'actualOperatorsLog',
        template: `
            <ion-item *ngFor="let item of actualOperatorsLog" class="operatorsLog">
                        <ion-icon name="person"></ion-icon> {{ item.voipid }} {{ item.action }} <span>{{ item.age }}</span>            
            </ion-item>
        `
    })
    export class ActualOperatorsLogComponent {
    
        @Input() actualOperatorsLog: OperatorLog[];
    
        constructor() {
        }
    }
  • a aplikaci zbuildíme pro android či iphone

Aplikaci podáváme na FULL HD (aby nedošlo ke střižení jako na screenu níže) monitoru připojeném k jakémukoliv androidímu zařízení. No není pěkná?

 

Související příspěvky:

Autor: