versuche es mal mit sudo davor
Danke, hatte ich auch probiert.
Bin sogar von Stretch auf Jessie zurückgegangen.
ein hcitool lescan findet 15 Devices sofort. Python jedoch hin und wieder max mal eine Adresse mit
>>> import bluetooth
>>> print bluetooth.discover_devices()
[]
[\CODE]
habe auch schon diverse tutorials gelesen, da ich dachte es fehlt vlt. ein Paket..
Danke für das Script,
geht bei mir.
Allerdings bricht das Script ab und zu mit ner Fehlermeldung ab. Konnte den Grund bislang nicht reproduzieren,
Gibts ne Möglichkeit nicht im Pythonscript zu prüfen ob eine bestimmte MAC vorliegt, sondern über den Webhook die sich im Zustand (anwesend oder nicht) geänderte MAC an IPS zurück zumelden. Dann wäre die gute Lösung einfacher anpassbar bzw. mann könnte schneller ein neues Handy einbinden…
hardlog
Hm schlecht, da würde das Script dauerhaft an IPS die Daten senden, das macht nicht wirklich Sinn…
ok,
ich kann (bislang) python nicht programmieren… bzw. verstehe das script nicht ganz.
Ich meinte auch nicht, dass das der pi die Daten pausenlos sendet und IPS die Interpretation macht, sondern, das python schon nur bei neuem hinzukommen bzw. verschwinden von bt-Geräten, die MAC des zuletzt geändeten Gerätes sendet…
Puh sorry das wüsste ich ich auch nicht wie das prüfen könnte…
Hi,
ich habe das gleiche Problem, auch mit dem Gigaset G-Tag, hast du es noch zum Laufen bekommen?
Gruß
Ich bin einen ganz anderen Weg gegangen. Ich nutze nun ein Skript auf einem Rapsberry mit einem selbst erstellten Alpine Linux (arbeitet in Memory und ist safe gegen Stromausfall).
Das Skript stammt aus dieser Quelle:
Presence detection (Bluetooth 4.0 Low energy Beacon) - Domoticz
ich habe die URL angepasst und mir ein Skript in Symcon als Webhook definiert. das Ganze läuft seit Monaten stabil und ergänzt das Geofencing als Fallback. Denn wenn ich unterwegs telefoniere im Auto hat mein Handy kein Update wegen Geofencing versendet.
Hi,
das hört sich super interessant an! Läuft das auch unter Raspbian? Kannst du mal das genaue Script mit den Anpassungen und das IPS Webhook Script posten?
Gruß
OK, kann ich machen. Aber bitte beachten, dass das Skript nicht von mir ist! Auch das Webhook Skript habe ich irgendwoher übernommen… (kann sogar aus dem Thread hier gewesen sein)
Ich hoffe für den Forenbetreiber ist es daher ok. Die Quelle für das Skript siehe oben.
[b]
- Skript für Webhook Processing anlegen:[/b]
Der Bluetooth Dongle wird bei mir nur für den „Checkin“ im Haus verwendet. Wenn Ihr auch das „Checkout“ tracker wollt, müsst ihr hier auf AWAY prüfen.
<?
if (@$_GET !="") $valueJSON=$_GET; // Prüfen ob es per Get gesendet wird....
//IPS_LogMessage("Pi Bluetooth1", print_r($valueJSON, true)); // Debugging !
$name= $valueJSON['name'];
$state= $valueJSON['value'];
IPS_LogMessage("Bluetooth Detection", print_r($name ." " .$state, true)); // Debugging !
if("G-tag" === $name) {
SetValueBoolean(56386, ('HOME'===$state));
if ("HOME" === $state) { // checkin, falls webhook login fehlgeschlagen ist
SetValueBoolean(44616, true);
}
}
?>
2. Webhook anlegen in Symcon:
/hook/bt
Als Skript wählt ihr das Skript aus #1 aus.
3. Raspberry Pi Skript
Ihr benötigt: bluez, py2-bluez, py2-requests. Je nachdem welche Distribution ihr verwendet, kann das Paket evtl. etwas anders heißen. Ich verwende Alpine 3.8.1
Verwendet ihr Raspbian so könnt Ihr im Skript auch die Sudo Aufrufe lassen. Ihr braucht dann nur Eure Tag MAC Adresse (davor ist der Name, in meinem Falle „G-tag“) eingeben (Zeile 55) und in Zeile 38 Eure Webhook URL.
URL_DOMOTICZ = ‚http://symcon.home:3777/hook/bt/blah?name=PARAM_NAME&value=PARAM_CMD‘
das „blah“ ist glaube ich überflüssig. Habe es damals nur Quick & Dirty angepasst um es zum laufen zu bekommen.
Das schöne am Skript ist, dass es auch selber prüft, ob der bluetooth stack läuft mit hcitool bzw. hciconfig.
Achtung: Bei Älteren bluez Versionen gab es ständig Thread Probleme mit dem lescan! Wenn ihr das aktuell Stretch verwendet sollte aber alles io sein.
#!/usr/bin/python
# File : check_beacon_presence.py
# Author: jmleglise
# Date: 10-Nov-2016
# Description : Check the presence of a list of beacon (BlueTooth Low Energy V4.0) and update uservariables in Domoticz accordingly.
# URL : https://github.com/jmleglise/mylittle-domoticz/edit/master/Presence%20detection%20%28beacon%29/check_beacon_presence.py
# Version : 1.0
# Version : 1.1 Log + Mac Adress case insensitive
# Version : 1.2 Fix initial AWAY state
# Version : 1.3 Log + script takes care of hciconfig + Return the RSSI when detected and "AWAY" otherwise
# Version : 1.4 Fix initial HOME state
# Version : 1.5 Split loglevel warning / debug
# Version : 1.6 Add le_handle_connection_complete + Manage Domoticz login
#
# Feature :
# Script takes care of Bluetooth Adapter. Switch it UP RUNNING.
# When the MACADRESS of a list of beacons are detected, update DOMOTICZ uservariable.
# Script operates now in 2 mode. Choose for each beacon witch one you want :
# REPEAT MODE : For beacon in range, update the uservariable every 3 secondes with the RSSI. And "AWAY" otherwise.
# SWITCH_MODE : For beacon in range, update only 1 time the uservariable with "HOME". And "AWAY" otherwise.
# Send "AWAY" when the beacons are not in range.
# The detection is very fast : around 4 secondes. And the absence is verified every 5 seconds by comparing the hour of the last presence with a time out for each beacon.
#
# References :
# https://www.domoticz.com/wiki/Presence_detection_%28Bluetooth_4.0_Low_energy_Beacon%29
# http://https://www.domoticz.com/forum/viewtopic.php?f=28&t=10640
# https://wiki.tizen.org/wiki/Bluetooth
# https://storage.googleapis.com/google-code-archive-source/v2/code.google.com/pybluez/source-archive.zip => pybluez\examples\advanced\inquiry-with-rssi.py
#
# Required in Domoticz : An uservariable of type String for each BLE Tag
#
# Usefull command
# sudo /etc/init.d/check_beacon_presence [stop|start|restart|status]
#
# Configuration :
# Change your IP and Port here :
#URL_DOMOTICZ = 'https://xxxxxx.xxxxxx.org:xxxx/json.htm?type=command¶m=updateuservariable&idx=PARAM_IDX&vname=PARAM_NAME&vtype=2&vvalue=PARAM_CMD'
URL_DOMOTICZ = 'http://symcon.home:3777/hook/bt/blah?name=PARAM_NAME&value=PARAM_CMD'
DOMOTICZ_USER='xxxxxx'
DOMOTICZ_PASS='xxxxxx'
REPEAT_MODE=1
SWITCH_MODE=0
#
# Configure your Beacons in the TAG_DATA table with : [Name,MacAddress,Timeout,0,idx,mode]
# Name : the name of the uservariable used in Domoticz
# macAddress : case insensitive
# Timeout is in secondes the elapsed time without a detetion for switching the beacon AWAY. Ie :if your beacon emits every 3 to 8 seondes, a timeout of 15 secondes seems good.
# 0 : used by the script (will keep the time of the last broadcast)
# idx of the uservariable in Domoticz for this beacon
# mode : SWITCH_MODE = One update per status change / REPEAT_MODE = continuous updating the RSSI every 3 secondes
TAG_DATA = [
["G-tag","11:22:33:44:55:66",15,0,8,SWITCH_MODE]
]
import logging
# choose between DEBUG (log every information) or warning (change of state) or CRITICAL (only error)
#logLevel=logging.DEBUG
logLevel=logging.CRITICAL
#logLevel=logging.WARNING
logOutFilename='/var/log/check_beacon_presence.log' # output LOG : File or console (comment this line to console output)
ABSENCE_FREQUENCY=5 # frequency of the test of absence. in seconde. (without detection, switch "AWAY".
################ Nothing to edit under this line #####################################################################################
import os
import subprocess
import sys
import struct
import bluetooth._bluetooth as bluez
import time
import requests
import signal
import threading
LE_META_EVENT = 0x3e
OGF_LE_CTL=0x08
OCF_LE_SET_SCAN_ENABLE=0x000C
EVT_LE_CONN_COMPLETE=0x01
EVT_LE_ADVERTISING_REPORT=0x02
def print_packet(pkt):
for c in pkt:
sys.stdout.write("%02x " % struct.unpack("B",c)[0])
def packed_bdaddr_to_string(bdaddr_packed):
return ':'.join('%02x'%i for i in struct.unpack("<BBBBBB", bdaddr_packed[::-1]))
def hci_disable_le_scan(sock):
hci_toggle_le_scan(sock, 0x00)
def hci_toggle_le_scan(sock, enable):
cmd_pkt = struct.pack("<BB", enable, 0x00)
bluez.hci_send_cmd(sock, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, cmd_pkt)
def handler(signum = None, frame = None):
time.sleep(1) #here check if process is done
sys.exit(0)
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
signal.signal(sig, handler)
def le_handle_connection_complete(pkt):
status, handle, role, peer_bdaddr_type = struct.unpack("<BHBB", pkt[0:5])
device_address = packed_bdaddr_to_string(pkt[5:11])
interval, latency, supervision_timeout, master_clock_accuracy = struct.unpack("<HHHB", pkt[11:])
#print "le_handle_connection output"
#print "status: 0x%02x
handle: 0x%04x" % (status, handle)
#print "role: 0x%02x" % role
#print "device address: ", device_address
def request_thread(idx,cmd, name):
try:
url = URL_DOMOTICZ
url=url.replace('PARAM_IDX',str(idx))
url=url.replace('PARAM_CMD',str(cmd))
url=url.replace('PARAM_NAME',str(name))
result = requests.get(url,auth=(DOMOTICZ_USER, DOMOTICZ_PASS))
logging.debug(" %s -> %s" % (threading.current_thread(), result))
except requests.ConnectionError, e:
logging.critical(' %s Request Failed %s - %s' % (threading.current_thread(), e, url) )
class CheckAbsenceThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
time.sleep(ABSENCE_FREQUENCY)
for tag in TAG_DATA:
elapsed_time_absence=time.time()-tag[3]
if elapsed_time_absence>=tag[2] : # sleep execute after the first Home check.
logging.warning('Tag %s not seen since %i sec => update absence',tag[0],elapsed_time_absence)
threadReqAway = threading.Thread(target=request_thread,args=(tag[4],"AWAY",tag[0]))
threadReqAway.start()
while True:
time.sleep(ABSENCE_FREQUENCY)
for tag in TAG_DATA:
elapsed_time_absence=time.time()-tag[3]
if elapsed_time_absence>=tag[2] and elapsed_time_absence<(tag[2]+ABSENCE_FREQUENCY) : #update when > timeout ant only 1 time , before the next absence check [>15sec <30sec]
logging.warning('Tag %s not seen since %i sec => update absence',tag[0],elapsed_time_absence)
threadReqAway = threading.Thread(target=request_thread,args=(tag[4],"AWAY",tag[0]))
threadReqAway.start()
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
if globals().has_key('logOutFilename') :
logging.basicConfig(format=FORMAT,filename=logOutFilename,level=logLevel)
else:
logging.basicConfig(format=FORMAT,level=logLevel)
#Reset Bluetooth interface, hci0
os.system("sudo hciconfig hci0 down")
os.system("sudo hciconfig hci0 up")
#Make sure device is up
interface = subprocess.Popen(["sudo hciconfig"], stdout=subprocess.PIPE, shell=True)
(output, err) = interface.communicate()
if "RUNNING" in output: #Check return of hciconfig to make sure it's up
logging.debug('Ok hci0 interface Up n running !')
else:
logging.critical('Error : hci0 interface not Running. Do you have a BLE device connected to hci0 ? Check with hciconfig !')
sys.exit(1)
devId = 0
try:
sock = bluez.hci_open_dev(devId)
logging.debug('Connect to bluetooth device %i',devId)
except:
logging.critical('Unable to connect to bluetooth device...')
sys.exit(1)
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
hci_toggle_le_scan(sock, 0x01)
for tag in TAG_DATA:
tag[3]=time.time()-tag[2] # initiate lastseen of every beacon "timeout" sec ago. = Every beacon will be AWAY. And so, beacons here will update
th=CheckAbsenceThread()
th.daemon=True
th.start()
while True:
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
flt = bluez.hci_filter_new()
bluez.hci_filter_all_events(flt)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )
pkt = sock.recv(255)
ptype, event, plen = struct.unpack("BBB", pkt[:3])
if event == bluez.EVT_INQUIRY_RESULT_WITH_RSSI:
i =0
elif event == bluez.EVT_NUM_COMP_PKTS:
i =0
elif event == bluez.EVT_DISCONN_COMPLETE:
i =0
elif event == LE_META_EVENT:
subevent, = struct.unpack("B", pkt[3])
pkt = pkt[4:]
if subevent == EVT_LE_CONN_COMPLETE:
le_handle_connection_complete(pkt)
elif subevent == EVT_LE_ADVERTISING_REPORT:
num_reports = struct.unpack("B", pkt[0])[0]
report_pkt_offset = 0
for i in range(0, num_reports):
#logging.debug('UDID: ', print_packet(pkt[report_pkt_offset -22: report_pkt_offset - 6]))
#logging.debug('MAJOR: ', print_packet(pkt[report_pkt_offset -6: report_pkt_offset - 4]))
#logging.debug('MINOR: ', print_packet(pkt[report_pkt_offset -4: report_pkt_offset - 2]))
#logging.debug('MAC address: ', packed_bdaddr_to_string(pkt[report_pkt_offset + 3:report_pkt_offset + 9]))
#logging.debug('Unknown:', struct.unpack("b", pkt[report_pkt_offset -2])) # don't know what this byte is. It's NOT TXPower ?
#logging.debug('RSSI: %s', struct.unpack("b", pkt[report_pkt_offset -1])) # Signal strenght !
macAdressSeen=packed_bdaddr_to_string(pkt[report_pkt_offset + 3:report_pkt_offset + 9])
for tag in TAG_DATA:
if macAdressSeen.lower() == tag[1].lower(): # MAC ADDRESS
logging.debug('Tag %s Detected %s - RSSI %s - DATA unknown %s', tag[0], macAdressSeen, struct.unpack("b", pkt[report_pkt_offset -1]),struct.unpack("b", pkt[report_pkt_offset -2])) # Signal strenght + unknown (hope it's battery life).
elapsed_time=time.time()-tag[3] # lastseen
if tag[5]==SWITCH_MODE and elapsed_time>=tag[2] : # Upadate only once : after an absence (>timeout). It's back again
threadReqHome = threading.Thread(target=request_thread,args=(tag[4],"HOME",tag[0])) # IDX, RSSI, name
threadReqHome.start()
logging.warning('Tag %s seen after an absence of %i sec : update presence',tag[0],elapsed_time)
elif tag[5]==REPEAT_MODE and elapsed_time>3 : # in continuous, Every 2 sec
rssi=''.join(c for c in str(struct.unpack("b", pkt[report_pkt_offset -1])) if c in '-0123456789')
threadReqHome = threading.Thread(target=request_thread,args=(tag[4],rssi,tag[0])) # IDX, RSSI, name
threadReqHome.start()
logging.debug('Tag %s is still there with an RSSI of %s : update presence with RSSI',tag[0],rssi)
tag[3]=time.time() # update lastseen
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
Super, das klappt, danke dir!