Fritzbox über Echo aufrufen

flatline

Neuer User
Mitglied seit
9 Mrz 2006
Beiträge
15
Punkte für Reaktionen
0
Punkte
1
Hi,

ich würde gerne meine Erfahrung und das Todo beim Erstellen des Telefonie-Skills mit euch teilen.

Voraussetzungen:

Fritzbox für Fernzugriffe konfigurieren.
Handset ist über das intergrierte DECT angeschlossen.

https://aws.amazon.com/
--> user beschaffen

https://developer.amazon.com/
--> user beschaffen

https://nodejs.org/
--> downloaden & installieren


mkdir alexa (bsp)
cd alexa

npm install md5
npm install request
npm install request-promise
npm install xml2js

Die angehängte (unten rein-gepostete) Datei 'myFritz.js' auf den Folder alexa kopieren

(Struktur:
myFritz.js
node_modules
)

und in der Methode


getLoginOptions = function(){

return {
username: 'XXX',
password: 'XXX',
server: 'XXX.myfritz.net',
port: '...433',
protocol: 'https'
};
};


die Informationen entsprechend eintragen.

Zum Testen auf der Commandline noch die Testdaten für
var sName = "XXX";
var sKind = mPhoneKind["handy"];
// siehe map mPhoneKind für die verschiedenen Möglichkeiten.

eintragen und mit 'node myFritz.js' aufrufen.

sKind wird berücksichtigt, sofern ein entsprechender Eintrag gefunden wird, ansonsten wird der erste Eintrag genommen.
Aktuell wird immer das 1. Telefonbuch verwendet (siehe Aufruf getPhonebook).


Die grundsätzlichen fritzbox-Zugriffe sind stark an das Projekt fritzbox.js von Sander Laarhoven angelehnt https://github.com/lesander.

Nachdem das soweit funktioniert, sollte der Test-Aufruf auskommentiert werden
und das File zusammen mit dem node_modules Folder ge-zippt werden (z.B. myFritz.zip).


Im aws eine lambda Funktion mit einem Alexa Skill Kit als Trigger eintragen.
Code entry type: 'Upload a Zip file' auswählen.
Beim Upload das zip File (myFritz.zip) auswählen.

Existing Role: service-role/lambda_basic_execution
Handler: ändern auf myFritz.handler
Advanced Settings: Timeout höher setzen.
Speichern.

Im amazon developer portal unter
Alexa Skills Kit auf Get Started gehen und einen Skill erstellen:
Sprache 'German'
Invocation name: fritzbox (! ist nur ein Beispiel)

Das Interaktionsmodel sieht bei mir folgend aus:
{
"intents": [{
"intent": "AnrufenIntend",
"slots": [{
"name": "name",
"type": "CALLEE"
},{
"name": "type",
"type": "PHONE_TYPE"
}]
}]
}

Custom Slot Types erstellen:
mit CALLEE Name1 | Name2 | Name3 | Name4 .. aus (hier eigene Einträge aus dem Telefonbuch eintragen) und
PHONE_TYPE home | zuhause | festnetz | mobile | mobil | handy | work | arbeit | arbeitsplatz (entspricht der Map mPhoneKind)

Die 'Sample Utterances':
AnrufenIntend anrufen {name}
AnrufenIntend anrufen bei {name}
AnrufenIntend telefoniere mit {name}
AnrufenIntend rufe an bei {name}
AnrufenIntend rufe an {name}
AnrufenIntend rufe {name} an
AnrufenIntend rufe {name} {type} an
AnrufenIntend rufe {name} am {type} an
AnrufenIntend rufe {name} auf {type} an
AnrufenIntend rufe {name} auf der {type} an
AnrufenIntend rufe {name} auf dem {type} an


Bei der Configuration -> Service End Point
AWS Lambda ARN anwählen
Europe

und die ARN id aus der erstellten Lambda Funktion eintragen (nur als Beispiel (ungültig!) arn:aws:lambda:eu-west-1:123456789012:function:myFritz)

Das wars :)


Sprach test:
Alexa starte fritzbox --> antwort: Willkommen zur Telefonie (siehe myFritz.js)
Rufe HUGO auf dem Festnetz an --> antwort: hugo wird angerufen. Klingelton zeigt an, wenn die Verbindung hergestellt ist. (siehe myFritz.js)

Wenn die Gegenseite abhebt, klingelt das ensprechende Handset.

Verbesserungsvorschläge sind willkommen.


Da ich das File myFritz.js nicht attachen konnte, poste ich den Inhalt hier:

Code:
var md5 = require('md5');
var request = require('request-promise');
var xml2js = require('xml2js');


var sName = null, sKind = null;
var mPhoneKind = {
    "home"    : "home",
    "zuhause" : "home",
    "festnetz": "home",
    "mobile"  : "mobile",
    "mobil"   : "mobile",
    "handy"   : "mobile",
    "work"    : "work",
    "arbeit"  : "work",
    "arbeitsplatz": "work"
}

exports.handler = (event, context, callback) => {

    try {

        // new session   
        if (event.session.new) {
           console.log("NEW SESSION");
        }

        switch (event.request.type) {
            // launch request
            case "LaunchRequest":
               console.log("LAUNCH REQUEST");
               context.succeed(
                 generateResponse(
                     buildSpeechletResponse("Willkommen zur Telefonie", false),
                     {}
                 )
               );
               break;

            // intent request
            case "IntentRequest":
               console.log("INTENT REQUEST");

               switch(event.request.intent.name) {

                  case "AnrufenIntend":


                     if (event.request.intent.slots.Item && event.request.intent.slots.Item.value) {                     
                        sName = event.request.intent.slots.Item.value;                     
                     }

                     if (event.request.intent.slots.name && event.request.intent.slots.name.value) {                     
                        sName = event.request.intent.slots.name.value;                     
                     }   

                     if (event.request.intent.slots.type && event.request.intent.slots.type.value) {                     
                        if (mPhoneKind[event.request.intent.slots.type.value]) {
                            sKind = mPhoneKind[event.request.intent.slots.type.value];
                        }                     
                     }                        


                    console.log("name=" + sName);                        

                     getPhoneBook(0)           
                        .then((oPhoneBook) => {

                            if (oPhoneBook) {

                                var oNumber = getPhoneEntry(oPhoneBook, sName.toLowerCase(), sKind);
                                if (oNumber) {   

                                console.log("name=" + sName + " Nummer=" + oNumber.number);

                                dialFritz(sName, oNumber.number)
                                    .then((sMessage) => {                           
                                        context.succeed(
                                            generateResponse(
                                                buildSpeechletResponse(sMessage, true),
                                                {}
)
)
                                    })
                                    .catch((error) => {
                                        context.fail("Exception: " + error);                           
                                    })  
                                } else {
                                    context.fail("Keine Nummer....");                                                                                                               
                                }
                            } else {
                                context.fail("Keine Telefonbuch....");
                            }                               

                        })
                        .catch((error) => {
                            context.fail("Exception: " + error);                            
                        })  



                    break;                     
               }              
               break;       

            // session ended request
            case "SessionEndedRequest":
               console.log("SESSION ENDED REQUEST");
               break;

            default:
               context.fail("INVALID REQUEST TYPE: ${event.request.type}");       
        }

    } catch(error) {
        context.fail("Exception: ${error}");   
    }   
};

//Helpers
buildSpeechletResponse = (outputText, shouldEndSession) => {
    return {
        shouldEndSession: shouldEndSession,       
        outputSpeech: {
           type: "PlainText",
           text: outputText
        }
    }
};

generateResponse = (speechletResponse, sessionAttributes) => {
    return {
        version: "1.0",
        sessionAttributes: sessionAttributes,
        response: {
            outputSpeech: speechletResponse.outputSpeech,
            shouldEndSession: speechletResponse.shouldEndSession
        }
    }
};



myRequest = (path, method, options, formData, formUrlEncoded) => {
  return new Promise(function (resolve, reject) {
    options.protocol = options.protocol || 'GET';

    // Make sure we have the required options.
    if (!options.server || options.server === '') {
      // We should probably check for more config settings..
      return reject('Missing login config.');
    }

    // add sid to path.
    if (options.sid && !options.sIdIsContainedInFormData) {
      path += '&sid=' + options.sid;
    }

    // Set the options for the request.
    var requestOptions = {
      uri: options.protocol + '://' + options.server + ':' + options.port + path,
      method: method || 'GET',
      resolveWithFullResponse: true,
      rejectUnauthorized: false     
    };

    console.log('uri:' + requestOptions.uri);

    if (formData) {
      requestOptions.formData = formData;
    }

    if (formUrlEncoded) {
      requestOptions.form = formUrlEncoded;
    }         

    // Execute HTTP(S) request.
    request(requestOptions)
    .then((response) => {
      return resolve(response);
    })
    .catch((error) => {
      console.log('Request failed.');
      return reject(error);
    })
  })
}


getSessionId = (options) => {
  return new Promise(function (resolve, reject) {
    // sid vorhanden ?
    if (options.sid) {
      return resolve(options.sid);
    }

    // Bereite GET zur sid Ermittlung vor
    myRequest('/login_sid.lua', 'GET', options)

    // Errechne sid aus Challange
    .then((response) => {

      var challenge = response.body.match('<Challenge>(.*?)</Challenge>')[1];

      var uc2buffer = new Buffer(challenge + '-' + options.password, "ucs2");
      var challengeResponse = challenge + '-' + md5(uc2buffer);

      var path = '/login_sid.lua?username=' + options.username + '&response=' + challengeResponse;

      return myRequest(path, 'GET', options);
    })

    // Check the response.
    .then((response) => {
      if (response.statusCode !== 200) {
        return reject("Status !== 200");
      }
      return response;
    })

    // lese sid
    .then((response) => {
      var sessionId = response.body.match('<SID>(.*?)</SID>')[1];

      if (sessionId === '0000000000000000') {
        return reject('Could not login to Fritz!Box. Invalid login?');
      }

      return resolve(sessionId);
    })

    .catch((error) => {
      //console.log('getSessionId failed.' + error);
      return reject(error);
    })
  });
}


dialFritz = (sName, phoneNumber) => {

  var options = getLoginOptions();

  return new Promise(function (resolve, reject) {
    getSessionId(options)
    .then((sid) => {
      options.sid = sid;
      var path = '/fon_num/fonbook_list.lua?xhr=1&dial=' + phoneNumber;

      return myRequest(path, 'GET', options);
    })

    .then((response) => {
      if (!JSON.parse(response.body).err) {
        return resolve(sName + ' wird angerufen. Klingelton zeigt an, wenn die Verbindung hergestellt ist.');
      }
      return reject('Fehler bei Wahl.....');
    })

    .catch((error) => {
      console.log('dialNumber failed.');
      return reject(error);
    })
  });
}


xmlToJson = (xml) => {
  return new Promise(function (resolve, reject) {
    xml2js.parseString(xml, (error, result) => {
      if (error) return reject(error);
      return resolve(result);
    })
  })
};


getPhoneBook = (phoneBookId) => {

  var options = getLoginOptions();   

  return new Promise(function (resolve, reject) {
    getSessionId(options)
    .then((sid) => {
      options.sid = sid;
      options.sIdIsContainedInFormData = true;
      var formData = {
        sid: options.sid,
        PhonebookId: phoneBookId,
        PhonebookExportName: 'Phonebook',
        PhonebookExport: ''
      }
      return myRequest('/cgi-bin/firmwarecfg', 'POST', options, formData)
    })

    .then((response) => {
      if (response.statusCode !== 200) {
        return reject(response);
      }
      return response;
    })

    .then((response) => {

      var oJson = xmlToJson(response.body);
      //console.log("response: " + oJson);

      return resolve(oJson);
    })

    .catch((error) => {
      console.log('getPhonebook failed.' + error);
      return reject(error);
    })
  })
};


getPhoneEntry = function(oBook, sName, sKind){

    var i, oPhoneEntry = null, nIdx=0;
    oBook.phonebooks.phonebook[0].contact.some( function(oEntry) {
        var sPhoneName = oEntry.person[0].realName[0].toLowerCase();
        //console.log("sPhoneName="+ sPhoneName);

        if (sPhoneName.indexOf(sName) >= 0) {
            oPhoneEntry = oEntry;

            if (sKind) {
                for (i = 0; i < oPhoneEntry.telephony[0].number.length; i++) {
                    if (oPhoneEntry.telephony[0].number.$.type === sKind) {
                        nIdx = i;
                        break;
                    }
                }
            }
            return true;
        }   

        return false;        
    });


    if (oPhoneEntry) {
        //console.log("oPhoneEntry: <" + JSON.stringify(oPhoneEntry.telephony[0].number[0]._));       
        return {
            number: oPhoneEntry.telephony[0].number[nIdx]._
        };
    }

    return null;
};    

getLoginOptions = function(){

    return {
        username: 'XXX',
        password: 'XXX',
        server: 'XXX.myfritz.net',
        port: '...433',
        protocol: 'https'     
    };
}


getPhoneBook(0)   
        .then((oPhoneBook) => {
            var sName = "XXX";
            var sKind = mPhoneKind["handy"];
            var oNumber = getPhoneEntry(oPhoneBook, sName, sKind);

            if (oNumber) {
                console.log("name=" + sName + " Nummer=" + oNumber.number);


                dialFritz(sName, oNumber.number)
                    .then((sMessage) => {                           
                        console.log(sMessage);
                    })
                    .catch((error) => {
                        console.log("Exception: ${error}");                          
                    })                           

            }
        })
        .catch((error) => {
            console.log(error)                          
        });

Edit DM41: Code-Tags gesetzt
 
Zuletzt bearbeitet von einem Moderator:

Zurzeit aktive Besucher

Statistik des Forums

Themen
244,840
Beiträge
2,219,268
Mitglieder
371,543
Neuestes Mitglied
Brainbanger
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.