Alexa und FRITZ!Box – und es bewegt sich doch…

Basierend auf diesem Blogpost habe ich mir eine Lösung für die AVM-DECT-Steckdosen FRITZ!Dect 200 gebastelt. Das ganze war als reine Fingerübung gedacht, da es aber immer wieder Nachfragen gibt, hier Details zur Umsetzung.

Das ganze läuft auf einem Uberspace als Webserver. Nehmen wir mal an, der Uberspace-Name ist jarvis und läuft auf dem Server stark. Dieser Webspace ist dann über https://jarvis.stark.uberspace.de/ erreichbar. Wichtig: jarvis immer durch den Namen eures Uberspaces ersetzen (vergebt ihr beim Anlegen) und stark immer durch den Namen des Servers, auf dem euer Uberspace liegt (bekommt ihr nach dem Anlegen automatisch zugewiesen und ist auf dem Reiter ‘Datenblatt’ ersichtlich).

Wir verbinden uns per ssh und wechseln in das DocumentRoot, klonen dort das git-Repo und benennen den Ordner in was brauchbares um:

cd /var/www/virtual/jarvis/
git clone https://github.com/gaiterjones/amazon-alexa-php-hello-world-example
mv amazon-alexa-php-hello-world-example/ alexa

Als nächstes müssen wir mittels Symlink dafür sorgen, dass der Webserver ein anderes Verzeichnis als DocumentRoot benutzt:

ln -s alexa/PAJ/www/Amazon/ jarvis.stark.uberspace.de

Abschließend brauchen wir noch die Datei, die mit der AVM-AHA-Interface spricht. Ich hab da schon mal was vorbereitet. Diese kommt in das folgende Verzeichnis:

/var/www/virtual/jarvis/alexa/PAJ/Application/Amazon/Alexa/Intent

Damit das funktioniert muss in Zeile 25 der Host geändert werden unter dem die FRITZ!Box aus dem Internet erreichbar ist. Außerdem ist den Zeilem 239 und 240 Benutzername und Passwort. Es ist zwingend notwendig, dass für die Anmeldung an der FRITZ!Box sowohl Benutzername als auch Passwort benötigt werden. Wenn das noch nicht der Fall ist muss man das entsprechend umstellen.
Auf jeden Fall sollte ein eigener User für die Kommunikation mit Alexa angelegt werden!

Jetzt loggen wir uns auf developer.amazon.com mit unserem Amazon-Developer-Account ein. Wer noch keinen hat registriert sich schnell einen, es muss sich dabei um den gleichen Account handeln der auch für Alexa hinterlegt ist, sonst steht der Skill anschließend nicht zur Verfügung.

Der Skill basiert auf dem Custom Interaction Model, deshalb ist auch ein Codewort erforderlich. Wenn man direkt die Smart Home Skill API verwendet benötigt man das nicht, damit habe ich mich aber nicht beschäftigt, da man dafür zwingend alles als AWS Lambda ARN hosten muss. Damit kann sich gerne AVM auseinandersetzen.

Unser Skill hat den Invocation Name ‘fritzbox’, das Intent Schema sieht so aus:

{
  "intents": [
    {
      "intent": "FritzBox",
      "slots": [
        {
          "name": "trigger",
          "type": "TRIGGER"
        },
        {
          "name": "state",
          "type": "STATE"
        },
        {
          "name": "action",
          "type": "ACTION"
        },
        {
          "name": "device",
          "type": "DEVICE"
        }
      ]   
    }
  ]
}

Die Slots so:

Und die Sample Utterances so:

FritzBox schalte {trigger} {state}
FritzBox liste meine {device} auf
FritzBox ich brauche die Liste der {device}
FritzBox ich brauche die {device} Liste
FritzBox wie ist die {action} von {trigger}
FritzBox nach dem {action} von {trigger}
FritzBox nach den {action} zu {trigger}

Wichtig ist beim SSL-Zertifikat die Option ‘My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority’ zu wählen, sonst läuft es nicht.

Im Testbereich können wir jetzt beliebige Test durchführen, z.B. ‘sag fritzbox schalte Bad ein’. Die Antwort sollte in etwa so aussehen:

{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "Bad wurde eingeschaltet"
    },
    "card": {
      "text": "Bad wurde eingeschaltet",
      "title": "FRITZ!Box",
      "image": {
        "smallImageUrl": "https://jarvis.stark.uberspace.de/alexaCardImage.php?size=small&image=default",
        "largeImageUrl": "https://jarvis.stark.uberspace.de/alexaCardImage.php?size=large&image=default"
      },
      "type": "Standard"
    },
    "shouldEndSession": true
  },
  "sessionAttributes": {}
}

Und damit sollten sich die Steckdosen entsprechend schalten und abfragen lassen. Prinzipiell müsste das auch mit Thermostaten funktionieren, ich hab aber keine und kann das daher nicht testen oder implementieren.

Der +1-Button – die SEO-Revolution

Mit dem internationalen Rollout des +1-Buttons auf Webseiten tauchen auch hier die ersten Anleitungen auf, wie man das ganze beispielsweise in einen xt:Commerce-Shop integrieren kann.

+1 wird die Suche, wie wir sie kennen, revolutionieren. Und mit der Integration in die Google Webmaster Tools geht der Suchgigant gleich noch einen Schritt weiter und bietet eine Auswertung des Buttons in einem seiner Produkte, das jeder Shopbetreiber eh nutzen sollte.

Das ganze lässt sich mit wenigen Handgriffen in den eigenen Shop integrieren. Ich präferiere aber einen Ansatz, der nicht an allen möglichen Stellen im Template {php}-Tags nutzt, daher: In includes/application_bottom.php kurz vor dem schließenden <body>-Tag einfügen:

echo '<script type="text/javascript" src="https://apis.google.com/js/plusone.js">{lang:''.$_SESSION['language_code'].''}</script>';

An der Stelle in der Produktseite, wo der +1-Button erscheinen soll, muss dann nur noch entweder der Button in Googles XML-Syntax oder die HTML5-Variante eingefügt werden (HTML5 empfiehlt sich, wenn man das ganze fehlerfrei durch den Validator bekommen will):

<div class="g-plusone" data-size="medium"></div>

Das ganze kann dann so aussehen:

Produktseite mit +1-Integration

Die Angabe des Zählers kann man sich sparen, wenn man diesen sowieso anzeigen will. Die Angabe der URL kann man sich ebenfalls sparen, wenn man saubere URLs hat oder einen canonical-Tag benutzt. Die Finger lassen sollte man generell von den xt:Commerce eigenen ‘SEO-URLs’ – und damit ist auch die preg_replace()-Geschichte, wie bei Christian vorgeschlagen, vom Tisch. Saubere URLs, z.B. für den canonical-Tag, bekommt man für ein Produkt mittels

xtc_href_link(FILENAME_PRODUCT_INFO, xtc_product_link($product->data["products_id"]))

Wer mit irgendwelchen Umleitungen beispielsweise seiner Produktseiten arbeitet, um auch serverseitig für wirklich kanonische URLs zu sorgen, muss evtl. ein gesondertes Augenmerk auf die Funktion legen, die hier für die Redirects verantwortklich ist. Google überprüft nämlich die URL, indem sie aufgerufen wird. Landet diese Überprüfung in einem Redirect hat man schnell statt des +1-Buttons ein rotes Ausrufezeichen und im Firebug findet sich in der Antwort des entsprechendes Requests:

400 Bad Request: Cannot confirm a connection that was not proposed.

Im Log findet sich zum zugehörigen Request ein Client, der sich als ‘Firefox/1.0.4’ identifiziert. Da dieser in der freien Wildbahn faktisch ausgestorben ist kann man darauf eine mögliche Überprüfung aufbauen.

Google vs. Phark: Das Problem ist text-indent

Es geht um eine Startseite und die Startseite rankt nicht. Nicht mal für den Brandnamen. Gerade mal für die URL. Oder anders ausgedrückt: Im Index ist sie drin. Zahlreiche SEOs haben sich die Seite angesehen, ein bisschen im Nebel gestochert und das ein oder andere Bröckchen zu Tage gefördert. Insgesamt war nichts verwertbares dabei. Das bisherige Highlight kam dann aber gestern:

Das Problem ist text-indent

Sorry, nein. Das Problem ist nicht text-indent!

Aber machen wir mal einen kleinen Ausflug in die Vergangenheit. Damals, als das Netz noch jung war und die Suchmaschinen dumm konnte man sie problemlos übertölpeln (was nicht heißt, dass das heute nicht mehr möglich wäre). Weißer Text auf weißem Grund, sieht die Suchmaschine ja nicht, wenn sie sich nur den Quelltext ansieht. Entsprechend ist das schnell auf der Liste spammiger Techniken gelandet, hat Einzug gefunden in die Google Richtlinien und in den Spambericht.

Zurück in die Gegenwart. Das Netz steht nicht still, immer wieder gilt es, Probleme zu lösen, die sich auch und vor allem um das Thema Schriften drehen. @font-face steckt noch in den Kinderschuhen. Auf frænkisch.de nutze ich es für Überschriften, damit auch dem kompletten Fließtext eine Schriftart abseits der Masse zu geben wie es Gerrit van Aaken auf praegnanz.de macht ist dabei schon die Ausnahme von der Ausnahme. Und nicht immer ist @font-face die Lösung, weil es beispielsweise die Lizenz der gewünschten Schriftart nicht hergibt.

Schon lange üblich ist hingegen, dass man die gewünschte Schrift einfach als Bild platziert. Eine Methode, die Google auch heute noch propagiert. Will man aber Markup und Darstellung konsequent trennen, auch im Zuge von Zugänglichkeit und Barrierearmut, ist es wünschenswert, dass sich im Quelltext eben keine Bilder finden, die aus rein visuellen Gründen dort platziert sind. Vielmehr sollte Struktur und Semantik des Dokuments erhalten bleiben, ohne auf die Vorteile, die Bilder im Hinblick auf ausgefallene Schriften bieten, verzichten zu müssen.
Wegen der Einfachheit in der Implementierung beliebt ist hier vor allem die sog. Phark-Methode. Und diese bedient sich eben text-indent mit einem hohen negativen margin, um den eigentlichen Text aus dem sichtbaren Bereich zu schieben. Empfohlen wird die Methode im übrigen unter anderem von Jens Meiert, seines Zeichens Webmaster. Bei Google.

Jetzt kann man natürlich orakeln, warum Google nach wie vor ein Vorgehen vorschlägt, dass seit mehreren Jahren technisch überholt ist. Vermutlich, weil sie nicht in der Lage sind, bei text-indent zuverlässig zu erkennen, ob es sich wirklich nur um eine optische Geschichte handelt oder der mit Keywords vollgestopfte <h1>-Tag aus dem Blickfeld des Betrachters, aber nicht des Googlebots, eliminiert werden soll.

Denn damit Google erkennen kann, dass Text mittels CSS (und vielleicht Javascript, welches diverse Elemente noch nachträglich modifiziert) ausgeblendet wird, müsste es weit mehr tun als nur Quelltexte indizieren. Es müsste prinzipiell die Seite komplett parsen, um dann erkennen zu können, ob bestimmter Text noch sichtbar ist oder nicht. Und ob der Text, der auf dem Bild steht, nicht vielleicht doch identisch ist mit dem im <h2>-Tag. Selbst bei der Rechenleistung, die Google zur Verfügung steht, eine unrealistische Annahme. Auch liest Google bei einem Besuch weder die CSS- noch die Javascript-Dateien. Und selbst wenn könnte man ihm die über die robots.txt, an die Google sich ja nach eigener Aussage hält, vorenthalten.

Die fragliche Seite hat im CSS 24 mal text-indent mit hohem negativen margin. 24 mal, um mittels Phark-Methode eine Überschrift oder einen Navigationspunkt durch ein Bild zu ersetzen. Sicherlich kann man das etwas eindampfen. Aber sollte das wirklich das Problem sein, dann wirft es neue Fragen auf:
Warum trifft es nur die Startseite, aber keine Unterseiten, obwohl beide das gleiche CSS verwenden?
Warum rankt die englische Startseite prima, die deutsche aber gar nicht, obwohl beide das gleiche CSS verwenden?

Wer sich auch mal an der Beantwortung dieser Fragen versuchen will, ein paar Invite-Codes hab ich noch. E-Mail genügt.