Zur Navigation

Frag doch mal Alexa (Amazon Echo)

1 Kokarde

Hallöchen

Da hat man kaum Freizeit und was macht man damit ... man setzt sich mit Alexa (Amazon Echo) auseinander *stöhn*

Gesehen, gekauft, für ganz lustig befunden ... und nun?
Na klar, das Ding pushen mit eigenen Skills, die auch noch irgendwie sinnhaft sind.

Also,

1. Amazon Dev. Account (https://developer.amazon.com) angelegt, der auf jeden Fall, wenn man denn hat, dem Account entsprechen sollte, den man auch für sein Prime nutzt (später noch wichtig!).

2. Amazon AWS Account (https://aws.amazon.com/de)
angelegt, der auf jeden Fall, wenn man denn hat, dem Account entsprechen sollte, den man auch für sein Prime nutzt (später noch wichtig!).

So, nun steckt man in der Amazon Welt, die einem mit vielen tollen Infos und und Tools etc. unterstützt (https://developer.amazon.com/alexa-skills-kit#get-started-now).

Auf Youtube wird man von Videos erschlagen und auch sonst gibt es überall tolle Hilfen. Aber ... als Laie wird man scheitern, das wurde mir schon sehr schnell klar. Auch wenn Amazon einem das anders verkaufen will.

Blöd wie ich war habe ich meine Funktion in PHP als "Webapp" geschrieben, weil Amazon sagt das geht. Nun liegt das PHP Script also auf meinem Webserver und ich habe es auch geschafft einen Skill damit zu verbinden.

Ja, es funzt - grundsätzlich - am Ende kommt, nach Sprachaufforderung an Alexa, eine spezielle Email bei mir im Postfach an.

Doch leider ... Alexa antwortet mir nicht korrekt und die Email kommt auch gleich 2 x an.

Nach ein wenig Lektüre habe ich dann verstanden dass man Alexa erst beibringen muss richtig zu reden. Dies passiert halt mit s. g. Events.
Doch dafür gibt es keine PHP Beispiele.

*kotz*

Grund hierfür ist die Struktur und das Design von Alexa.
Eigentlich will das Dreckstück lieber Javascript (node.js) und JAVA haben.

Hab ich mich also hin gesetzt und einen Basic-Skill geladen und als s. g. AWS Lambda Funktion mit meinem Skill verbunden => AWS (Codebereich) per ID verbunden mit SKILL (den man ja im Dev. Portal anlegt).

Schon das ist blöde, wie ich finde.
Naja, egal, das eine ist eine Service-Landschaft (Infrastruktur) und das andere halt der Bereich wo ich meine Skills und Apps developed.

Nun steht also meine tolle Funktion in AWS (Javascript) und siehe da, Alexa redet nun schön mit mir und gibt auch ein sauberes klares Feedback.

Scheisse nur dass ich nun mein PHP Script da nicht mit verbunden bekomme.

Ein Blick in stackoverflow sagt mir, dass dies scheinbar auch nicht so easy ist.
Schon alleine deswegen, weil wir hier 2 getrennte Plattformen haben.
Nämlich einmal meinen Webserver mit dem Script und die AWS Welt von Amazon.

JÖRG oder andere nette LEUTE hier, habt Ihr Bock mir zu helfen????

21.02.2017 11:30

2 Jörg Kruse

Mangels Erfahrung mit den Alexa-Skills müsste ich mich da auch erst einlesen :)

Blöd wie ich war habe ich meine Funktion in PHP als "Webapp" geschrieben, weil Amazon sagt das geht.

Scheisse nur dass ich nun mein PHP Script da nicht mit verbunden bekomme.

Ein Blick in stackoverflow sagt mir, dass dies scheinbar auch nicht so easy ist.

Hast du da noch die passenden Links dazu?

21.02.2017 13:26

3 Kokarde

Hi

http://stackoverflow.com/questions/4238353/how-to-include-js-file-in-php

http://stackoverflow.com/questions/29443759/javascript-innerhtml-include-php

http://stackoverflow.com/questions/16933989/how-do-i-load-a-php-include-into-a-page-with-ajax-whilst-the-page-is-loading

http://www.frihost.com/forums/vt-109752.html

Um nur Einige zu nennen.

Aber, bei mir ist das so:

PHP FILE => test.php => ]<?php mein code hier ?> => wird aufgerufen von der Alexa Funktion wie ein ganz normaler Web-Link => www.meinedomain.de/test.php => PHP Script wird ausgeführt und Email landet bei mir. OK

Jetzt redet Alexa nur mit mir, wenn man speziellen Code für die Sprache einbindet. Ansonsten meldet Alexa immer nur das ein Fehler aufgetreten in der Kommunikation mit dem Script. Dennoch wird es ausgeführt. Warum dann immer noch 2 Emails bei mir ankommen ist erst mal unwichtig.

Das geht so:

JAVASCRIPT FILE => alexa.js => viel Quellcode => führt zu Antwort / Kommunikation von Alexa

Ausschnitt nur zur Verdeutlichung der Codestruktur:

/****************
 * This simple skill is built on the fact-skill by Amazon Alexa Skills
 * nodejs skill development kit.
 * This skill supports multiple lauguages. (en-US, en-GB, de-DE).
 * original are located at https://github.com/alexa/skill-sample-nodejs-fact
 * edit by Kai 
 ****************/

'use strict';

const Alexa = require('alexa-sdk');

const APP_ID =  'amzn1.ask.skill.49e1ba36-445f-493d-b7fa-ae813ef66fdb';  // TODO replace with your app ID (OPTIONAL) or insert undefined.

const languageStrings = {
    'en-GB': {
        translation: {
            FACTS: [
                'Email was send!',
            ],
            SKILL_NAME: 'EMAIL',
            GET_FACT_MESSAGE: "OK: ",
            HELP_MESSAGE: 'You can say tell me a space fact, or, you can say exit... What can I help you with?',
            HELP_REPROMPT: 'What can I help you with?',
            STOP_MESSAGE: 'Goodbye!',
        },
    },

Ich muss also diese .JS Datei so in mein PHP einbinden, dass es vor oder nach dem Durchlauf noch aufgerufen wird um eine Sprachausgabe von Alexa zu erzeugen.

In meinem Fall würde die lauten "OK: Email was send!"

Was wäre eine erste Idee bzw. welcher der o. g. Links zeigt eigentlich wie funzen müsste?

Ich hatte auch schon den Ansatz, dass ich vor dem Start des PHP Scripts ganz normalen HTML Code stelle und dort wie man es kennt das .JS einfüge. Doch leider hat das nicht funktioniert.

So mache ich es ja auch in meinem kleinen Programm für die Munitionsverwaltung.

22.02.2017 11:44 | geändert: 22.02.2017 11:45

4 Jörg Kruse

Das Beispiel-Script ist nodejs, und das wird nicht von einem Browser ausgeführt, sondern auf einem Server durch einen node-Interpreter. Von daher bringt der Einbau in eine HTML-Datei nichts. Wenn auf einem Server nodejs installiert ist, sowie die notwendigen Module (welche durch require() eingebunden werden) und PHP Shell-Commands absetzen darf, könnte man das Script vielleicht über exec() oder system() ausführen. Das sind aber Voraussetzungen, die ein normaler Webspace wohl kaum erfüllen wird.

So ganz steige ich durch das ganze allerdings auch noch nicht durch, deswegen noch ein paar Fragen:

Nun liegt das PHP Script also auf meinem Webserver und ich habe es auch geschafft einen Skill damit zu verbinden.

Kannst du das mal konkret ausführen, wie diese Verbindung hergestellt wird?

Was macht das PHP-Script ansonsten - verschickt es die genannte Email?

Vielleicht kann man auch den umgekehrten Weg gehen und das PHP-Script über einen Ajax-Aufruf in dem nodejs Script ausführen?

22.02.2017 20:05 | geändert: 22.02.2017 20:06

5 Kokarde

Hallöchen

Wenn auf einem Server nodejs installiert ist, sowie die notwendigen Module (welche durch require() eingebunden werden ...

Genau das ist der Scheiss ...
Das geht nämlich in der Amazon-Welt nicht.
Also in der Console bei Amazon, wo man seinen Skill-Code eingibt.
developer.amazon.com

Sonst hätte ich mir eines der vielen Email-JS, wie z. B. nodemailer, installiert und eingebunden per "npm install nodemailer" und dann per Aufruf "mail = require('sendmail')();"

Dumm ist halt der Zustand dass die Sprachfunktionen gut beschrieben sind und bereits als Vorlagen in node.js bereit gestellt werden.
Auch in Java und in Python gibt es fertige Vorlagen.
Aber überall das gleiche Grundproblem = Ich kann keine Erweiterungen hinzu verbinden.

Der Aufbau bei Amazon ist wie folgt.

1) Du brauchst als erste Ebene developer.amazon.com - dort baust Du den Skill, so wie ein Grundmodel. Man könnte auch sagen man erstellt ein App-Grundgerüst, mit diversen Optionen.

2) Du brauchst dann, entsprechend der gewählten Möglichkeiten aus 2 Möglichkeiten in der developer.amazon.com Welt, ein Ziel. Dies kann bei Möglichkeit 1 ein Weblink sein (eigener Webserver SSL) der auf eine Datei zeigen muss (html, php, js), oder Möglichkeit 2 einen s. g. AWS Link, der dann auf die https://aws.amazon.com/de/ Console = ein Script was auf der Infrastruktur von Amazon läuft, zeigt.
Da die AWS Welt von Amazon viele Services kennt, nennt sich dieser Teil AWS Lambda (https://aws.amazon.com/de/lambda/?nc2=h_m1).
Dort kann man diverse Vorlagen (Blueprints) auswählen in den Sprachen Node.js / Java / Python und ich glaub C#
Die Meisten sind aber Node.js
Diese Dateien (Templates/Vorlagen) enthalten dann so eine Art SDK Kit und damit lassen sich schnell z. B. Antwortverhalten von Alexa definieren.

Es gibt in der Amazon Welt noch eine 3 Möglichkeit.
Diese ist aber auch Teil der AWS https://aws.amazon.com/de/ Welt.
Dort kann man u. a. auch seinen eigenen kleinen virtuellen Server bauen und Dateipakete hoch laden. Das nennt sich dann EC2

Es gibt noch weitere coole AWS Services, die hoffentlich eine Rettung des Problems bedeuten, da man diese wohl direkt ohne Umwege einbinden kann.

- https://aws.amazon.com/de/sns/?nc2=h_m1

- https://aws.amazon.com/de/ses/?nc2=h_m1

Im Moment bin ich angefressen weil halt eigentlich beide Seiten, mein PHP Email Script, was nur eine Email verschickt, per Möglichkeit 1 aufgerufen wird und das dann auch alles korrekt macht.

Auf der anderen Seite funktioniert Möglichkeit 2 und erzeugt die Sprachausgabe.

Ich bekomme halt beiden Welten nicht verbunden und es gibt auch leider scheinbar nicht wirklich Lösungsansätze dafür.
Dies m. E. nach auch der Grund ist warum es nur so banale Anwendungen auf der deutschen Plattform von Amazon gibt.

Soll ich mal beide Code-Blöcke posten?

23.02.2017 12:18

6 Jörg Kruse

2) Du brauchst dann, entsprechend der gewählten Möglichkeiten aus 2 Möglichkeiten in der developer.amazon.com Welt, ein Ziel. Dies kann bei Möglichkeit 1 ein Weblink sein (eigener Webserver SSL) der auf eine Datei zeigen muss (html, php, js), oder Möglichkeit 2 einen s. g. AWS Link, der dann auf die https://aws.amazon.com/de/ Console = ein Script was auf der Infrastruktur von Amazon läuft, zeigt.

OK, dann ist klar, wie der Ablauf ist. Und was du möchtest, ist gewissermaßen ein Verkettung der der beiden Scripte?

Ich bekomme halt beiden Welten nicht verbunden

Eine Möglickeit habe ich ja bereits angesprochen:

Vielleicht kann man auch den umgekehrten Weg gehen und das PHP-Script über einen Ajax-Aufruf in dem nodejs Script ausführen?

Ich weiß allerdigns nicht, ob man dies in nodejs hinbekommt, wenn man keine Module einbinden kann

einen s. g. AWS Link, der dann auf die https://aws.amazon.com/de/ Console = ein Script was auf der Infrastruktur von Amazon läuft, zeigt.

Ist dieser AWS Link von außen erreichbar? dann könnte man ihn ja auch durch das PHP-Script aufrufen (z.B. mit curl oder file_get_contents())

Also entweder das PHP-Script über Ajax aus dem Lambda-Script heraus aufrufen oder das Lambda-Script über einen HTTP-Request aus dem PHP-Script heraus.

23.02.2017 21:07

... 1 Monat später ...

7 Kokarde

Hallöchen

Business brennt, daher erst jetzt wieder hier online - sorry.

Danke für dein cooles Feedback!

Also, ein Kumpel sagt ich solle "http.get(options[, callback])" nehmen.
Doch ich habe es leider nicht hin bekommen.

Das ist dann wohl auch so wie deine Idee mit dem Ajax.
Damit können man das PHP Script anstarten oder so ...

Ja, ich möchte eine Verkettung der beiden Scripte bzw. Scriptwelten.

Leider hat die Idee mit den beiden Services nicht funktioniert, weil nicht dokumentiert ist, wie man diese SMS bzw. Emailfunktion in den Node.js Code einbaut. Auch hier werden immer nur "Aufruf-Links (IDs)" erzeugt und zur Verfügung gestellt.

Der AWS Lambda ARN Link ist von aussen nicht erreichbar

Nur die Plattform von Amazon, also dort wo der Skill läuft, kann den Link (ID) aufrufen und dort auch nur eine Art von interner Konfig-Box in der man seinen Ablauf schreibt (siehe Bilder).

Link-Layout (App Welt von Amazon, also der Skill an sich): amzn1.ask.skill.40b9582b-3db3-463f-a237-0e1b52ac000d

RUFT AUF / IST VERKNÜPFT MIT

1) Amazon Quellcode-Welt (AWS LAMBDA), dort wo das Script läuft:
Hier klappt dann leider nur die Voice-Ausgabe, aber ich weiss halt nicht wie ich in dem Node.js Quellcode nun das Email-Script extern aufrufe.

ODER

2) einen HTTP-Link wo ein eigenes Script läuft.
Dies kann dann z. B. PHP sein - siehe Bilder.

Dieses PHP Script sollte einen Secure-Pfad-Aufruf haben:

$post = file_get_contents( 'php://input' );

$post = json_decode( $post );

if ( 'amzn1.ask.skill.40b9582b-3db3-463f-a237-0e1b52ac000d' == $post->session->application->applicationId ) { ..... 

so dass nur Amazon die Datei aufrufen kann.
Ruft Jemand den Link so auf, dann passiert nix.
Aber das muss ich Dir ja nicht erzählen :)

Mach ich das so, wie in Nummer 2) dann erzeugt Alexa leider kein mündliches Feedback - schickt aber brav die Email an mich (zwar immer noch doppelt, aber das ist bestimmt ein Quellcode-Error von mir!

Mache ich das Ganze mit der 1) Option, dann .. wie bereits gesagt ... kommt ein mündliches Feedback, aber es kann ja keine Email kommen weil kein Aufruf dazu erfolgt (PHP Script liegt auf externem Server!).

Ich denke deine Idee mit dem AJAX bzw. mit diesem HTTP_GET ist schon der richtige Ansatz.

Peace - Kai

30.03.2017 13:56

8 Jörg Kruse

Also, ein Kumpel sagt ich solle "http.get(options[, callback])" nehmen.
Doch ich habe es leider nicht hin bekommen.

Wie schaut der Code denn aus?

Wichtig ist wohl, dass die Options korrekt gesetzt sind. Die Callback-Funktion ist ja erstmal sekundär, und könnte später genutzt werden, um die Antwort deines PHP-Scriptes zu verarbeiten.

Und das betreffende Modul muss wohl auch hier eingebunden sein:

http = require('http');

30.03.2017 15:51 | geändert: 30.03.2017 15:54

9 Kokarde

Hi, erst mal schnell Antworten :)

Code kommt gleich!

Die Callback-Funktion ist wirklich nicht wichtig, so weit ich das verstehe, weil das Feedback vom PHP-Script ist mir nicht wichtig und brauche ich auch nicht.

Genau, mit dem Einbinden der Module ist halt so ne Sache.
Mal geht es bei Amazon - mal nicht.
Wieso oder warum genau erschliesst sich mir nicht.
Siehe dazu auch gleich den folgenden Beitrag.

In diesem Fall, ob richtig oder nicht, weiss ich nicht, klappt es mit:

const http = require('http');

Doch was nun?

ThanX

07.04.2017 11:19 | geändert: 07.04.2017 11:23

10 Kokarde

http = require('http');



Hallöchen

Also, hier mal der Code, so wie er funktioniert - es wird ein wenig Umfangreich jetzt, da es 2 Code-Blöcke sind (s. g. Blueprints) - die eine für Sprachausgabe und die Andere für eine Email zu erzeugen.

Hier soll nun entweder

1) eine Email-Funktion aus Blueprint 2 eingebunden (in Blueprint 1)
2) ein Aufruf eines externen Mail-Script eingebunden (in Blueprint 1)
3) oder auch Blueprint 1 mit 2 verbunden

werden.

Dies sind die 3 möglichen Ansätze.

BLUEPRINT 1 (Voice Feedback)

'use strict';

const Alexa = require('alexa-sdk');

const APP_ID = undefined;  // TODO replace with your app ID (OPTIONAL).

const languageStrings = {
    'de-DE': {
        translation: {
            FACTS: [
                'Deine Email wurde versendet',
            ],
            SKILL_NAME: 'Email OK',
            GET_FACT_MESSAGE: 'Email gesendet!',
            HELP_MESSAGE: 'Du kannst sagen, „Öffne Email“, oder du kannst „Beenden“ sagen... Wie kann ich dir helfen?',
            HELP_REPROMPT: 'Wie kann ich dir helfen?',
            STOP_MESSAGE: 'Auf Wiedersehen!',
        },
    },
};

const handlers = {
    'LaunchRequest': function () {
        this.emit('GetFact');
    },
    'GetNewFactIntent': function () {
        this.emit('GetFact');
    },
    'GetFact': function () {
        // Get a random space fact from the space facts list
        // Use this.t() to get corresponding language data
        const factArr = this.t('FACTS');
        const factIndex = Math.floor(Math.random() * factArr.length);
        const randomFact = factArr[factIndex];

        // Create speech output
        const speechOutput = this.t('GET_FACT_MESSAGE') + randomFact;
        this.emit(':tellWithCard', speechOutput, this.t('SKILL_NAME'), randomFact);
    },
    'AMAZON.HelpIntent': function () {
        const speechOutput = this.t('HELP_MESSAGE');
        const reprompt = this.t('HELP_MESSAGE');
        this.emit(':ask', speechOutput, reprompt);
    },
    'AMAZON.CancelIntent': function () {
        this.emit(':tell', this.t('STOP_MESSAGE'));
    },
    'AMAZON.StopIntent': function () {
        this.emit(':tell', this.t('STOP_MESSAGE'));
    },
    'SessionEndedRequest': function () {
        this.emit(':tell', this.t('STOP_MESSAGE'));
    },
};

exports.handler = (event, context) => {
    const alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

BLUEPRINT 2 (Email und SMS sending)

'use strict';

const AWS = require('aws-sdk');
const EMAIL = process.env.email;
const SNS = new AWS.SNS({ apiVersion: '2010-03-31' });
const APP_ID = undefined;  // TODO replace with your app ID (OPTIONAL).


function findExistingSubscription(topicArn, nextToken, cb) {
    const params = {
        TopicArn: topicArn,
        NextToken: nextToken || null,
    };
    SNS.listSubscriptionsByTopic(params, (err, data) => {
        if (err) {
            console.log('Error listing subscriptions.', err);
            return cb(err);
        }
        const subscription = data.Subscriptions.filter((sub) => sub.Protocol === 'email' && sub.Endpoint === EMAIL)[0];
        if (!subscription) {
            if (!data.NextToken) {
                cb(null, null); // indicate that no subscription was found
            } else {
                findExistingSubscription(topicArn, data.NextToken, cb); // iterate over next token
            }
        } else {
            cb(null, subscription); // a subscription was found
        }
    });
}

/**
 * Subscribe the specified EMAIL to a topic.
 */
function createSubscription(topicArn, cb) {
    // check to see if a subscription already exists
    findExistingSubscription(topicArn, null, (err, res) => {
        if (err) {
            console.log('Error finding existing subscription.', err);
            return cb(err);
        }
        if (!res) {
            // no subscription, create one
            const params = {
                Protocol: 'email',
                TopicArn: topicArn,
                Endpoint: EMAIL,
            };
            SNS.subscribe(params, (subscribeErr) => {
                if (subscribeErr) {
                    console.log('Error setting up email subscription.', subscribeErr);
                    return cb(subscribeErr);
                }
                // subscription complete
                console.log(`Subscribed ${EMAIL} to ${topicArn}.`);
                cb(null, topicArn);
            });
        } else {
            // subscription already exists, continue
            cb(null, topicArn);
        }
    });
}

/**
 * Create a topic.
 */
function createTopic(topicName, cb) {
    SNS.createTopic({ Name: topicName }, (err, data) => {
        if (err) {
            console.log('Creating topic failed.', err);
            return cb(err);
        }
        const topicArn = data.TopicArn;
        console.log(`Created topic: ${topicArn}`);
        console.log('Creating subscriptions.');
        createSubscription(topicArn, (subscribeErr) => {
            if (subscribeErr) {
                return cb(subscribeErr);
            }
            // everything is good
            console.log('Topic setup complete.');
            cb(null, topicArn);
        });
    });
}


exports.handler = (event, context, callback) => {
    console.log('Received event:', event.clickType);

    // create/get topic
    createTopic('aws-iot-button-sns-topic', (err, topicArn) => {
        if (err) {
            return callback(err);
        }
        console.log(`Publishing to topic ${topicArn}`);
        // publish message
        const params = {
            Message: ` THATS ME - Kai from Berlin`,
            Subject: ` Hello from your Alexa Skill `,
            TopicArn: topicArn,
        };
        // result will go to function callback
        SNS.publish(params, callback);
    });
};



Der Blueprint 2 ist eigentlich für die s. g. IoT Buttons vorgesehen und kann eigentlich nur durch diesen = TRIGGER ausgelöst werden.

Aber, auf der Plattform von Amazon funktioniert das auch so wenn man in der AWS Lambda Console dort auf TEST klickt. Es kommt dann eine Email bei mir an mit dem voreingestellten Text. So wie ich das ja auch will.

Doch es erfolgt keine korrekt Sprachausgabe, da diese dort nicht definiert ist!
Startet man also den Skill mit dem Sprachaufruf, kommt von Alexa als Antwort nur eine Fehlermeldung, obwohl die Email korrekt verschickt wird!

Gleiches Verhalten wie bereits ganz oben von mir beschrieben wenn ich mein Email-Script aufrufen lasse. Weil auch dort keine Sprachausgabe vorgesehen ist.

Bin bei Blueprint 2 auch an der Einbindung der Sprachausgabe gescheitert, wobei ja eigentlich möglich sein sollte, da ja NODE.JS - weil Amazon scheinbar die Verbindung von den nötigen "Required" Einträgen nicht erlaubt.

Ich dachte, hey .. ein Lösungsansatz und ich kombiniere einfach die beiden Blueprints (Funktionen). Aber nein, fucking Amazon will das scheinbar nicht.

Der Fehler lautet dann immer: "errorMessage": "Cannot find module 'alexa-sdk'"

Was ich einfach nicht kapiere .. denn der Aufruf ist der Gleiche wie im anderen Blueprint und dort funktioniert es.

Wenn ich das richtig verstehe, müsste ich nur irgendwie in den "exports.handler" die Funktionen für die Sprachausgabe oder umgekehrt für den Emailversand hinein bekommen. Doch wenn es schon am require('alexa-sdk'); scheitet, na dann Arschkarte ...

Oder, wie ganz am Anfang eigentlich gewünscht, in das Blueprint 1, das so schön antwortet, den Aufruf meines Email-Skriptes einbinden mit get_http oder so ähnlich.

Ideen?

Vielleicht mit dem const http = require('http'); weiter machen?

07.04.2017 11:25