I live at a place with a well developed public transport network, within 600 meters from my home there are 1 subway station, 2 city train stations, 3 bus-stops - so there are many alternatives and at times it is not easy to decide which way to turn when I leave my home to get the fastest connection.

They have those info boards at many stations with a count down until the next bus or train arrives. I wanted such a thing for myself, with all the nearby stations on it to check on a glance. And as I build my own smarthome I naturally wanted it to be a part of that.

Well, it can be done:

to build this it took:

  • an access key and some documentation to the database of public transport. VBB, the regional public transport org, gives it out for free if asked nicely. I assume other transport organisations will have something comparable.
  • a daemon running on a computer in the own network which fetches the data from the database, I did that in python
  • mosquitto running on a server in the home network to communicate the json to the smarthome
  • rules and items in openHab2 to receive, parse and format the connection data
  • HTML to format and display the connections into a habPanel page like the image above shows

 Now, the details:

  • The python code to fetch the data from the database:
     
#!/usr/bin/python
# -*- coding: utf-8 -*-

# 18.5.2019, v0.1

import RPi.GPIO as GPIO
import time
import os
from datetime import datetime
from datetime import timedelta
import urllib, json
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import sys
reload(sys)
sys.setdefaultencoding('utf-8')


baseUrl="https://demo.hafas.de/openapi/vbb-proxy/"
suffix="&accessId=get-it-from-vbb&format=json"


toScreen=1

duration=25

selection=[u'900060101N',u'900054105O', u'900054105U',u'900060107N',u'900060153N',u'900061153N',u'900060101S',u'900054105W',u'900060107S',u'900060153S',u'900061153S']


# mqtt
pubTopic="ext/vbb/timetable/state"
bannerTopic="ext/vbb/timetable/banner"
cmdTopic="ext/vbb/timetable/cmd"

mqBroker="192.168.150.1"
banner=""

boost=False
boostMin=5
noBoostMin=4
boostCount=0
slapCount=0
inProgress=False

def reset_halten():
        global halten
        halten={
        u'900060101N':{'lat': 52.47, 'lon': 13.340594, 'dist': 348, 'walk':5.0, 'dir':u'900600003', u'dep':[], u'to':'hin', 'name': u'S Friedenau (Nord)', 'extId': u'900060101'},
        u'900060101S':{'lat': 52.47, 'lon': 13.340594, 'dist': 348, 'walk':5.0, 'dir':u'900063101', u'dep':[], u'to':'her', 'name': u'S Friedenau (Süd)', 'extId': u'900060101'},
        u'900054105O':{'lat': 52.478099, 'lon': 13.342878, 'dist': 599, 'walk':8.0, 'dir':u'900054104', u'lines':'S42,S46,S45', u'dep':[], u'to':'hin', 'name': u'S Innsbrucker Platz (Ost)', 'extId': u'900054105'},
        u'900054105W':{'lat': 52.478099, 'lon': 13.342878, 'dist': 599, 'walk':8.0, 'dir':u'900044202', u'lines':'S41,S46,S45', u'dep':[], u'to':'her', 'name': u'S Innsbrucker Platz (West)', 'extId': u'900054105'},
        u'900054105U':{'lat': 52.478099, 'lon': 13.342878, 'dist': 599, 'walk':8.0, 'dir':u'900055101', u'dep':[], u'to':'hin', 'name': u'U Innsbrucker Platz', 'extId': u'900054105'},
        u'900060107N':{'lat': 52.47525, 'lon': 13.34313, 'dist': 280, 'walk':3.5, 'dir':u'900054105', u'dep':[], u'to':'hin', 'name': u'Cecilieng\xe4rten (Nord)', 'extId': u'900060107'},
        u'900060107S':{'lat': 52.47484, 'lon': 13.34302, 'dist': 230, 'walk':3.1, 'dir':u'900060104', u'dep':[], u'to':'her', 'name': u'Cecilieng\xe4rten (Süd)', 'extId': u'900060107'},
        u'900060153N':{'lat': 52.47086, 'lon': 13.34385, 'dist': 210, 'walk':2.8, 'dir':u'900054105', u'dep':[], u'to':'hin', 'name': u'Rubensstr. (Nord)', 'extId': u'900060153'},
        u'900060153S':{'lat': 52.46971, 'lon': 13.34377, 'dist': 350, 'walk':4.8, 'dir':u'900060104', u'dep':[], u'to':'her', 'name': u'Rubensstr. (Süd)', 'extId': u'900060153'},
        u'900061153N':{'lat': 52.47591, 'lon': 13.34091, 'dist': 550, 'walk':5.7, 'dir':u'900054105', u'dep':[], u'to':'hin', 'name': u'H\xe4hnelstr. (Nord)', 'extId': u'900061153'},
        u'900061153S':{'lat': 52.47531, 'lon': 13.33969, 'dist': 550, 'walk':5.7, 'dir':u'900061105', u'dep':[], u'to':'her', 'name': u'H\xe4hnelstr. (Süd)', 'extId': u'900061153'}
        }

        halten[u'callTime']=datetime.now().strftime('%Y-%m-%d %H:%M:%S')


############### MQTT section ##################

# when connecting to mqtt do this;

def on_connect(client, userdata, flags, rc):
        #print("Connected with result code "+str(rc))
        client.subscribe(cmdTopic)

# when receiving a mqtt message do this;

def on_message(client, userdata, msg):
        global boostCount,boost
        message = str(msg.payload)
        print(msg.topic+" "+message)

        if message == "boost":
                boost=True
                boostCount=0
                checkOnce()

def on_publish(mosq, obj, mid):
        pass
        #print("mid: " + str(mid))


def publishDict(obj):
        json_string = json.dumps(obj)
        client.publish(pubTopic,json_string,1)

def publishBanner(obj):
        json_string = json.dumps(obj)
        client.publish(bannerTopic,json_string,1)

def checkOnce():
        global inProgress
        if inProgress:
                return
        inProgress=True
        getAllTables()
        publishDict(halten)
        inProgress=False

def getAllTables():
        global selection,banners
        reset_halten()
        banners=""
        for halteKey in selection:
                getTableByHaltekey(halteKey)

def doNothing():
        pass

def getTableByHaltekey(halteKey):
        global halten, baseurl,suffix,duration,toScreen,banners

        halte=halten[halteKey]
        walk=halte[u'walk']
        extId=halte[u'extId']
        direction=halte[u'dir'] 
        reqTime=datetime.now() + timedelta(minutes=walk)
        datum = reqTime.strftime('%Y-%m-%d')
        zeit  = reqTime.strftime("%H:%M")
        datetimeFormat = '%Y-%m-%d %H:%M:%S'

        banner = "{} {} m {} min. Weg".format( halte[u'name'],halte[u'dist'],int(round(halte[u'walk'])) )
        #banners += banner + '\r\n'
        if toScreen:
                print "\n"+banner
                #print "{} {} m {} min. Weg".format( halte[u'name'],halte[u'dist'],round(halte[u'walk']) )

        # fuer die Ringbahn werden, wie im Witz, Richtungen nicht unterschieden...
        if u'lines' in halte:
                lines=halte[u'lines']
                uri = baseUrl+"departureBoard?extId={}&date={}&time={}&rtMode=FULL&duration={}&direction={}&lines={}".format(extId,datum,zeit,duration,direction,lines)+suffix
        else:
                uri = baseUrl+"departureBoard?extId={}&date={}&time={}&rtMode=FULL&duration={}&direction={}".format(extId,datum,zeit,duration,direction)+suffix

        # im debug die uri ausgeben
        if toScreen:
                pass
                #print uri

        response = urllib.urlopen(uri)
        dat = json.loads(response.read())

        if u'Departure' in dat:
                data=dat["Departure"]
                if len(data)==0:
                        return
        else:
                return 

        # kann sein, dass rtTime in einzelnen records fehlt, und manche records sind unplausibel
        delRecs=[]
        for recnum in range(len(data)):
                record=data[recnum]
                if u'rtTime' in record:
                        # verspätung als rtTime-time
                        planTime=record[u'date']+' '+record[u'time']
                        realTime=record[u'rtDate']+' '+record[u'rtTime']
                        lateSec=datetime.strptime(realTime, datetimeFormat)-datetime.strptime(planTime, datetimeFormat)
                        late=int(round(lateSec.seconds / 60.0) )
                        record[u'late']=late

                else:
                        # ha! record ohne rtTime, rtDate -> diese aus time, date kopieren
                        record[u'rtDate']=record[u'date']
                        record[u'rtTime']=record[u'time']
                        record[u'late']=0

                # ausgefallene Züge (?) bleiben im Suchergebnis stehen, obwohl ihre Zeit längs um wäre
                leaveTime=record[u'rtDate']+' '+record[u'rtTime']
                diff=datetime.strptime(leaveTime, datetimeFormat)-datetime.now()
                diffMin=int(round((diff.seconds / 60.0) - walk))
                if diffMin>100:
                        print "strange time: {}  rtTime: {} late: {} noch: {}".format(record[u'time'],record[u'rtTime'],record[u'late'],diffMin)
                        delRecs.append(recnum)
                if diffMin<-10:
                        print "spooky  time: {}  rtTime: {} late: {} noch: {}".format(record[u'time'],record[u'rtTime'],record[u'late'],diffMin)
                        delRecs.append(recnum)

                # Busse, die zu früh kommen, haben 1440 min offset
                if record[u'late'] > 1000:
                        record[u'late'] -= 1440

        for recnum in reversed(delRecs):
                del data[recnum]
        delRecs=[]

        dat=sorted(data, key = lambda i: (i['rtDate'], i['rtTime']))


        for pos in [0,1]:
                if pos==1 and len(dat)==1:
                        continue

                par=dat[pos]
                useDate=par["rtDate"]
                useTime=par["rtTime"]

                leaveTime=useDate+' '+useTime
                diff=datetime.strptime(leaveTime, datetimeFormat)-datetime.now()
                diffMin=int(round((diff.seconds / 60.0) - walk))
                # fix the occasional 1437 diffMin (instead of -3)
                if diffMin > 100:
                        diffMin -= 14400

                splitter = useTime.split(':')
                useTime = splitter[0]+':'+splitter[1]

                aline = "   {} {} {} {} noch: {} late: {}".format( par[u'name'].lstrip(),par[u'direction'],useDate,useTime,diffMin,par[u'late'])
                banner = banner + "
" + aline

                if toScreen:
                        print aline

                zug={u'name':par[u'name'].lstrip(),u'direction':par[u'direction'],u'date':useDate,u'time':useTime,u'left':diffMin,u'late':par[u'late'] }
                halte['dep'].append(zug)
                halte['banner']=banner
                #banners += banner +'
'
                banner=""



# mqtt
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.on_publish = on_publish
client.username_pw_set(username="user",password="password")
client.connect(mqBroker, 1883, 90)
time.sleep(4)
client.loop_start()


try:
        # loop
        while 1:
                if boost or (slapCount % noBoostMin == 0):
                        checkOnce()
                        #publishBanner({u'banner':banners})
                        boostCount+=1

                        if boostCount>=boostMin:
                                boost=False
                        print "boost: {} boostCount: {}".format(boost,boostCount)

                slapCount += 1
                time.sleep(60)

finally:
        print 'bye'

  • openHab items to receive or send data to/from mosquitto:
     
String mqVbb "incoming" { mqtt="<[mqttBroker:ext/vbb/timetable/state:state:default]" }  
String mqVbbCmd "outgoing" { mqtt=">[mqttBroker:ext/vbb/timetable/cmd:state:*:default]" } 
  • openHab items to obtain the parsed data; this is trivial and not shown
     
  • an openHab rule to parse and format the connection info into dedicated items (only the first part of it is shown)
     
rule "rule triggered  by mqVbb changed"
when 	
    Item  mqVbb changed 

then
    var String halte=""
    var String station=""
    var String zug1=""
    var String zug2="" 
    var String noch1=""
    var String noch2=""

    var String json = (mqVbb.state as StringType).toString
    
    halte="900060101N" // S Friedenau Nord
    
    station = transform("JSONPATH", "$['"+halte+"'].name", json) + " (" + transform("JSONPATH", "$['"+halte+"'].dist", json) + "m / " + transform("JSONPATH", "$['"+halte+"'].walk", json) + " min) "
    zug1= transform("JSONPATH", "$['"+halte+"'].dep[0].name", json) +" -> " + transform("JSONPATH", "$['"+halte+"'].dep[0].direction", json) +": " +transform("JSONPATH", "$['"+halte+"'].dep[0].time", json)+" ("+ transform("JSONPATH", "$['"+halte+"'].dep[0].late", json)+") "
    zug2= transform("JSONPATH", "$['"+halte+"'].dep[1].name", json) +" -> " + transform("JSONPATH", "$['"+halte+"'].dep[1].direction", json) +": " +transform("JSONPATH", "$['"+halte+"'].dep[1].time", json)+" ("+ transform("JSONPATH", "$['"+halte+"'].dep[1].late", json)+") "
    noch1=transform("JSONPATH", "$['"+halte+"'].dep[0].left", json)
    noch2=transform("JSONPATH", "$['"+halte+"'].dep[1].left", json)

    vbbHalte1.sendCommand(station)
    if (zug1.length()<100){vbbHalte1Zug1.sendCommand(zug1);vbbHalte1Noch1.sendCommand(noch1)}else{vbbHalte1Zug1.sendCommand(" ");vbbHalte1Noch1.sendCommand(" ")}
    if (zug2.length()<100){vbbHalte1Zug2.sendCommand(zug2);vbbHalte1Noch2.sendCommand(noch2)}else{vbbHalte1Zug2.sendCommand(" ");vbbHalte1Noch2.sendCommand(" ")}



    
    halte="900060101S" // S Friedenau Süd
    station = transform("JSONPATH", "$['"+halte+"'].name", json) + " (" + transform("JSONPATH", "$['"+halte+"'].dist", json) + "m / " + transform("JSONPATH", "$['"+halte+"'].walk", json) + " min) "
    zug1= transform("JSONPATH", "$['"+halte+"'].dep[0].name", json) +" -> " + transform("JSONPATH", "$['"+halte+"'].dep[0].direction", json) +": " +transform("JSONPATH", "$['"+halte+"'].dep[0].time", json)+" ("+ transform("JSONPATH", "$['"+halte+"'].dep[0].late", json)+") "
    zug2= transform("JSONPATH", "$['"+halte+"'].dep[1].name", json) +" -> " + transform("JSONPATH", "$['"+halte+"'].dep[1].direction", json) +": " +transform("JSONPATH", "$['"+halte+"'].dep[1].time", json)+" ("+ transform("JSONPATH", "$['"+halte+"'].dep[1].late", json)+") "
    noch1=transform("JSONPATH", "$['"+halte+"'].dep[0].left", json)
    noch2=transform("JSONPATH", "$['"+halte+"'].dep[1].left", json)

    vbbHalte2.sendCommand(station)
    if (zug1.length()<100){vbbHalte2Zug1.sendCommand(zug1);vbbHalte2Noch1.sendCommand(noch1)}else{vbbHalte2Zug1.sendCommand(" ");vbbHalte2Noch1.sendCommand(" ")}
    if (zug2.length()<100){vbbHalte2Zug2.sendCommand(zug2);vbbHalte2Noch2.sendCommand(noch2)}else{vbbHalte2Zug2.sendCommand(" ");vbbHalte2Noch2.sendCommand(" ")}


and finally some HTML to go into the template widget of a HabPanel page:

<div align="left" class "row">

  <div class="col-sm-6"><span style="color: red; font-size: 12pt">{{itemValue('vbbHalte1')}} </span>
    <ul>
      <li>  <span style="color: orange; font-size: 9pt">{{itemValue('vbbHalte1Zug1')}} </span><span style="color: cyan; font-size: 10pt">{{itemValue('vbbHalte1Noch1')}} </span></li>
      <li>  <span style="color: orange; font-size: 9pt">{{itemValue('vbbHalte1Zug2')}} </span><span style="color: cyan; font-size: 10pt">{{itemValue('vbbHalte1Noch2')}} </span></li>
    </ul>  
  </div>

  <div class="col-sm-6"><span style="color: red; font-size: 12pt">{{itemValue('vbbHalte2')}} </span>
    <ul>
      <li>  <span style="color: orange; font-size: 9pt">{{itemValue('vbbHalte2Zug1')}} </span><span style="color: cyan; font-size: 10pt">{{itemValue('vbbHalte2Noch1')}} </span></li>
      <li>  <span style="color: orange; font-size: 9pt">{{itemValue('vbbHalte2Zug2')}} </span><span style="color: cyan; font-size: 10pt">{{itemValue('vbbHalte2Noch2')}} </span></li>
    </ul>  
  </div>

Again this is just the start of it and those lines are repeated for all the stations and their items.

This is all just Alpha and there's room left for improvements, But 'm glad it works!

My little thing uses but a tiny subset of what functionality the api offers

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, and then reactivate them one by one when the system is up and has it's feet on the ground.

It did not work exactly like described there for me, but I still found a solution that has cleaned up the messy startup here.

A small bash script does the renaming. I put it in /etc/openhab2 next to openhab's configuration into a new folder /exec-scripts:

#!/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 start 
#  to rename *.rules away initially 
#
#  synopsis: move_rules_at_start.sh org-extension new-extension (POST)
#  
#  first call is from
#  /etc/systemd/system/openhab2.service.d/override.conf
#
#  called from the running openhab again to rename them back one by one
#
# $3 allows to distinguish between pre and post action 
#
# 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=005_start.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/things/tradfri.things
fi


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 a service file that is not replaced by the next update.

Now don't try to copy the entire .service file, systemd will complain duplicated statements. My override.conf looks like this:

[Service]
ExecStartPre=/etc/openhab2/exec-scripts/move_rules_at_start.sh rules rules_

The third element is a .rules file in openhab2 that triggers the renaming of all the other .rules once the system is running. It is excepted from the renaming and I named it 005_start.rules. This works for me:


var rulesDelayed
var nada

rule "triggered by system start"
when 
    System started
then
    var duration = 1
    rulesDelayed = createTimer(now.plusMinutes(duration), [|
					logInfo("rulesDelayed", "Timer expired and start")
					nada=executeCommandLine("/bin/bash /etc/openhab2/exec-scripts/move_rules_at_start.sh rules_ rules POST ",90000)
                    logInfo("rulesDelayed", "result: "+nada)
					rulesDelayed = null

				])
    logInfo("rulesDelayed", "Timer set on "+duration+" min")
end


The bash script works the list of rules -files in an alphanumerical order so a naming scheme like praefixing all .rules with a three digit number finally allows to control the order of rules loading.

Startup is much faster now, less or no exceptions and a much cleaner log :-) Openhab is a Java application and those exceptions are super-ugly and come with a performance penalty.

You'll notice that the bash script supports an optional 3. parmeter to distinguish between pre- and post-action and that a certain config file is touched after the renaming. touch-ing triggers a reload of that config file and this was a workaround for a bug in the tradfri-addon, the bug may have been addressed by now.

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. 

 

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


raspberry openHab Links

Wednesday, January 24. 2018

PinOut physical/BCM/WiringPi 

 

AdaFruit DHT temp/humid sensor project

Temperatur- und Luftfeuchtigkeitsmessung mit Sensor DHT22/AM2302 und Raspberry Pi

 

Presence detection by mobile
It’s Android so my first approach would be to set up Tasker to turn ON/OFF an OH Item through the OH REST API when Tasker sees the home wifi.

Kalender einbinden:
Binding-docu

Zeiten und Rythmen, Astro und DayTime

Chromecast Audio bespielen:
https://community.openhab.org/t/starting-a-google-play-music-playlist-on-a-chromecast-audio/38619

433 Mhz Funk für FunkSteckDosen, openhab-Einbindung

Openhab docu:

Sitemaps Items Rules  Rules-Tutorial  Xtend-Docu  community.openhab.org/

Editor: VS Code Tool: CronMaker 

 

 

 

 

(Page 1 of 1, totaling 5 entries)