German Erledigt!!! Großer Dank geht an rwblinn !!! Wer kann gegen Bezahlung helfen?

Wolli013

Well-Known Member
Licensed User
Longtime User
Hallo Freunde,

da ich mich mit Bluetooth nicht so großartig auskenne, wollte ich hier mal nachfragen,
ob mir jemand diesen Code (gegen vereinbarte Bezahlung) in B4A umsetzen kann?
Ich möchte gerne mit B4A die geloggten Daten vom RuuviTag Sensor auslesen.

Die daten werden nach dem Einschalten im Logintervall zu je 5 Min. aufgezeichnet.
Danach sind sie über Bluetooth abrufbar.
Der Code ist Open Source.
Anbei die Links zum Code, wo der Auslesevorgang stattfindet.

https://github.com/ruuvi/com.ruuvi....tion/bluetooth/gatt/NordicGattManager.kt#L165

 
Last edited:

DonManfred

Expert
Licensed User
Longtime User
Das Projekt ist ein Kotlin projekt. Wird relativ schwer mit der Umsetzung.
Besser ist es eine JAVA Version zu finden.

Von was für eine Hardware genau redest Du hier? URLs
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Von dieser Hardware.

App Store:


iOS Code:
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Vielleicht hilft das hier noch dazu.
Ein Code aus dem RuuviTag Forum, wo ein Kollege es wohl mit Python hinbekommen hat.

"""
Verbindung zu RuuviTag über Bluetooth herstellen und Sensorprotokolldaten lesen

# Code-Stack
* python (Version 3.x, getestet mit python 3.10.7)
* bleak (python bluetooth library)
https://bleak.readthedocs.io/en/latest/usage.html

Getestet auf Linux (64-Bit) mit Kernel 5.19.7 und bluez 5.65-3

# Einrichtung
Einige Python-Bibliotheken (bleak) werden benötigt, installieren Sie diese mit Ihrem Paketmanager
ODER
Erstellen Sie eine virtuelle Umgebung:
python -m venv env
Quelle env/bin/activate
pip install bleak

# Ausführen
python ./ruuvitag_read_log_data.py

# Worum geht es hier eigentlich?
Der RuuviTag ist ein drahtloser Sensor für Temperatur, Luftfeuchtigkeit, Luftdruck und Bewegung.
* https://ruuvi.com/ruuvitag/
Er speichert Messdaten (Temperatur, Luftfeuchtigkeit und Luftdruck) im internen Speicher.
Das Standardintervall für die Aufzeichnung beträgt 5 Minuten.
Die Longlife/Longmem-Firmware für den RuuviTag erhöht das Protokollintervall auf 15 Minuten.

Um die Firmware aus dem RuuviTag auszulesen, muss zunächst eine Bluetooth-Verbindung hergestellt werden.
Der RuuviTag stellt den NUS (Nordic UART Service) zur Verfügung, um Daten vom/zum Gerät zu lesen/schreiben.
* https://docs.ruuvi.com/communication/bluetooth-connection/nordic-uart-service-nus
Mit dem Befehl read log data sendet der Tag die geloggten Daten zurück.
* https://docs.ruuvi.com/communication/bluetooth-connection/nordic-uart-service-nus/log-read
Die Daten werden beim Lesen des Logs _nicht_ vom RuuviTag gelöscht. Sie werden automatisch
überschrieben, wenn der Speicherplatz aufgebraucht ist (sollte bei der Standard-Firmware 10 Tage betragen).

"""



## Die Sensormessungen, die vom RuuviTag abgefragt werden können (unten von DATATYPE_LOG verwendet)
TEMPERATURE = 0x30 # \x30 Temperature [0,01°C]
HUMIDITY = 0x31 # \x31 Humidity [0,01%rH]
AIR_PRESSURE = 0x32 # \x32 Air pressure [1Pa]
ALL_SENSORS = 0x3A # \x3A All of the above

## Nordic UART Service UUIDs, ändern Sie diese _nicht_.
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

## Kopfdatentyp für Anzeigendaten, _nicht_ ändern
DATATYPE_ADV = 0x05



### SETTINGS AREA BEGIN ###

## Enter the MAC address of your RuuviTag
DEVICE_MAC = "00:00:00:00:00:00"

## Anzahl der Sekunden zum Abrufen von Protokolldaten
LOG_SECONDS = 7200 # 7200 = last 2 hours

## Wählen Sie die vom RuuviTag zu lesenden Protokolldaten (TEMPERATUR oder FEUCHTIGKEIT oder LUFTDRUCK oder ALLE_SENSOREN)
DATATYPE_LOG = ALL_SENSORS

### SETTINGS AREA END ###



import asyncio
import sys
import calendar
import time
import struct
import bleak
from datetime import datetime, timezone

log_data_end_of_data = False

async def ruuvi_read_log_data():
global log_data_end_of_data

def handle_disconnect(_: bleak.BleakClient):
print("Device was disconnected", flush=True)
# Das Abbrechen aller Aufgaben beendet das Programm effektiv.
for task in asyncio.all_tasks():
task.cancel()

# vom Bluetooth-Client empfangene Daten, angefordert von start_notify()
def handle_rx(sender_handle: int, data: bytearray):
global log_data_end_of_data

if len(data) <= 0:
print(f"no data (size == {len(data)})")
else:

if (data[0] == DATATYPE_LOG):
if len(data) != 11:
print("Header = log data, but wrong data size", flush=True)
else:
# unpack binary data
dat = struct.unpack('>BBBII', data)
if (dat[3] == 0xFFFFFFFF and dat[4] == 0xFFFFFFFF):
print(" Log: end of output received", flush=True)
log_data_end_of_data = True
else:
if (dat[1] == TEMPERATURE):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0:.2f}°C", flush=True)
elif (dat[1] == HUMIDITY):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0}%rH", flush=True)
elif (dat[1] == AIR_PRESSURE):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0}mBar", flush=True)

elif data[0] == DATATYPE_ADV:
if len(data) != 18:
print("Header = advertisement data, but wrong size", flush=True)
else:
dat = struct.unpack('>BhHHhhhHBH', data)

print(f" Advertisement data received: "
f"T={dat[1]/200.0:.2f}°C "
f"H={dat[2]/400.0:.1f}%rH "
f"P={(dat[3] + 50000)/100.0:.2f}mBar "
f"[x={dat[4]:+5d} y={dat[5]:+5d} z={dat[6]:+5d}] "
f"U={((dat[7]/32)+1600)/1000.0:.2f}V "
f"Tx_strength={(dat[7]%32)*2 - 40:+d}dBm "
f"movement_counter={dat[8]:3d} "
f"sequence_counter={dat[9]:5d}", flush=True)

else:
print(f"unknown first byte: {hex(data[0])}")

# Dieser Code wird beim Aufruf von ruuvi_read_log_data() ausgeführt
print(f"Searching for RuuviTag {DEVICE_MAC}", flush=True)

async with bleak.BleakClient(DEVICE_MAC, disconnected_callback=handle_disconnect, timeout=30) as client:

print(f"Connected to Ruuvi, starting notify on UART channel", flush=True)
await client.start_notify(UART_TX_CHAR_UUID, handle_rx)

# Anforderungsdatenkopf erstellen
data_tx = b''
data_tx += bytes([DATATYPE_LOG])
data_tx += bytes([DATATYPE_LOG])
data_tx += b'\x11'
# timestampts
timenow = int(time.time()) # aktuelle Systemzeit ermitteln
timeprv = timenow - LOG_SECONDS
data_tx += struct.pack('>I', timenow)
data_tx += struct.pack('>I', timeprv)

print(f"Requesting log data for the last {LOG_SECONDS} seconds")
await client.write_gatt_char(UART_RX_CHAR_UUID, data_tx)

# wait for data
while True:
if log_data_end_of_data:
print(f"All log data received, closing connection")
client.close()
break
await asyncio.sleep(1)


if __name__ == "__main__":
try:
asyncio.run(ruuvi_read_log_data())
except asyncio.CancelledError:
# Aufgabe wird beim Trennen der Verbindung abgebrochen, daher ignorieren wir diesen Fehler
pass
except bleak.exc.BleakDBusError as error:
## bleak.exc.BleakDBusError: [org.bluez.Error.NotReady] Resource Not Ready
## bluetoothctl power on
## bleak.exc.BleakDBusError: [org.freedesktop.systemd1.NoSuchUnit] Unit dbus-org.bluez.service not found.
## systemctl start bluetooth.service
print("bleak.exc.BleakDBusError")
print(error)
except bleak.exc.BleakError as error:
## bleak.exc.BleakError: Not connected
print("bleak.exc.BleakError")
print(error)
except:
print("unknown exception")
raise # Wiedererhöhung der Ausnahme für den Benutzer zur Fehlersuche
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Hallo, --- VERSUCH ---

vielleicht hilft ein B4A testprojekt welches daten, via BLE2, von einem Xiaomi Mi Temperature and Humidity Monitor 2, Model LYWSD03MMC, empfängt.
Dieses B4A testprojekt habe ich von einem B4R library rmilywsd03mmc konvertiert.

Ändere im projekt (Python code info als basis verwendet):
B4X:
'MAC address Ruuivi sensor
Private BLE_MAC_ADDRESS As String = "NN:NN:NN:NN:NN:NN"
'UUIDs (lowercase)
'UUID remote service to connect (UART_SERVICE_UUID)
Private SERVICE_UUID As String = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
'UUID for characteristic of the remote service holding the advertised data (UART_TX_CHAR_UUID).
Private CHAR_UUID As String = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

Jetzt der spannende teil, d.h. im
B4X:
Sub BLEManager_DataAvailable
versuchen den Python code umzusetzen vom
Python:
# vom Bluetooth-Client empfangene Daten, angefordert von start_notify()
def handle_rx(sender_handle: int, data: bytearray):
Als test erstmal versuchen byte 0 auf DATATYPE_ADV zu prüfen und dann restliche bytes zuordnen.
Nur als scribble...
B4X:
Dim b() As Byte = Characteristics.Get(BLECharId)
if b(0) == 0x05 'DATATYPE_ADV
   if b.Length != 18 then error und return ' print("Header = advertisement data, but wrong size", flush=True)
  Log('Advertisement data received. Converting...')
  dim b1 as int = b(1)
  dim t as double = b1 / 200.0 '°C
  dim b2 as int = b(2)
  dim h as double = b2 / 400.0 '%RH
  dim b3 as int = b(3)
  dim p as double = b3 + 50000/ 100.0 'mBar
etc...
und loggen ...

Bin gespannt.

ps: bitte nicht auf grammatik achten, da deutsch nicht meine muttersprache ist.
 

Attachments

  • mitemphum.zip
    12.6 KB · Views: 170
Last edited:

Wolli013

Well-Known Member
Licensed User
Longtime User
Erstmal vielen Dank.

Das kommt als Fehlermeldung.

BLEManager_DeviceFound: Connecting to Ruuvi 35DB, C4:33:CF:A7:35:DB

Discovering services.
Connected: [00001800-0000-1000-8000-00805f9b34fb, 00001801-0000-1000-8000-00805f9b34fb, 0000180a-0000-1000-8000-00805f9b34fb, 6e400001-b5a3-f393-e0a9-e50e24dcca9e]
(ArrayList) [00001800-0000-1000-8000-00805f9b34fb, 00001801-0000-1000-8000-00805f9b34fb, 0000180a-0000-1000-8000-00805f9b34fb, 6e400001-b5a3-f393-e0a9-e50e24dcca9e]

b4xmainpage_blemanager_connected (java line: 86)
java.lang.RuntimeException: Service not found
at anywheresoftware.b4a.objects.BleManager2.getService(BleManager2.java:418)
at anywheresoftware.b4a.objects.BleManager2.setNotify(BleManager2.java:335)
at anywheresoftware.b4a.objects.BleManager2.SetNotify(BleManager2.java:332)
at de.rwblinn.mitemphum.b4xmainpage._blemanager_connected(b4xmainpage.java:86)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:213)
at anywheresoftware.b4a.BA$2.run(BA.java:387)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Setze die UUIDs service und characteristics in kleinbuchstaben.
Die UUIDs sind "casesensitive". Die discovering services rückgabe war in kleinbuchstaben:
B4X:
Connected: [00001800-0000-1000-8000-00805f9b34fb, 00001801-0000-1000-8000-00805f9b34fb, 0000180a-0000-1000-8000-00805f9b34fb, 6e400001-b5a3-f393-e0a9-e50e24dcca9e]
 
Last edited:

Wolli013

Well-Known Member
Licensed User
Longtime User
Habe ich gemacht, dann kommt diese Fehlermeldung.

BLEManager_DeviceFound: Connecting to Ruuvi 35DB, C4:33:CF:A7:35:DB
Discovering services.

Connected: [00001800-0000-1000-8000-00805f9b34fb, 00001801-0000-1000-8000-00805f9b34fb, 0000180a-0000-1000-8000-00805f9b34fb, 6e400001-b5a3-f393-e0a9-e50e24dcca9e]
(ArrayList) [00001800-0000-1000-8000-00805f9b34fb, 00001801-0000-1000-8000-00805f9b34fb, 0000180a-0000-1000-8000-00805f9b34fb, 6e400001-b5a3-f393-e0a9-e50e24dcca9e]

b4xmainpage_blemanager_connected (java line: 86)
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattDescriptor.setValue(byte[])' on a null object reference
at anywheresoftware.b4a.objects.BleManager2.setNotify(BleManager2.java:343)
at anywheresoftware.b4a.objects.BleManager2.SetNotify(BleManager2.java:332)
at de.rwblinn.mitemphum.b4xmainpage._blemanager_connected(b4xmainpage.java:86)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:213)
at anywheresoftware.b4a.BA$2.run(BA.java:387)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
 

Wolli013

Well-Known Member
Licensed User
Longtime User
So jetzt kommen Daten, musste das ändern.
'UUID for characteristic of the remote service holding the advertised data (UART_RX_CHAR_UUID).
Private CHAR_UUID As String = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Das ist jetzt die Sub

Private Sub BLEManager_DataAvailable (SrvId As String, Characteristics As Map)

Log($"BLEManager_DataAvailable: service=${SrvId}, characteristics=${Characteristics}"$)

Dim b() As Byte = Characteristics.Get(BLECharId)
Dim bc As ByteConverter
Log(bc.HexFromBytes(b))

If b(0) = 0x05 Then 'DATATYPE_ADV
'If b.Length <> 18 Then Return
Log("Advertisement data received Converting")

Dim b1 As Int = b(1)
Dim t As Double = b1 / 200.0 '°C
Dim b2 As Int = b(2)
Dim h As Double = b2 / 400.0 '%RH
Dim b3 As Int = b(3)
Dim p As Double = b3 + 50000/ 100.0 'mBar

Log(t)
Log(h)
Log(p)

End If

End Sub


Und das sind die Ergebnisse

BLEManager_DataAvailable: service=6e400001-b5a3-f393-e0a9-e50e24dcca9e, characteristics={6e400003-b5a3-f393-e0a9-e50e24dcca9e=[B@6b95b45}
050E8A6B66C097FFC003E80018A8D66FCBB1
Advertisement data received Converting
0.07
-0.295
607
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Und das sind die Ergebnisse
BLEManager_DataAvailable: service=6e400001-b5a3-f393-e0a9-e50e24dcca9e, characteristics={6e400003-b5a3-f393-e0a9-e50e24dcca9e=[B@6b95b45}
050E8A6B66C097FFC003E80018A8D66FCBB1
Advertisement data received Converting
0.07
-0.295
607
Ich habe die protokoll information gelesen und in ein testsub mit den daten (050E...) umgesetzt für dataformat 05.
Ergebnis: t=19 °C, h=97 %RH, p=99,155 Pa

Versuche mal die sub zu verwenden mit den ByteConverter:

B4X:
Private Sub BLEManager_DataAvailable (SrvId As String, Characteristics As Map)
    Log($"BLEManager_DataAvailable: service=${SrvId}, characteristics=${Characteristics}"$)
    ConvertPayload(bc.HexFromBytes(Characteristics.Get(BLECharId)))
End Sub

dataformat_05:
Offset | Allowed values | Description
-------|:--------------:|-----------
0 | `5` | Data format (8bit)
1-2 | `-32767 ... 32767` |Temperature in 0.005 degrees
3-4 | `0 ... 40 000` | Humidity (16bit unsigned) in 0.0025% (0-163.83% range, though realistically 0-100%)
5-6 | `0 ... 65534` | Pressure (16bit unsigned) in 1 Pa units, with offset of -50 000 Pa
7-8 | `-32767 ... 32767` | Acceleration-X (Most Significant Byte first)
9-10 | `-32767 ... 32767` | Acceleration-Y (Most Significant Byte first)
11-12 | `-32767 ... 32767` | Acceleration-Z (Most Significant Byte first)
13-14 | `0 ... 2046`, `0 ... 30` | Power info (11+5bit unsigned), first 11 bits Is the battery voltage above 1.6V, in millivolts (1.6V To 3.646V range). Last 5 bits unsigned are the TX Power above -40dBm, in 2dBm steps. (-40dBm To +20dBm range)
15 | `0 ... 254`| Movement counter (8 Bit unsigned), incremented by motion detection interrupts from accelerometer
16-17 | `0 ... 65534`| Measurement sequence number (16 Bit unsigned), each time a measurement Is taken, this Is incremented by one, used For measurement de-duplication. Depending on the transmit interval, multiple packets with the same measurements can be sent, And there may be measurements that never were sent.
18-23 | `Any valid mac` | 48bit MAC address.

B4X:
'Testdata:
'ConvertPayload("05 0E 8A 6B 66 C0 97 FF C0 03 E8 00 18 A8 D6 6F CB B1")
'Example Output:
'050E8A6B66C097FFC003E80018A8D66FCBB1
'data format: 5
'hex=0E8A, t=19 °C
'hex=6B66, h=69 %RH
'hex=C097, p=99,303 Pa
Sub ConvertPayload(payload As String)
    Dim bc As ByteConverter
    payload = payload.Replace(" ", "")
    Dim b() As Byte = bc.HexToBytes(payload)
    Dim hex As String

    'Check the data format = Byte 0 = value must be 5 (dataformat_05)
    Dim hex As String = payload.SubString2(1,2)
    Log($"data format: ${hex}"$)

    hex = payload.SubString2(2, 6)
'    Temperature Degree C = Bytes 1 and 2
    Dim temperature As Double = Bit.ParseInt(hex, 16) * 0.005
    Log($"hex=${hex}, t=${NumberFormat(temperature,0,0)} °C"$)

'    Humidity %RH = Bytes 3 and 4
    hex = payload.SubString2(6, 10)
    Dim humidity As Double = Bit.ParseInt(hex, 16) * 0.0025
    Log($"hex=${hex}, h=${NumberFormat(humidity,0,0)} %RH"$)

'    Pressure Pa - Bytes 5 and 6
    hex = payload.SubString2(10, 14)
    Dim pressure As Double = Bit.ParseInt(hex, 16) + 50000
    Log($"hex=${hex}, p=${NumberFormat(pressure,0,1)} Pa"$)
  
'    Log($"t=${temperature}, h=${humidity}, p=${pressure}"$)
[CODE=b4x]
 

Wolli013

Well-Known Member
Licensed User
Longtime User
So habe ich gemacht.
es kommen jetzt Daten die Ok aussehen, aber nicht aktuell sind.
Sollen das Daten aus der Log Historie sein oder aktuelle?
Wenn es Aktuelle sein sollen, damm passen sie nicht.
Hier die gelogten:

hex=0E9C, t=19 °C
hex=03EFF, h=40 %RH
hex=C803, p=101,203 Pa

und hier die wirklich aktuellen:
18,7 °C
70,11 % RH
99212 Pa
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Die Temperatur scheint zu passen.
Luftfeuchte liegt teilweise um 30% daneben und der Luftdruck schwank auch zu stark.

Aber es sind wohl die aktuellen Daten, wie bekomme ich es hin die Log Historie auszulesen?
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Für mich ist diesen sensor auch neuland. ohne device schwierig zu testen.
Habe noch folgende informationen gefunden:
https://github.com/ruuvi/ruuvi-sensor-protocols/blob/master/gatt_formats.md mit youtube .

Sieht so aus das auch noch ein setnotify auf 6e400002-b5a3-f393-e0a9-e50e24dcca9e nötig ist = ist auch im Python code zu finden.
Wenn zwei characteristiken verwendet werden, dann in BLEManager_DataAvialable prüfen und umsetzen. Für logdata ist eine andere umsetzung nötig (siehe Python code)
Scribble - suche stellen in code und ersetze:
B4X:
Private BLEServiceId, BLERXCharId, BLETXCharId As String
Private RX_CHAR_UUID As String = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
Private TX_CHAR_UUID As String = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

BLEServiceId    = SERVICE_UUID 
BLERXCharId        = RX_CHAR_UUID     
BLETXCharId        = TX_CHAR_UUID     

BLEManager.SetNotify(BLEServiceId, BLERXCharId, True)
BLEManager.SetNotify(BLEServiceId, BLETXCharId, True)

BLEManager_DataAvailable (SrvId As String, Characteristics As Map)
    Log($"BLEManager_DataAvailable: service=${SrvId}, characteristics=${Characteristics}"$)
  
    'Advertisement data received
    If (Characteristics.ContainsKey(BLETXCharId)) Then
        Dim b() As Byte = Characteristics.Get(BLETXCharId)
        ...
    End If
  
    'Log data
    If (Characteristics.ContainsKey(BLERXCharId)) Then
        Dim b() As Byte = Characteristics.Get(BLERXCharId)

        ...
    End If
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Also du hast ja dein Code den du vorher gepostet hast noch abgeändert und damit geht es 100%
Es mal vielen dank dafür.
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Super. Kannst wenn fertig den code mal posten --- vielleicht kaufe ich auch so ein sensor ...
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Schick mir per PN deine Adresse und ich schenke dir einen Sensor für deine Mithilfe.
Das Historie Loggen habe ich zwar noch nicht hinbekommen, aber vielleicht schaffst du es ja wenn due ein Sensor hast.
 
Top