openHab2 startup chaos

Thursday, October 25. 2018

starting and stopping openhab2 is a messy affair, filling the logs with loads of Java's verbose exception messages and since the order of loaded models is basically random things may or may not work as intended when items have not been loaded or rules not executed that are assumed to be there.

As it was manual corrections and reloads were needed after each restart.

There is an elegant workaround discussed at community.openhab.org/t/cleaning-up-the-startup-process-renaming-rules-windows-possible/38438/9 which uses systemd's

ExecStartPre and ExecStartPost commands to deactivate all rules before starting openhab intially, and then reactivate them one by one when the system is up and has it's feet on the ground. a small bash script does the renaming. Since the openhab2.service file is part of the package and thus is replaced with each update modifying the .service file would need to be repeated whenever a new version gets installed, but, systemd provides a way to override a service definition. systemctl edit openhab2.service creates an override dir and opens the editor to allow creating service file that is not replaced by the next update. Since the bash script works the list of rules -files in an alphanumerical order a naming scheme like praefixing all .rules with a three digit number finally allows to control the order of rules loading.

 

The bash script sits at some place like /etc/openhab2/exec-scripts/and looks like:

 

 

#!/bin/bash
#######################
#
# clean up the start process 
#  starting rules in a sorted manner after openhab2 got it's feet on the ground
#  called from systemd pre and post start 
#  to rename *.rules away initially and then rename them back one by one
#  
#  /etc/systemd/system/openhab2.service.d/.#override.conf041cd66becaa4539
#
# $3 allows to distinguish between pre and post action for other THings to eXecute
#
# REF: https://community.openhab.org/t/cleaning-up-the-startup-process-renaming-rules-windows-possible/38438/9


ORG=$1
NEW=$2
THX=$3
DUR=1
#IGNORE=moverules.rules

for f in /etc/openhab2/rules/*.${ORG};
do
    CURRENT=$(basename $f)
    if [ "$CURRENT" == "$IGNORE" ]    
    then
        echo "ignoring $IGNORE"
    else
        OLDFILE=$f
        NEWFILE=${f%$ORG}$NEW
        mv "$OLDFILE" "$NEWFILE"
    fi
    # let some time between each load
    if [ "$THX" == "POST" ]
    then
        /bin/sleep $DUR
    fi
done

# some things left on startup
if [ "$THX" == "POST" ]
then
    /usr/bin/touch /etc/openhab2/tradfri.things
fi



 

The relevant lines in the [Service] part of the openhab2.service file are:

 

 
ExecStartPre=/etc/openhab2/exec-scripts/move_rules_at_start.sh rules rules_ 
ExecStart=/usr/share/openhab2/runtime/bin/karaf $OPENHAB_STARTMODE 
ExecStop=/usr/share/openhab2/runtime/bin/karaf stop 
ExecStartPost=-/bin/sleep 90 
ExecStartPost=/etc/openhab2/exec-scripts/move_rules_at_start.sh rules_ rules POST 
TimeoutStartSec=180  

 

(The lines with ExecStart / ExecStop are already there.)

TimeStamp in mosquitto.log

Monday, September 24. 2018

 mosquitto gibt jedem Log-Eintrag einen TimeStamp im für Maschinen recht praktischen Aekunden-seit-Epoche - Format. Für menschliche augen ist diese Angabe eher sperrig.

Mit perl und ccze (macht's bunt) kann man die Ausgabe etwas aufhübschen:

tail -n2000 -f  mosquitto/mosquitto.log |  perl -pe 's/(\d+)/localtime($1)/e'| ccze -m ansi

 

Presence Detection in WLAN

Sunday, September 9. 2018

 I was not content with the network/pingdevice based presence detection openhab2 offers. While it finds and detects smarphones connect to the wlan, it soon gets beaten by some energy-savong sleep modes, so the device does not ping. It may still respond to arping, though.

The following script is used on a linux router with several interfaces where a device miight be reachable. It can be configured from a settings.ini, reads it's sc an targets from a different ini file and reports it's findings via openHab's REST api.

settings.ini:

[settings]
baseUrl=http://koel.intern:8080/rest/items/
interval=30
interfaces=eth0,eth2


baseUrl points to the REST api of the openHab2 server, interfaces is a comma separated list of the interface/s that should get scanned and interval gives the number of seconds the scanner will sleep between to scans

scan.ini:

[feng]
item=presence_feng
MAC=C0:EE:FB:43:A8:ED

[goldAmmer]
item=presence_goldammer
MAC=192.168.123.11


[ente]
item=presence_ente
MAC=192.168.123.99


Each device has its own section with the section name acting as the unique id of the device, item is the openHab item (expected to be a Switch) that should get the result (ON or OFF) and MAC has the MAC or IP of the device 

The scanner is done in python (2.7)
arpscan.py:

#!/usr/bin/env python
#############################

import ConfigParser
import string
import os
import subprocess
import time
import sys

conf={}
devices={}
interfaces=""
scantext=""
grace=3

def get_script_path():
   return os.path.dirname(os.path.realpath(sys.argv[0]))
        
def LoadConfig(file, config={}):
   """
   returns a dictionary with keys of the form
   <section>.<option> and the corresponding values
   """
   config = config.copy(  )
   cp = ConfigParser.ConfigParser(  )
   cp.read(file)
   for sec in cp.sections(  ):
       name = string.lower(sec)
       obj  = { }
       for opt in cp.options(sec):
           obj[string.lower(opt)] = string.strip(
               cp.get(sec, opt))
       config[name]=obj
   return config

def ScanThings():
       scantext=""
       for interface in interfaces:
           p = subprocess.Popen(["arp-scan","-l","-q","-r","3", "-I", interface], stdout=subprocess.PIPE)
           output, err = p.communicate()
           scantext += output
                
       for key in devices:
           device=devices[key]
           url=conf['settings']['baseurl']+device['item']
           if device["mac"].lower() in scantext.lower():
               print key+" ja"
               val = "ON"
               device["tap"]=0
           else:
               print key+" nein"
               device["tap"] = device.get("tap", 0) + 1
               if device["tap"] <= grace:
                   print key+" tap: "+str(device["tap"])
                   val="na"
               else:
                   val="OFF"
                    
           if val != "na":         
               os.system('/usr/bin/curl --silent --header "Content-Type: text/plain" --request POST --data '+val+' '+url)


conf=LoadConfig(get_script_path()+"/"+"settings.ini", conf)
devices=LoadConfig(get_script_path()+"/"+"scan.ini",devices)

interfaces=conf['settings']['interfaces'].split(",")


while 1:
   ScanThings()
   time.sleep(float(conf['settings']['interval']))

 

           

 

           

The script is most useful when run as a daemon. You can easily do this with systemd which uses .service files found in /lib/systemd/system/

arpscan.service:

[Unit]
Description=Run arpscan device detection
After=multi-user.target
 
[Service]
Type=simple
ExecStart=/usr/bin/python /usr/local/bin/presence/arpscan.py
Restart=on-abort
 
[Install]
WantedBy=multi-user.target

The scanner reads settings and scan data, runs an arpscan on each configured interface and finally searches the specified MAC (or IP, just one of the two is needed) in the result.
it needs the linux arp-scan package to be installed, which you can test and confirm by running

arp-scan -l -I eth0

from the commandline. It needs to be run as root.

Some devices may show up oscillating between on and off, to get more stable results the script waits for a number of fails before the "OFF" result is sent. This can be finetuned by the value for grace. 

 

 Technologiewechsel, die Umstellung alter ISDN-Anschlüsse auf Voice over IP und damit verbunden von DSL auf VDSL rollt planmässig und wie eine Dampfwalze und nun war mein Anschluss auch dran. Ein paar mails, ein Brief, ein Päckchen mit der weissen Homebox-Flunder. Dann war der Tag da und obwohl mir der Abschluss der Ustellung erst für 17.30h angekündigt war, war um 10 schon alles über die Bühne. Kabel umgepfriemelt, einen ZUgangscode eingetippt, paar Minuten gewartet und dann war die flunder im Netz. 

Soweit so gut, nun zum Manual und die Kiste als Modem einrichten, wie es im Zyxel -Handbuch für das Teil beschrieben ist - bloss: die im Handbuch beschriebenen Menueoptionen gab es auf meiner Kiste gar nicht. Einfach nur das Modem austauschen und meine eigene Linux-Büchse dahinter als Router weiterlaufen lassen wie seit 20 Jahren schon - dass ging nicht. 
pppd passthrough, den die Flunder laut Manual anbietet, kann man nicht einstellen und so musste ich erst mal meine firewall-Konfig zerschiessen, um überhaupt meinem Lan den Weg ins Netz zu öffnen. Und an dem nun fehlenden event des allmorgendlichen pppd-Neustarts (um die Zwangstrennung zu einer mir genehmen Zeit stattfinden zu lassen) - das Update meines dynamischen DNS bei dns.he.net, das Update des IPv6 over Ipv4-Tunnels, überhaupt der Tunnel, Kollateralschäden. Für IPv6 hat die Kiste übrigens Einträge, aber die bleiben alle leer. Das ist Telefonica oder ihrem Vorleister Telekom noch zu neumodisch...

Dann verriet mir nmap, dass alle ports gefiltert seien. Die Kiste lässt mich aber die Einstellungen der Firewall weder ansehen noch ändern - alles was man machen kann, ist Port-Forwarding für TCP und UDP einzurichten. Dabei zeigt sich, dass 7170/TCP und 5060/UDP reserviert sind, auf letzterem läuft SIPund ersterer Port wird für Fernwartung genutzt bzw. freigehalten.

OK, damit waren nun die Services auf dem Gateway - der Proxy zu OpenHab für remote-Zugang zum SmartHome, der MQTT-Server, Da kleine tool auf dem Apache - wieder von Aussen zugänglich. Der IPv6-Tunnel blieb aber tot.

Das ist alles recht unerfreulich, ich kann schon den Nutzen davon sehen, so eine Homebox 'Omi-sicher' auszuliefern, aber auch erfahrenere Kunden so in einen weissen Käfig zu sperren - bei Hansenet (selig) oder auch Alice hätte es das nicht gegeben.

Recherche im Netz fand keinen workaround, keinen Trick zum Freischalten, sondern immer nur den Verweis auf die Fritzbox 7490. Schon bestellt.

Aber, nach all dem Gemecker: es gab auch eine erfreuliche Überraschung, und das ist die Bandbreite.Die war zu Alice-Zeiten mal bei stolzen 17,x MBit/sec im Download gewesen und in den letzten Jahren immer mehr verkümmert, letzthin war ich schon froh, wenn es 13 MBit/sec waren und nicht nur 10.
Ja, und nun kraxelte der Graph in meinem Bandbreiten-Check plötzlich bei 30 herum, und der Download knapp bei 10.

mit iPerf, scp und diversen Bandbreiten-Checkern nachgemessen (was bei letzteren Ergebnisse mit bis zu 54MBit anzeigten), openHab zeigte den Wechsel so:
2 bis 2,5 fachen Download und zehnfache Bandbreite im Upload. 

Man kann auch mal was gut finden..

bandbreit

Wie ich später merkte, war es tatsächlich der WLAN-Durchsatz, der den gemessenen Upload auf 35 kbit/s begrenzte, mit einem Lan-Kabel habe ich nun ~47, also auch im Upload das 3-4 - fache.

dash ruft smartHome

Wednesday, March 7. 2018

Und wie ?

das Teil hat eine recht kleine Batterie und Amazon hat die Button deshalb so ausgelegt, dass sie nur nach dem Knopfdruck kurz booten, ins WLAN gehen, ihre Message abgeben und sich dann wieder runterfahren.
Bei der Ersteinrichtung lernt das dash die SSID und das Passwort für das Wlan, aber die IP holt es sich jedesmal vom dhcpd.
Und dabei kann man den Button erwischen.

Im Prinzip konfigurierst du dir den dash erst mal so, wie Amazon vorgibt. (Wähle dir einen dash, der Bestellungen ~5€ von Produkten erlaubt, die du gebrauchen kannst. Du bekommst nicht 4,99 erstattet sondern auf die erste Bestellung bis zu 4,99€ Rabatt. Zu billig darf das Produkt also nicht sein) 
Und ja, einmal bestellen, um den Rabatt zu kassieren. (Persönlich finde ich, dass A. da ausreichend 'Ne-doch-nich'-Schwellen und Kindersicherungen eingebaut hat. )
Dann setzt du den Dash zurück und gehst den Weg noch einmal, aber den letzten Schritt: Produktwahl, lässt du aus. Schon hast du einen Dash, der funktioniert, aber nicht richtig bestellen kann. An sich genau, was wir wollen. A. will das nicht und schickt pro Klick auf den dash eine Mail...

Jetzt ist es an der Zeit, in /var/log/daemon.log (oder wo bei dir der dhcpd seine logs ablegt) nachzuschauen, welche MAC der dash benutzt. Und für die MAC legt du eine feste IP an. 
Mit WireShark kannst du nun zuschauen, was der dash mit A. so zu besprechen hat. Und dann machst du in der firewall genau diese Verbindung zu. Aus ist's mit den Mails.

So, jetzt fehlt eigentlich nur noch die Hauptsache. Etwas muss im WLAN die Fühler hochhalten und die dhcp-requests des Dash erschnüffeln. python mit scapy können sowas. Wie genau, tja, da gibt es einen Rüstungswettlauf zwischen A. und Technikforschern. Also googeln und durchprobieren. (siehe unten)

Letztlich hast du etwas, das nach ~3 sec einen event auf deinem smarthome*-Bus auslösen kann, dann ~10 Sekunden Unerreichbarkeit (der dash versucht verzweifelt, A. zu erreichen und schmollt anschliessend noch etwas.)

Alles in allem ein konkurrenzlos günstiger Wifi-Switch mit heftiger Latenz.

 Bei mir tut dies:

#!/usr/bin/python
#############################################
from scapy.all import *
import sys, os

def arp_display(pkt):
    if pkt.haslayer(DHCP):

        if pkt[Ether].src == "fc:65:de:bb:xx:yy": 
            os.system('/usr/bin/curl --silent --header "Content-Type: text/plain" --request POST --data "ON" http://192.168.55.44:8080/rest/items/dashConnect_01')

        if pkt[Ether].src == "fc:a6:67:cc:xx:yy": 
            os.system('/usr/bin/curl --silent --header "Content-Type: text/plain" --request POST --data "ON" http://192.168.55.44:8080/rest/items/dashConnect_02')

print sniff(prn=arp_display, store=0,count=0)


und damit das als daemon läuft und auch nach einem Neustart des raspi wieder läuft, habe ich es als einen service bei systemd angelegt:
/lib/systemd/system/dashsniffer.service 



[Unit]                                                                                                                                                                                            
Description=dash dhcp Paket Sniffer                                                                                                                                                              
After=multi-user.target                                                                                                                                                                           
                                                                                                                                                                                                  
[Service]                                                                                                                                                                                         
Type=simple                                                                                                                                                                                       
ExecStart=/usr/bin/python /var/local/scripts/dash/dashSniffer.py                                                                                                                                               
Restart=on-abort                                                                                                                                                                                  

 
[Install]
WantedBy=multi-user.target


(Page 1 of 35, totaling 174 entries) » next page