Mittwoch, 18. Januar 2017

Ein Remote Taster in FHEM mit Rückmeldung

HowTo

Einfacher Taster

Zielstellung

Ein Dummy mit klickbarem Symbol für die Weboberfläche als "Taster" für verschiedenen Aktionen.

Als erste Komponente stattet man einen dummy mit dem Attribute devStateIcon aus:
define testdummy dummy
attr testdummy devStateIcon _start:on:stop _stop:off:start
attr testdummy event-on-change-reading state
set testdummy _stop

Als Zweites braucht man ein notify welches nur auf die Events start und stop vom testdummy reagiert:
define nty_test notify testdummy:(start|stop) set $NAME _$EVENT

Diese Konstruktion wirkt wie ein toggle Schalter, das Symbol beim dummy ist klickbar und bei jedem Klick schaltet es sichtbar on und off.

Die Rückmeldung des Schalters ist damit vorhanden nur er tut nicht wirklich etwas. Zur Demonstration lassen wir "noch etwas tun":
defmod nty_test notify testdummy:(start|stop) sleep 2;;set $NAME _$EVENT

Jetzt erfolgt die Reaktion auf den Klick mit kurzer Pause, stattdessen kann man natürlich auch etwas sinnvolles tun.

Remote Taster mit Rückmeldung

Zielstellung

Ein "Taster" in der Weboberfläche, der auf einem Remote System z.B. einen Dienst startet oder beendet. Mein Taster soll erst reagieren, wenn der Befehl vom Remote System ausgeführt wurde.

Die System sind mit ssh "gekoppelt", wie das geht steht in meinem vorherigen Beitrag.

Achtung! Seit  März 2017 funktioniert die hier beschriebene Steuerung von FHEM über die Webschnittstelle nicht mehr so einfach. Bitte meinen Beitrag im März beachten.

Demo Script

Auf dem Remote System wird ein Script im Home Verzeichnis des verwendeten Remote Benutzers erzeugt/abgelegt und mit chmod +x test.sh ausführbar gemacht:
#!/bin/sh
# Parameter Übergabe
host=$1
name=$2
event=$3
# Kontrollausgabe zum Test aktivieren
# echo $host $name $event
# Zur Demo ein Sleep
sleep 3
# Abfrage und Verzweigung
case "$event" in
    start)
 # hier kann etwas spezifisches bei Start passieren
    ;;

    stop)
 # hier kann etwas spezifisches bei Stop passieren
    ;;
    *)
       exit 1
    ;;

esac
# Web Kommando an FHEM absetzen
h=$host':8083'; c='set '$name' '$status; curl --data "fwcsrf=$(curl -s -D - http://$h/fhem?XHR=1 | awk '/X-FHEM-csrfToken/{print $2}')" http://$h/fhem?cmd=$(echo $c|sed 's/ /%20/g')

exit 0

Zum Test kann man dieses Script mit ./test.sh <FHEM hostname> testdummy start aufrufen. Dabei muss sich der Status unseres Dummy ändern.

Funktioniert dieser Test, wird das notify geändert, die hostnamen müssen angepasst werden:
defmod nty_test notify testdummy:(start|stop) "ssh pi@<remote hostname> ./test.sh <FHEM hostname>  $NAME $EVENT"

An der Reaktion des Dummy hat sich nicht viel geändert, da ich im Script sleep 3 gesetzt habe dauert es jetzt 1 Sekunde länger. Dafür passiert aber etwas auf einem anderen System!

Praktische Verwendung

Das Demo Script kann so als Grundgerüst verwendet werden. Im einfachsten Fall fügt man an den entsprechende Stellen einfach nur Befehle ein. Komplizierter wird es, wenn man z.B. vor Rückkehr (absetzen des Befehls an FHEM) wirklich überprüfen will/muss ob die gewünschet Aktion auch gelaufen ist.
Das notify kann für mehrere dummy verwendet werden.

Dienst steuern

Um einen Dienst zu starten und zu stoppen muss das Script und der dummy etwas angepasst werden. Zum Testen habe ich meinen Dienst aus diesem Beitrag genommen. Der Name muss entsprechend angepasst werden. Man könnte den Dienstnamen auch im notify übergeben.
Der Steuerbefehl (event) wird nochmal überprüft und übergeben. Nach kurzer Wartezeit wird der Status abgefragt und dieser direkt an FHEM übergeben. Der Status ist also direkt im dummy sichtbar
#!/bin/sh
# Parameter Übergabe
host=$1
name=$2
event=$3
service=<Dienst Name>
# Abfrage und Verzweigung
case "$event" in
    start|stop)
   # Sicherstellen das nur zulässige Events verwendet werden
   sudo systemctl $event $service 
   sleep 2
          status=$(systemctl is-active $service)
   # Web Kommando an FHEM absetzen (neu mit csrfToken)
   h=$host':8083'; c='set '$name' '$status; curl --data "fwcsrf=$(curl -s -D - http://$h/fhem?XHR=1 | awk '/X-FHEM-csrfToken/{print $2}')" http://$h/fhem?cmd=$(echo $c|sed 's/ /%20/g')

    ;;

    *)
      exit 1
    ;;

esac

exit 0

Wenn der Dienst läuft wird er als active erkannt, wenn er gestoppt ist liefert er unknown zurück. Das Mapping des devStateIcon wird einfach angepasst.

attr testdummy devStateIcon active:on:stop unknown:off:start

Montag, 16. Januar 2017

Per ssh Remote Befehle direkt ausführen

Hinweis 2021: Dieser Artikel ist etwas in die Jahre gekommen und ich habe mittlerweile einige Dinge neu beschrieben:
---

Dienst unter Raspbian einrichten

Wahrscheinlich passt das Thema unter jedem debian System, ich hab es aber auf dem Raspberry unter Jessie getestet.
Mein Ziel war es, selbst einen Dienst einzurichten und dabei die Unterschiede zwischen wheezy (SysVinit) und Jessie (SystemD) zu lernen.
Eine gute Beschreibung für meinen Start habe ich hier gefunden. Python ist wie Perl als Grundlage im Debian System schon vorhanden, man braucht also keine weitere Installation. Das Python Script aus dem genannten Artikel ist eine gute Grundlage und ich habe es etwas modifiziert.
Alle Scripte müssen im Unix Format (nur lf als Zeilenwechsel) gespeichert werden!

Das Demo-Script

Es schreibt kontinuierlich in eine "Tages"-Logdatei im /tmp Verzeichnis und kann bei normalen Start entweder mit ctrl-c oder im Hintergrund einfach mit kill beendet werden. Das Script demonstriert ein paar nützliche Techniken, ich habe lediglich den Abbruch am Ende entfernt und das Intervall auf eine Minute heraufgesetzt. So läuft das Script "ewig". Im Script selbst ist das Wichtigste dokumentiert.

#!/usr/bin/env python

import logging
import logging.handlers
import argparse
import sys
import time  # this is only being used as part of the example

# Defaults
LOG_FILENAME = "/tmp/myservice.log"
LOG_LEVEL = logging.INFO  # Could be e.g. "DEBUG" or "WARNING"

# Define and parse command line arguments
parser = argparse.ArgumentParser(description="My simple Python service")
parser.add_argument("-l", "--log", help="file to write log to (default '" + LOG_FILENAME + "')")

# If the log file is specified on the command line then override the default
args = parser.parse_args()
if args.log:
        LOG_FILENAME = args.log

# Configure logging to log to a file, making a new file at midnight and keeping the last 3 day's data
# Give the logger a unique name (good practice)
logger = logging.getLogger(__name__)
# Set the log level to LOG_LEVEL
logger.setLevel(LOG_LEVEL)
# Make a handler that writes to a file, making a new file at midnight and keeping 3 backups
handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight", backupCount=3)
# Format each log message like this
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
# Attach the formatter to the handler
handler.setFormatter(formatter)
# Attach the handler to the logger
logger.addHandler(handler)

# Make a class we can use to capture stdout and sterr in the log
class MyLogger(object):
        def __init__(self, logger, level):
                """Needs a logger and a logger level."""
                self.logger = logger
                self.level = level

        def write(self, message):
                # Only log if there is a message (not just a new line)
                if message.rstrip() != "":
                        self.logger.log(self.level, message.rstrip())

# Replace stdout with logging to file at INFO level
sys.stdout = MyLogger(logger, logging.INFO)
# Replace stderr with logging to file at ERROR level
sys.stderr = MyLogger(logger, logging.ERROR)

i = 0

# Loop forever, doing something useful hopefully:
while True:
        logger.info("The counter is now " + str(i))
        print "This is a print"
        i += 1
        time.sleep(60)

SysVinit konfigurieren 

Init-Script

Ich möchte den Dienst unter einem normalen User laufen lassen und nicht als root (default). Im Pfad /etc/init.d/ befinden sich die Scripte welche zur Dienste Steuerung verwendet werden. Dort gibt es auch ein Script Namens skeleton, welches als Template verwendet werden kann. Also zunächst mal das Steuerungs Script, wiederum aus dem eingangs erwähnten Artikel, leicht modifiziert. Wie zu sehen ist, soll der Dienst unter dem User pi ($DAEMON_USER) und im Homedirectory ($DIR) vom User laufen.

#!/bin/sh

### BEGIN INIT INFO
# Provides:          myservice
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Put a short description of the service here
# Description:       Put a long description of the service here
### END INIT INFO

# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/home/pi
DAEMON=$DIR/myservice.py
DAEMON_NAME=myservice

# Add any command line options for your daemon here
DAEMON_OPTS=""

# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=pi

# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid

. /lib/lsb/init-functions

do_start () {
    log_daemon_msg "Starting system $DAEMON_NAME daemon"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
    log_end_msg $?
}
do_stop () {
    log_daemon_msg "Stopping system $DAEMON_NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    log_end_msg $?
}

case "$1" in

    start|stop)
        do_${1}
        ;;

    restart|reload|force-reload)
        do_stop
        do_start
        ;;

    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
        ;;

    *)
        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
        exit 1
        ;;

esac
exit 0

Scripte kopieren und einrichten

Mit winscp werden die Scripte ins Home Verzeichnis von pi kopiert. Dann mit putty ein ssh Terminal öffnen und das Init-Script an Ort und Stelle kopieren, Rechte setzen und den Dienst einrichten.
sudo cp myservice.sh /etc/init.d/
chmod +x myservice.py
chmod +x /etc/init.d/myservice.sh
sudo update-rc.d myservice.sh defaults
Jetzt kann man mit den üblichen Befehlen oder durch direkten Script Aufruf den Dienst starten, abfragen und beenden.
sudo /etc/init.d/myservice.sh start
service myservice status
sudo service myservice stop
Will man den Dienst wieder entfernen, hilft uns update-rc.d mit den Optionen disable und remove.

SystemD

SystemD wird unter jessie mit installiert und läuft kompatibel und transparent. Man kann also sofort den Dienst auch mit systemctl <start|status|stop> myservice kontrollieren. Ich will aber nun die "alte" Einrichtung deaktivieren und den Dienst unter SystemD einrichten.
Ein Dienst in SystemD wird mit sogenannten Unit Files eingerichtet.

1. Variante: einfach das init.d Script verwenden

Im FHEM Forum gibt es ein HowTo wie man den existierenden Service in einen SystemD Service umwandelt. Die folgende Datei wird als myservice.service gespeichert und mit winscp wieder nach /home/pi übertragen.
Wie man sieht, wird dabei gleich das alte SysVinit Script recycelt.

[Unit]
Description=Test myservice

[Service]
Type=forking
ExecStart=/etc/init.d/myservice.sh start
ExecStop=/etc/init.d/myservice.sh stop

[Install]
WantedBy=multi-user.target

Zunächst also den Dienst wieder entfernen, die Datei nach /etc/systemd/system kopieren und systemd über die Änderung informieren.
sudo update-rc.d myservice.sh disable
sudo cp myservice.service /etc/systemd/system/
sudo systemctl --system daemon-reload
Jetzt kann man zur Probe den Dienst mit systemctl <start|status|stop> myservice starten. Wenn alles funktioniert muss er noch aktiviert werden.
sudo systemctl enable myservice

2. Variante: extra Startscript verwenden 

Hierauf basierend habe ich mir ein universelles Startscript erstellt. Zumindest mit meinem Pythonscript funktioniert das einwandfrei. Im Endeffekt ist es aber in der Funktion dem SysVinit adäquat. Die Funktion ist anders realisiert, ich finde es besser lesbar und der SysVinit Abschnitt am Anfang fehlt. Letztendlich könnte man sogar den Scriptaufruf als Parameter übergeben.
#!/bin/bash

PID=""
script="python myservice.py"

function get_pid {
   PID=`pidof $script`
}

function stop {
   get_pid
   if [ -z $PID ]; then
      echo "server is not running."
      exit 1
   else
      echo -n "Stopping server.."
      kill -9 $PID
      sleep 1
      echo ".. Done."
   fi
}


function start {
   get_pid
   if [ -z $PID ]; then
      echo  "Starting server.."
      $script &
      get_pid
      echo "Done. PID=$PID"
   else
      echo "server is already running, PID=$PID"
   fi
}

function restart {
   echo  "Restarting server.."
   get_pid
   if [ -z $PID ]; then
      start
   else
      stop
      sleep 5
      start
   fi
}


function status {
   get_pid
   if [ -z  $PID ]; then
      echo "Server is not running."
      exit 1
   else
      echo "Server is running, PID=$PID"
   fi
}

case "$1" in
   start)
      start
   ;;
   stop)
      stop
   ;;
   restart)
      restart
   ;;
   status)
      status
   ;;
   *)
      echo "Usage: $0 {start|stop|restart|status}"
esac

Praktisch gesehen ist der Unit Teil bei Systemd vom Startscript getrennt. Basierend auf dieser Dokumentation  habe ich  mal eine ziemlich "aufwändige" Variante erstellt. Hierbei wird start|stop|restart durch das obige Script gesteuert.

[Unit]
Description=Test myservice
After=network.target

[Service]
Type=forking
User=pi
WorkingDirectory=/home/pi
ExecStart=/home/pi/server.sh start
ExecStop=/home/pi/server.sh stop
ExecReload=/home/pi/server.sh restart
KillMode=none

[Install]
WantedBy=multi-user.target

3. Variante: Kurz und knapp einfach ein Unitfile erstellt

Hier übernimmt systemd die komplette Steuerung. Überraschenderweise funktioniert dies auch mit meinem Endlosscript. Ich habe die Anregung dazu von hier. Dort ist noch ein besseres Beispiel mit einem kleinem Webserver als Testscript dargestellt. Lässt man den User= Eintrag weg startet der Dienst als root.

[Unit]
Description=Test systemd myservice

[Service]
User=pi
ExecStart=/home/pi/myservice.py
StandardOutput=null

[Install]
WantedBy=multi-user.target

Sonntag, 15. Januar 2017

Code Blöcke formatieren und hervorheben

Der Editor von Blogger.com ist für mich für die meisten Dinge völlig ausreichend, allerdings möchte ich die Codeblöcke in meinem Blog gut lesbar formatieren.
Ich habe mehrere Möglichkeiten gefunden dies zu tun, leider ist so etwas ganz einfaches nicht dabei. Quasi als meinen Notizzettel, habe ich die Möglichkeiten, die ich so gefunden habe mal aufgeschrieben.
Generell unschön finde ich, dass man für alle Varianten vom "Verfassen" Modus in den "HTML" Modus wechseln muss, aber nur so kann man den für die Formatierung notwendigen HTML Code direkt eingeben. Eine andere Möglichkeit habe ich nicht gefunden.

Copy & Paste

Zunächst mal gibt es ein paar Seiten, die bieten einen "Code Formatierer" an.
Man kopiert seine Code Zeilen in die Zwischenablage, wechsel auf die Seite mit dem Formatierer und fügt sie in ein spezielles Fenster wieder ein. Man kann hier meist ein paar Optionen einstellen und beim Drücken eine Knopfes wird in einem weiteren Fenster die Formatierung gezeigt und in einem Dritten kann man den HTML Code kopieren und die Formatierten Codezeilen so einfach in seine Blog an die gewünschte Stelle einfügen.
Das geht eigentlich alles sehr simpel und benötigt überhaupt keine Vorbereitung.

Eine relative schlichte Optik kann man auf dieser Seite erzeugen:
http://codeformatter.blogspot.de/2009/06/how-to-format-my-source-code-for.html

 Codezeile eins 
 Codezeile zwei

Sehr auffällig und bunt geht es auf dieser Seite:
http://hilite.me/

1
print 'hello world!'

Template oder Vorlage editieren

Etwas aufwendiger ist es die Stylesheets zu editieren. Dabei wird ein Stück HTML Code in der Vorlage zum Blog (die austauschbar ist und die Optik des Blogs wesentlich bestimmt) eingebaut und an entsprechender Stelle wird dann lediglich noch ein HTML-Tag eingefügt. Der Code zur Formatierung wird zentral abgelegt und der Code Block bloß mit einem Kurzen Tag gekennzeichnet. Das hat noch den Vorteil, man kann das Aussehen der Formatierung an einer Stelle für den gesamten Blog ändern. Dazu ein einfaches Beispiel, welches auf diesen Artikel basiert.

Mit Hilfe von dem Tag textarea kann man eine kleine Box einfügen:
Einfach folgenden Code in der HTML Ansicht einfügen
<textarea name="textarea" cols="40" rows="4" wrap="VIRTUAL">
YOUR CODE GOES HERE
</textarea>
Das Ergebnis sieht so aus:



Der Vorteil dieses kleinen Textrahmens ist, das man ihn in der normalen Editor Ansicht sieht. Er hat in der Editor Ansicht auch "editierende" Eigenschaften:
Man kann an der kleinen Ecke unten rechts ziehen und den Rahmen optisch anpassen. Es wird dabei automatisch dieser HTML Code zwiswchen rows= und wrap eingefügt:
style="height: 64px; margin: 0px; width: 301px;"
Man kann hier eine definierte Höhe und Breite eintragen, oder anstatt mit Pixeln(px) auch mit auto oder 100% arbeiten.

Um eine Codebox als CSS in den Blog einzufügen, muss man folgendes tun:

  1. Am Dashboard anmelden und links auf Vorlage navigieren.
  2. Unter der Vorlage auf Anpassen klicken
  3. Links oben auf Erweitert und daneben nach unten scrollen und auf Add CSS klicken
  4. In dem Fenster Benutzerspezifisches CSS hinzufügen gibt man den unten stehenden Code Block ein
  5. Anschließend oben rechts auf "Auf Blog anwenden" drücken.

Jetzt kann man Seinen Code Block in der HTML Ansicht einfach mit diesen Tags "einrahmen":



Großer Nachteil: Den Erfolg sieht man erst in der Vorschau, in der normalen Editor Ansicht sieht man leider nichts und man muss verdammt aufpassen wenn man mal was korrigiert. Zwei Parameter sorgen für wichtige Eigenschaften:
  • overflow: auto - bewirkt, dass Scrollbalken angezeigt werden, sobald die max-height überschritten wurde. 
  • white-space: nowrap - sorgt dafür, dass kein automatischer Textumbruch stattfindet.

Code Block

Java Script einbetten

Eine relativ elegante Methode ist die Formatierung mit dem Javascript Prettify, der Quellcode und Beschreibung findet man hier: https://github.com/google/code-prettify
Als erstes muss man den Script Code im Blog verfügbar machen. Dazu muss man nicht die Vorlage editieren, man kann es einfach im Layout einbetten. Dazu fügt man im Layout (Dashboard/Layout) einfach an unauffälliger Stelle ein Gadget hinzu. Man wählt das Gadget "HTML/JavaScript konfigurieren" aus der Liste aus, gibt den Titel PrettyPrint ein und im Content wird folgender Code eingefügt:
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>

Die Code Blöcke werden in der HTML Ansicht einfach in



eingepackt. Jetzt wird abhängig von der Programmiersprache auch entsprechend hervorgehoben.

Feintuning

Kombination aus Prettify und Codeformatter

Damit der Text gut lesbar bleibt, sollten Code Passagen nicht allzu lang sein, bzw. sollte man sie im Text in Scrollboxen optisch einkürzen. Dazu verwende ich einfach noch die style Eigenschaft aus dem Codeformatter. Das style Attribute height:auto passt das Fenster der Anzahl Zeilen automatisch an, das Attribute max-height:300px; begrenzt die Box und fügt Scrollbalken ein.
<pre class="prettyprint" style="height: auto; max-height: 300px; overflow: auto; width: auto;">
Code Block
</pre>
Der style Teil kann mit in das beschriebene Gadget eingefügt werden:
<style>
pre {
  height: auto;
  max-height: 300px;
  overflow: auto;
  width: auto;
}
</style>
Damit der Code einfach markiert werden kann wird noch ein spezieller Code tag eingefügt:
<pre class="prettyprint"><code contenteditable="true">Code Block
</code></pre>

Quelle


Hier ein Beispiel, dies ist kein funktionierender Code!:

#!/bin/bash

# Laufzeit des Scripts messen 
# Zeitmessung Start.
REAL_STARTTIME=$(date +%s) #(($STARTTIME - $PRE_NETWORK_DURATION))
echo ""
echo "Installation started at $(date --date="@$REAL_STARTTIME" --utc)."

#Grundlage für FHEM installieren
apt-get update && apt-get -y install libdevice-serialport-perl libwww-perl libio-socket-ssl-perl
#Meine Pakete installieren
apt-get -y install bluez libcrypt-rijndael-perl libdigest-sha-perl libgd-graph-perl libgd-text-perl libimage-info-perl libimage-librsvg-perl libjson-perl libjson-xs-perl libmime-base64-perl libnet-ssleay-perl libnet-telnet-perl libnet-telnet-perl libnet-upnp-perl libsoap-lite-perl libsoap-lite-perl libxml-parser-lite-perl mplayer msttcorefonts samba samba-common-bin sendEmail telnet

# Manuelle Installation
# wget https://debian.fhem.de/fhem.deb
# dpkg -i fhem.deb

# von debian.fhem.de installieren 
# Erstmal https für apt nachinstallieren
# https Transport installieren und Sourcellisten sichern
apt-get -y install apt-transport-https
wget -qO - https://debian.fhem.de/archive.key | apt-key add -
cp /etc/apt/sources.list /etc/apt/sources.list.bak
# Manuelle Installation
# wget https://debian.fhem.de/fhem.deb
# dpkg -i fhem.deb

# von debian.fhem.de installieren 
# Erstmal https für apt nachinstallieren
# https Transport installieren und Sourcellisten sichern
apt-get -y install apt-transport-https
wget -qO - https://debian.fhem.de/archive.key | apt-key add -
cp /etc/apt/sources.list /etc/apt/sources.list.bak
# Manuelle Installation
# wget https://debian.fhem.de/fhem.deb
# dpkg -i fhem.deb

# von debian.fhem.de installieren 
# Erstmal https für apt nachinstallieren
# https Transport installieren und Sourcellisten sichern
apt-get -y install apt-transport-https
wget -qO - https://debian.fhem.de/archive.key | apt-key add -
cp /etc/apt/sources.list /etc/apt/sources.list.bak

# reboot

Oder mit Zeilenummer:
<pre class="prettyprint linenums" style="height: auto; max-height: 300px; overflow: auto; width: auto;">
Code Block
</pre>

Formatierung über Gist und GitHub

Für Powershell funktioniert prettyprint leider nicht sehr gut. Ich habe eine einfache Möglichkeit hier gefunden https://gist.github.com/ Die ist hier kurz beschrieben.
Leider ist die Hervorhebung von Powershell auch nicht optimal. Größter Nachteil ist: Beim Erstellen des Blogbeitrages sieht man vom Code gar nichts.

Um die Höhe der Scriptfenster etwas zu begrenzen kann man im css Bereich noch etwas konfigurieren (Blogbeitrag):

.gist .blob-wrapper.data {
   max-height:300px;
   overflow:auto;
}


Ob man das Alles noch eleganter machen kann, weiß ich derzeit noch nicht.

Zeilenumbruch

Manchmal ist es nützlich in HTML Ansicht Zeilenumbrüche einzufügen um an einer definierten Stellen Leerräume in der Normalen Ansicht zu haben. Einfach diesen Tag einfügen
<br />


Freitag, 6. Januar 2017

Umzug von FHEM

Die alte Hardware ist zu schwach, defekt oder warum auch immer - das System soll neu gemacht werden!?
Ausgangszustand:
  • Raspberry Pi2, 
  • Raspbian Wheezy, 
  • Probleme mit der SSL Lib beim https Zugriff (SSL Version SSLv2 not supported), ist mit jessie behoben.
Zielzustand:
  • Raspberry Pi3
  • Raspbian Jessie Lite

Geht relativ einfach, wenn man einige Vorbereitungen trifft. Der grobe Fahrplan sieht so aus:
  1. Auf neuer Hardware ein neues System installieren.
  2. Alle benötigten Tools und Module (debian Pakete) nachinstallieren.
  3. FHEM als Grundlage installieren, eventuell Details testen (z.B. SSL).
  4. Backup und shutdown aktuelles FHEM System auf Pi2
  5. Transfer der Backupdatei auf Pi3
  6. Restore und Start von FHEM auf dem Pi3
  7. Test ob System einwandfrei läuft, wenn ja alles gut, wenn nein eventuell erstmal Restart FHEM auf Pi2 und in Ruhe untersuchen -> nochmal ab Schritt 4 bis alles läuft.

Schritt 1-3 

habe ich hier ausführlich beschrieben, das dauert je nach dem was man im Schritt 2 alles benötigt alles in allem keine 20 min. Dieser Artikel wird von mir laufend aktualisiert. Zu dem Wissen zu gelangen was man alles benötigt, kann allerdings wesentlich länger dauern, wenn man sich nichts aufgeschrieben hat. Im Zweifelsfall sollte man ein Paket/Tool mehr als eines zu wenig installieren. Die empfohlene Liste auf debian.fhem.de ist sicher ein guter Kompromiss.  Ich habe angefangen eine Liste zu erstellen, welches FHEM Modul welche cpan Module bzw. debian Pakete benötigt. Die werde ich noch veröffentlichen.

Schritt 4 und 6 

kann man nach seinen persönlichen Vorlieben gestalten. Normalerweise sollte das in wenigen Minuten abgeschlossen sein.
In der Regel sollte alles was FHEM betrifft in diesem Pfad liegen /opt/fhem/. Dieser Pfad muss auf das neue System und muss die richtigen Berechtigungen erhalten. Ich habe das hier schon mal im Detail beschrieben und es genauso gemacht:

Pi2 in der FHEM Oberfläche 
backup
warten bis backup fertig -> Log oder Eventmonitor
shutdown
  • mit winscp die aktuelle Sicherung ins zentrale Sicherungs-Share auf den Server kopieren
  • und in  einer zweiten Sitzung gleich wieder auf den Pi3 ins /home/pi/ Verzeichnis kopieren
Pi3 als User Pi im putty Terminal
sudo systemctl stop fhem
sudo tar -xvzf /home/pi/FHEM-201xxxxx_xxxxxx.tar.gz -C /opt/fhem/
sudo systemctl start fhem

Schritt 7

  • FHEM Web erreichbar?
  • Log File kontrollieren!!!

Wenn bis hier grobe Fehler vorhanden sind am Besten erstmal den Pi3 wieder beenden und alles auf dem Pi2 wieder starten. In Ruhe Fehler suchen.

  • Schalter betätigen
  • Sonderfunktionen kontrollieren (Sound und Sprachausgabe usw)

Nützliche Hinweise und Befehle

Umfangreiche Manipulation müssen nicht zeileweise mit sudo gemacht werden, sudo su schaltet den sudo Modus ein bis zum exit.

Es kann immer mal passieren, dass die Rechte im fhem Verzeichnis falsch sind. Dies kann man relativ einfach beheben in dem man den Eigentümer aller Dateien setzt:
sudo chown -R fhem:dialout /opt/fhem/
Manchmal muss die Rechte einzelner Dateien setzen, z.B. ausführbar machen:
sudo chmod +x /home/pi/shutdown.py
In besonderen Fällen muss das UID Bit gesetzt werden:
chmod u+s /usr/bin/dfu-programmer
Oder ganz brutal, einfach Rechte auf Alles:
sudo chmod 777 /etc/systemd/system/pishutdown.service
Ein paar Beispiele für User und Gruppen Mitgliedschaften:
useradd -m test
passwd test
gpasswd -a test users
gpasswd -a pi pulse-access
gpasswd -d pi audio
gpasswd -d test audio
groups test
getent group test
cat /etc/group
Samba Freigabe für Sonos einrichten:
sudo mkdir /mnt/SonosSpeak
sudo chmod 777 /mnt/SonosSpeak/
sudo nano /etc/samba/smb.conf
am Ende folgendes einfügen:
[SonosSpeak] comment = Audio-Files for SonosPlayer to Speak
read only = false
path = /mnt/SonosSpeak
guest ok = yes
anschließen den Service neu starten
sudo systemctl restart smbd.service
Zusatzinfo

Das Verhalten/der Syntax in der smb.conf hat sich offenbar bei Jessie etwas geändert! Nutzt man die Originale smb.conf von Raspbian muss man nur den Abschnitt des Shares (siehe oben) einfügen.
Die Anonyme Zugriff auf Shares wird durch die Standard Einstellung im Abschnitt
   [global]
   security = user
   map to guest = bad user
ermöglicht. Die erste Zeile ist die Standardeinstellung und ist in der smb.conf nicht extra gesetzt. Die zweite Zeile steht so in der Original smb.conf schon drin.

Donnerstag, 5. Januar 2017

Kalender auswerten

Am Abend vor einem Termin eine Ansage bekommen? Wie das mit FHEM geht will ich hier kurz zeigen.

Beispiel: Abholungen der Abfalltonnen

Ich habe den Abfallkalender einfach in einem neuen Googlekalender angelegt. Das ist schnell gemacht, man braucht nur drei Terminserien und muss ein paar Tage im Jahr modifizieren. Der von der Abfallwirtschaft angebotene ICAL zum download geht nur hart bis zum Jahresende, hat eine falsche Zeitzone und besteht nicht aus Serien- sondern Einzelterminen.

Anlegen eines Kalenders in FHEM

Für die weitere Arbeit brauchen wir die URL vom Kalender. Diese erhalten wir beim Googlekalender unter Einstellungen/Kalender/<Kalendername>/Privatadresse (der grüne Ical Button) oder Kalenderadresse wenn der Kalender öffentlich ist.

Ein define Befehl in FHEM reicht:
define AbfallKalender Calendar ical url <URL>

Diese Gerät liest jetzt erstmal mal nur den Kalender. Man "sieht" nicht viel, maximal das diese Modul irgendetwas tut. Mit get Befehlen können wir Termine aus dem Kalender abfragen.

Auswertung des Kalenders in FHEM

Für die Ermittlung der Termine für die Abfallentsorgung benutzen wir ein weiteres Modul -> Abfall. Da es sich um ein "Entwicklungsmodul" handelt müssen wir es zuerst in FHEM verfügbar machen. Man kann es gleich installieren, ohne komplettes Update:

update all https://raw.githubusercontent.com/uniqueck/fhem-abfall/master/controls_fhemabfall.txt
Damit es in Zukunft per Update kommt:
update add https://raw.githubusercontent.com/uniqueck/fhem-abfall/master/controls_fhemabfall.txt
Jetzt läuft das einzelne Update und wir müssen es abschließen:
save 
shutdown restart

Mit der folgenden Definiton in FHEM bekommen die heutigen, morgigen und nächsten Termine in entsprechenden Readings.
define Abfall ABFALL AbfallKalender

Hinweis Stand August 2017 
Sollte es mit der aktuelle Version des Moduls Probleme geben, diese hier verwenden.

Erinnerung per Ansage

Eine freundliche Ansage am Abend vor dem Termin und am Morgen des Termins weist uns auf den Abholtermin hin.
define di_AbholungAnsage1 DOIF ([?17:00 - 20:00] and [PIR1:"motion: on"] and [Abfall:next_tage] == 1) ( set MyTTS volume 30, set MyTTS tts Morgen wird [Abfall:next_text] abgeholt)
define di_AbholungAnsage2 DOIF ([?06:00 - 10:00] and [PIR1:"motion: on"] and [Abfall:now_text] ne "" and [Abfall:now_text] ne "0") ( set MyTTS volume 30, set MyTTS tts Heute wird [Abfall:now_text] abgeholt)
attr di_AbholungAnsage.* cmdpause 1800
Hinweis: Derzeit gibt es noch einen temporären Zustand wo nur now_text mit einer 0 gefüllt wird. Deswegen habe ich diesen Zustand extra abgefragt.

Feintuning

Das Standard Intervall zur Abfrage des Kalenders beträgt 1 h (3600 sec). Für diese Anwendung reicht eigentlich ein größeres Intervall (z.B. 4 h=14400). Mit einem zusätzlichen Parameter hinter der URL kann man dies verändern. Einfach die DEF editieren.
Die Ansage wird alle halbe Stunde in dem Zeitraum wiederholt. Das kann man beliebig anpassen.

Montag, 2. Januar 2017

ESP8266 in FHEM

Der ESP8266 Baustein kann ziemlich einfach an FHEM angebunden werden. Das FHEM Modul wird zur Zeit in einem Entwicklerthread vorgestellt.

Vorbereitung

Das Modul braucht das debian Paket libjson-perl.

sudo apt-get update && sudo apt-get install libjson-perl

Seit März 2017 ist ESPEasy offiziell in FHEM enthalten, der folgende Befehl ist nicht mehr nötig.
 Es handelt sich um ein "Entwicklungsmodul" ist also nicht normaler Bestandteil der FHEM Installation. Es lässt sich aber einfach in unsere Installation einbinden, anstatt es nur einmal als Datei herunterzuladen und umständlich in den FHEM Ordner zu kopieren:
update add https://raw.githubusercontent.com/ddtlabs/ESPEasy/master/controls_ESPEasy.txt
update all https://raw.githubusercontent.com/ddtlabs/ESPEasy/master/controls_ESPEasy.txt
shutdown restart

In FHEM wird quasi ein kleiner Web Agent/Empfänger - die Bridge - auf Port 8383 bereitgestellt. Das Port ist natürlich "frei" wählbar.

define espBridge ESPEasy bridge 8383

Damit ist die das Modul empfangsbereit, alles weitere passiert weitestgehend automatisch. Die Bridge erzeugt die eigentlichen Geräte entsprechend der Konfiguration im ESP automatisch. Man braucht nur ein Bridge für mehrere ESP Module.
Das Modul enthält die Dokumentation, sie ist in der lokalen commandref zu finden. Das Modul wird durch diese Installation beim normalen update aktualisiert.

Installation ESP8266

Zunächst muss man auf dem Modul ESPEasy installieren/flashen. Die aktuelle Firmware findet man bei www.letscontrolit.com über diesen Link. Unbedingt den aktuelle release candidate nehmen, derzeit R147_RC8!

Update 07.03.2018
Mittlerweile gibt es eine ESP-Easy Version 2.0.0 und 3
In der Zip Datei ist jetzt das Tool FlashESP8266.exe, dort kann man COM Port und bin Datei (im gleichen Pfad) einfach per Drop Down auswählen. Das Konfigurationsnetzwerk meldet sich jetzt ESP_Easy_0. Die Weboberfläche ist leicht verändert.

Die zip Datei enthält ein paar Dateien und das Flash Programm.
Nach dem Anschluss des Moduls an den USB Anschluss müssen wir im Gerätemanager den COM Port herausfinden (Anschlüsse (COM & LPT) Bei mir COM5.
Es darf ab jetzt kein "serieller Monitor" auf die COM Schnittstelle zugreifen!
Jetzt wird die Datei flash.cmd gestartet, diese fragt zunächst die Parameter ab:

Comport -> nur die Zahl eingeben -> 5
Size - je nach Modul in kByte eingeben -> ESP12F -> 4096
Buildnummer -> R147_RC8 -> 147

Achtung: gern wird die Flashsize auch in kbit angegeben. Ein ESP01 hat 4096 kbit= 512 kByte. Ein ESP12F hat 4096 kByte=4 MByte.

Das Programm erwartet die Datei ESPEasy_R147_4096.bin im gleichen Pfad und startet mit dem esptool.exe den Flashvorgang. Hektisches Blinkern der blauen LED auf dem Modul zeigt die Datenübertragung an. Beim ersten Flashen mit ESPEasy wird nach dem Neustart zunächst der gesamte Flashspeicher gelöscht, dieser Vorgang dauert etwas. Also nicht unruhig werden wenn das Wlan ESP_0 nicht gleich sichtbar wird.
Dieser Flashvorgang löscht nicht jedesmal den kompletten Speicher, das Modul kann auf eine neue Version geflashed werden und die Konfiguration bleibt erhalten! (Der NodeMCU Flasher formatiert den Flashspeicher nach dem Neustart immer komplett.)

Einrichtung des ESP8266

Man sollte nach dem Flashen etwa eine Minute warten und dann den Reset Knopf am Modul drücken. Während dieser taucht ein offenes Wlan ESP_0xxxxx auf. Damit kann man m.M. nichts anfangen.
Es dauert ein paar Sekunden bis sich das Modul nach dem Start  mit einem eigenen verschlüsselten WLAN ESP_0 meldet. Hier findet man die Beschreibung.
Man verbindet sich am einfachsten mit dem Smartphone zu diesem WLAN, das WPA Kennwort ist configesp.
Normalerweise wird man nach der Verbindung sofort auf die Konfigseite des Moduls geleitet.

Dort trägt man seine WLAN Konfiguration in die Weboberfläche ein.

Falls es nicht automatisch passiert löst man die WLAN Verbindung mit dem Smartphone.
Es kann sein, dass das Wlan ESP_0 nach kurzer Unterbrechung wieder verbunden erscheint und man aufgefordert wird sich wieder anzumelden. Diese Anmeldung erfolgt nur für eine Sekunde und das Wlan ESP_0 verschwindet. Ich habe im seriellen Monitor beobachtet, dass man dies auch tun sollte. Erst dann wird er Flash geschrieben und der Baustein neu gestartet. Dies dauert wenige Sekunden.








Nach einem Neustart kann man sich nach kurzer Zeit zu http://newdevice verbinden.
Auf der config Seite konfiguriert man mindestens folgende Felder.

Name: <Eindeutiger Name> damit es im eigenen Netzwerk auffindbar ist.
Protocol: FHEM HTTP
Locate Controller: Auswahl entweder Name oder IP Adresse
Controller IP: <IPAdresse des FHEM Servers>
Controller Port:8383 (Das Gleiche wie beim define der Bridge)

Mit submit speichern!

Die blauen Fragezeichen verlinken ins Wiki, meist mit nützlichen Informationen.

Wenn man jetzt auf der Devices Seite ein Gerät konfiguriert sieht man das Ergebnis nach kurzer Zeit in FHEM.
Als Beispiel kann man dort System Info auswählen. Egal was man dort in der weiteren Klappleiste auswählt man sollte zumindest die Felder Name IDX und Value Name ausfüllen um sinnvolle Einträge zu bekommen.


Hier als Beispiel die RSSI Werte des WiFi Moduls:


Und so sieht es dann nach kurzer Zeit in FHEM aus ->







Tipps

Um die flash.cmd Datei gleich im richtigen Pfad zu öffnen, geht man in Windows auf den Ordner in dem die Datei liegt und drückt die Umschalttaste und gleichzeitig mit der rechten Maustaste das Context Menü - hier kann man den Punkt Eingabeaufforderung hier öffnen auswählen. Bei Windows 10 ab Version 1703 wird hier die Powershell angeboten. In dieser läuft die flash.cmd nicht -> einfach cmd & enter eintippen um die alte commandshell zu starten! Für häufigere Flashvorgänge sollte man sich eine angepasste Batchdatei erstellen.

Auf der Seite Tools kann man den Befehl reset absetzen, der löscht alle Einstellungen und versetzt das Modul wieder in den AP Modus - ohne das man neu flashen muss. Danach bootet das Modul von selbst wieder. Der Löschvorgang dauert etwa 1:40 min. Also nicht unruhig werden, wenn man darauf wartet, dass ESP_0 wieder erscheint.
Das funktioniert auch im Browser: http://<ESP Name>/tools?cmd=reset. Achtung wenn man diesen Befehl abgesetzt hat, bleibt das Fenster so stehen, wechselt man wieder in dieses Fenster oder drückt Refresh wird der Befehl sofort erneut gesendet.
Man kann "Factory Reset" auch per Hardware machen: Einfach RX und TX verbinden und neu starten. Den Trick habe ich hier  als Ausweg aus dem Deep Sleep gefunden.

Auf der Seite tools kann man auch die gesamte Konfiguration speichern und wiederherstellen. Der Dateiname endet zwar per default auf txt, es ist aber keine Textdatei. Man kann damit also nur die komplette Konfiguration übertragen, inklusive Gerätenamen usw. und leider nicht zwischendurch editieren.

Stromversorgung

Der verbreitete FTDI Chip darf laut Datenblatt 50 mA liefern, das ESP Modul kann bis zu 300 mA Spitzenstrom ziehen und verbraucht schon im normalen Modus ca. 50-70 mA. Die handelsüblichen FTDI Adapter ohne separaten Spannungsregler sind damit ohne externe 3,3 Volt Versorgung ungeeignet. Alle Developerboards haben separate Spannungsregler!

Fehlerquellen

Die Stromversorgung des ESP Bausteines ist nicht unkritisch. Häufig sieht alles gut aus, der Flashvorgang läuft durch aber anschließend funktioniert nicht alles -> Der Flashinhalt ist korrupt!
Ein billiger FTDI Adapter z.B. liefert nicht genügend Strom um den ESP zu betreiben. Ein zu langes mehrfach gestecktes USB Kabel kann sich als Fehlerquelle entpuppen.

Wenn sich der Baustein eigenartig verhält, ESP_0 nicht erscheint sondern ein anderes Wlan ESP_xxxx oder der Baustein nach korrektem Eintrag der SSID und Passwort und der Anmeldung im Wlan nicht als http://newdevice erreichbar ist -> unbedingt Stromversorgung des Bausteins prüfen!