keptenkurk/BS440

Support for BS410

Closed this issue · 78 comments

I recently bought a BS410, only to later discover there is a community for the BS440. I got through the steps hoping to succeed, but got stuk with this error message:

Mon, 26 Jun 2017 20:27:07 INFO     scan Discovered F4:B8:5E:C7:3A:7E (0203B F4B85EC73A7E)
Mon, 26 Jun 2017 20:27:07 INFO     scan Discovered 9C:20:7B:BB:89:32 (None)
Mon, 26 Jun 2017 20:27:07 INFO     scan Found 2 BLE devices
Mon, 26 Jun 2017 20:27:07 INFO     connect Connecting to F4:B8:5E:C7:3A:7E with timeout=5
Mon, 26 Jun 2017 20:27:12 ERROR    connect Timed out connecting to F4:B8:5E:C7:3A:7E after 5 seconds.

It did discover the scale at some point, however, so I'm hopeful there is something I can do :-)
I'm tried using the bluez package through pi's apt-get (version 5.23-2+rpi2) and through manual compilation (version 5.45). The latter one does no longer have hciconfig nor gatttool apparently, so the python code fails ...

Other stuff I tried:

With the scale on (and emitting):

# gatttool -t random -b F4:B8:5E:C7:3A:7E -I
[F4:B8:5E:C7:3A:7E][LE]> connect
Attempting to connect to F4:B8:5E:C7:3A:7E
[F4:B8:5E:C7:3A:7E][LE]>

(Never says connected?)

When it turns dark:

[F4:B8:5E:C7:3A:7E][LE]> connect
Error: connect error: Connection refused (111)

Got a bit further (using bluez-5.23-2+rpi2 from the package maanger):

# gatttool -I
[                 ][LE]> connect F4:B8:5E:C7:3A:7E
Attempting to connect to F4:B8:5E:C7:3A:7E
Connection successful
[F4:B8:5E:C7:3A:7E][LE]> primary
attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x000f uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0010, end grp handle: 0xffff uuid: 000078b2-0000-1000-8000-00805f9b34fb

@jovandeginste Hey never tried a BS410 but since the BS430,BS440 and the BS444 funktion the same way i would suppose this one should too.
From your last post it looked like you have a connection so what is the problem, don't you get the measurements?

@MarcHeinrich correct, I got some responses, but I can't get the right order fast enough; it's a PITA that the scale shuts down after about a minute :-) Also, there are some unclear things in the blog posts, especially what code should be written to which handle (and why specifically that handle). I tried interpreting the code, but I get stuck there too.

@jovandeginste i see i had this problem too.
Since you can connect the steps to get the measurements after that should be(if it's the same as the other scales):

  1. enable the notifications for the 3 data characteristics (UUIDs: 8A21, 8A22, 8A82) they all belong to the same service UUID 78B2, these will notify you the measurement data
  2. read characteristic UUID 8A82
  3. Write to characteristic UUID 8A81 the message: 0x23, 0x02, unix timestamp (timestamp in seconds and you need to translate it to a byte array to transmit it, i think a simple cast should do)

I am not sure right know if you need to send the message with the timestamp over and over or if one time is enough

You ask why to these specific, don't know how to anwser this properly but UUID 8A81 is something like a contol point for the scale all commands go there and the scale gets and executes them.
The same goes for UUID 8A81 thats the point where the scale gives all the featback (excluding the measurements)

That should be the steps you need to take, can you tell me if this works for you?

@MarcHeinrich could you give me a gatttool client example for one of these steps? Or more specifically, how I write to UUID 8A21, how do I find the handle (or is this not relevant?)

@jovandeginste I can try i am not that good with gatttool but i will try my best :)
to get the available handles this command should work: char-desc
to read you can use: char-read-hnd -handle-
to write to one use: char-write-req -handle- -data-

The write is also used to enable notifications for a characteristic simply write ti the characteristic with the data 0100 (thats the command to enable notifications) but if i remember correctly the scale uses indications which require the data 0200
As a note i am not sure if the command reads it with big or little endian, to make it clear to enable indication a 2 needs to be send if it reads the last byte before the fist one you need to write 0002

@MarcHeinrich thank you; what exactly is the handle? In my output of "primary" I see two handles for every uuid, and I often see multiple handles; I also saw in the blog posts something about "1 less than the handle"...

@jovandeginste can you post the output you get from the command char-desc i don't have it here

@MarcHeinrich I will try to do that tonight (I don't have the scale with me at work ;-))

@jovandeginste I looked at the post and from my understanding what you need is the following:

  • For UUID 8A82 the handle is 0x26
  • For UUID 8A21 the handle is 0x1c
  • For UUID 8A22 the handle is 0x1f

The thing with the "1 less than the handle" is for the response values for example the UUID 8A82 would respond with the handle 0x25 instead of 0x26

To the question what the handle is the handle is something like a shortcut for the UUID and it indicates what type of message follows
In this case a 0x23 from my write command above is the handle for this characteristic followed by the command for a time sync so you would do something like this:
char-write-req 0x23 (0x02 unix timestamp)

DjZU commented

@jovandeginste First try getting the scale closer to your bluetooth receiver.
You can also increase the time out from 5 seconds to something greater (I use 8 seconds). This change is to be made in the main BS440.py at line 68. On the same line, you will notice the 'random' which it seems you had to remove from the gatttool command line to get connected. If I remember well, you can try 'public' instead of 'random'.
Then the output of 'char-desc' gatttool command will help see if it's the same for your scale than ours.

These scales use BLE (Bluetooth Low Energy) for their battery to last longer. The drawback is that the distance between the scale and the receiver should be quite short or the connection will fail most of the time. BTW, the scale stores up to 30 measurements and send them all at every connection so you don't lose your measurement if the connection fails: it will get transmitted the next day you make a new measurement and have successful connection.

FYI, in your output of 'primary' you actually see a range of handles not only two.
FYI, this page describes the standards services. You will recognize 0x1800 as Generic Access and 0x1801 as Generic Attribute but the others are specific to the manufacturer.

@DjZU About the handles it should be one handle for each attribute in the characteristic/service if i understand this correct right?

The saved measurements aren't transmitted automatically they will be send after receiving a Timestamp at least that is what i experienced (this projekt sends such a command on a connect).

@DjZU @MarcHeinrich I got a lot more now by simply increasing the timeout and changing random to public:

Tue, 27 Jun 2017 20:32:38 INFO     connect Connecting to F4:B8:5E:C7:3A:7E with timeout=8
Tue, 27 Jun 2017 20:32:39 DEBUG    get_handle Looking up handle for characteristic 00008a22-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a00-0000-1000-8000-00805f9b34fb, value handle: 0x3
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a01-0000-1000-8000-00805f9b34fb, value handle: 0x5
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a02-0000-1000-8000-00805f9b34fb, value handle: 0x7
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a03-0000-1000-8000-00805f9b34fb, value handle: 0x9
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a04-0000-1000-8000-00805f9b34fb, value handle: 0xb
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00002a05-0000-1000-8000-00805f9b34fb, value handle: 0xe
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00008a20-0000-1000-8000-00805f9b34fb, value handle: 0x12
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00008a21-0000-1000-8000-00805f9b34fb, value handle: 0x14
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00008a22-0000-1000-8000-00805f9b34fb, value handle: 0x17
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00008a81-0000-1000-8000-00805f9b34fb, value handle: 0x1a
Tue, 27 Jun 2017 20:32:39 DEBUG    _save_charecteristic_callback Found characteristic 00008a82-0000-1000-8000-00805f9b34fb, value handle: 0x1c
Tue, 27 Jun 2017 20:32:41 DEBUG    get_handle Found <Characteristic uuid=00008a22-0000-1000-8000-00805f9b34fb handle=23>
Tue, 27 Jun 2017 20:32:41 DEBUG    char_write_handle Sending cmd=char-write-cmd 0x18 0200
Tue, 27 Jun 2017 20:32:41 INFO     char_write_handle Sent cmd=char-write-cmd 0x18 0200
Tue, 27 Jun 2017 20:32:41 INFO     subscribe Subscribed to uuid=00008a22-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:41 DEBUG    get_handle Looking up handle for characteristic 00008a21-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:41 DEBUG    get_handle Found <Characteristic uuid=00008a21-0000-1000-8000-00805f9b34fb handle=20>
Tue, 27 Jun 2017 20:32:41 DEBUG    char_write_handle Sending cmd=char-write-cmd 0x15 0200
Tue, 27 Jun 2017 20:32:41 INFO     char_write_handle Sent cmd=char-write-cmd 0x15 0200
Tue, 27 Jun 2017 20:32:41 INFO     subscribe Subscribed to uuid=00008a21-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:41 DEBUG    get_handle Looking up handle for characteristic 00008a82-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:41 DEBUG    get_handle Found <Characteristic uuid=00008a82-0000-1000-8000-00805f9b34fb handle=28>
Tue, 27 Jun 2017 20:32:41 DEBUG    char_write_handle Sending cmd=char-write-cmd 0x1d 0200
Tue, 27 Jun 2017 20:32:41 INFO     char_write_handle Sent cmd=char-write-cmd 0x1d 0200
Tue, 27 Jun 2017 20:32:41 INFO     subscribe Subscribed to uuid=00008a82-0000-1000-8000-00805f9b34fb
Tue, 27 Jun 2017 20:32:41 DEBUG    char_write_handle Sending cmd=char-write-req 0x23 02c9a45259
Tue, 27 Jun 2017 20:32:41 INFO     receive_notification Received notification on handle=0x1c, value=0x845302800121b2e0000000000000000000000000
Tue, 27 Jun 2017 20:32:41 DEBUG    processIndication Unhandled Indication encountered
Tue, 27 Jun 2017 20:32:42 ERROR    char_write_handle No response received
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/gatttool.py", line 500, in char_write_handle
    self.sendline(cmd)
  File "/usr/lib/python2.7/contextlib.py", line 24, in __exit__
    self.gen.next()
  File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/gatttool.py", line 180, in event
    self.wait(event, timeout)
  File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/gatttool.py", line 154, in wait
    raise NotificationTimeout()
NotificationTimeout: None
Tue, 27 Jun 2017 20:32:42 INFO     <module> Waiting for notifications for another 30 seconds

I will try to catch the char-desc.

Is any of you available via instant messaging or similar tonight (GMT+2)?

This is the full output from char-desc:

[F4:B8:5E:C7:3A:7E][LE]> connect
Attempting to connect to F4:B8:5E:C7:3A:7E
Connection successful
[F4:B8:5E:C7:3A:7E][LE]> char-desc
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0007, uuid: 00002a02-0000-1000-8000-00805f9b34fb
handle: 0x0008, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0009, uuid: 00002a03-0000-1000-8000-00805f9b34fb
handle: 0x000a, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000b, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x000c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x000d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000e, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000f, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0010, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0011, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0012, uuid: 00008a20-0000-1000-8000-00805f9b34fb
handle: 0x0013, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0014, uuid: 00008a21-0000-1000-8000-00805f9b34fb
handle: 0x0015, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0016, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0017, uuid: 00008a22-0000-1000-8000-00805f9b34fb
handle: 0x0018, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001a, uuid: 00008a81-0000-1000-8000-00805f9b34fb
handle: 0x001b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001c, uuid: 00008a82-0000-1000-8000-00805f9b34fb
handle: 0x001d, uuid: 00002902-0000-1000-8000-00805f9b34fb

Made some progress (by trial and error - this is still so much mystery to me!)

--- a/BS440.py
+++ b/BS440.py
@@ -22,7 +22,7 @@ def processIndication(handle, values):
     handle: byte
     value: bytearray
     '''
-    if handle == 0x25:
+    if handle == 0x25 or handle == 0x1c:
         result = decodePerson(handle, values)
         if result not in persondata:
             log.info(str(result))
@@ -44,7 +44,7 @@ def processIndication(handle, values):
         else:
             log.info('Duplicate bodydata record')
     else:
-        log.debug('Unhandled Indication encountered')
+        log.debug('Unhandled Indication encountered: %s' % str(handle))


 def wait_for_device(devname):
@@ -65,7 +65,7 @@ def connect_device(address):
     device = None
     while not device_connected and tries > 0:
         try:
-            device = adapter.connect(address, 5, pygatt.BLEAddressType.random)
+            device = adapter.connect(address, 10, pygatt.BLEAddressType.public)
             device_connected = True
         except pygatt.exceptions.NotConnectedError:
             tries -= 1

Now gives as (partial) output:

Tue, 27 Jun 2017 21:19:20 INFO     receive_notification Received notification on handle=0x1c, value=0x845302800121b2e0000000000000000000000000
Tue, 27 Jun 2017 21:19:20 INFO     processIndication {'gender': 'male', 'age': 33, 'person': 2, 'valid': False, 'activity': 'normal', 'size': 178}

Which happens to be correct :)

Now the other ones...?

@MarcHeinrich @DjZU Tried a lot, only thing left would be the 0x23 handle; no idea how this was determined (can't find much in the blog either). I could try brute forcing this. Any clue would be appreciated :-D

Ok got more now by simple brute forcing. Trying to pinpoint the correct value first.

The correct value for BS410 appears to be 0x1a. I commited my changes in my branch at https://github.com/jovandeginste/BS440/tree/BS410

Now all that remains is finding a way to parameterize these settings according to the scale type. I don't know python, so would be very happy if someone else wanted to do this.

Unless there is no demand for this, and a hard fork is the better way here (I personally don't think so)?

@jovandeginste Sry i had no time last evening
If it's working thats good to hear

What do you mean by 0x1a appears to be the correct value for which characteristic or message or is it used as a substitute for the 0x23 handle?
Do i understand this correct that the same steps used for the BS440 also apply to the BS410?

@MarcHeinrich you understand correctly; the changes are visible in the last commit in my fork (which I linked higher up)

DjZU commented

@jovandeginste Glad to hear you finally got it working! Welcome! Would you like to describe the steps for getting the correct handles, so that in the future, anyone with another scale type will be able to do what you've done?

@MarcHeinrich @keptenkurk All the handles are different, more the address type has to be 'public' instead of 'random' as you can see in his fork there. I believe the scale type should be stored in the BS440.ini, then BS440.py will read that and configure the correct handles and address type. BTW, the 'time out' would be fine in the BS440.ini as well.

@DjZU Mostly thanks to your hint about timeout and public, I knew that something was possible. Since I have no experience with Python, my path was probably a lot harder than should have been.

Anyway, to find the trigger-handle (0x23 here, 0x1a for BS410) I did a brute force. I changed this code:

device.char_write_handle(0x1a, timestamp,
    wait_for_response=True)

to (from memory, it may have been slightly different yesterday night):

for n in xrange(0x01, 0x22):
  device.char_write_handle(n, timestamp,
      wait_for_response=False)

When this triggered a response, I changed the range (divide and conquer :-)) until I had narrowed it down to a single value. Had this whole range not triggered a response, I would have tried with a higher range (eg. 0x24 to 0x50), but I probably would have quickly given up :-)

(As an aside: apparently the wait_for_response can be False)

Then I started to see response handle + values similar to the ones in the blog, only with different handles than the code expected. I could quickly correlate the new handles to the ones in the code, and I was lucky enough to see that the value decoder just worked for me too.

@MarcHeinrich @keptenkurk @DjZU I agree that a mapping of model (from the ini) to a bunch of handles would be most convenient. I would also suggest a separate section in the ini where you configure the mapping of the scale names to those handles (as opposed to a hardcoded Python dictionary), so that adding new ones would be trivially easy (with or without any Python knowledge). Alternative to a section in the current root ini, a new ini per scale model would also be an option (so that you have models/BS440.ini, models/BS410.ini, etc, and the code reads the correct file according to the value of model in the root ini)

@DjZU as another aside, yesterday night I had the scale (and my laptop) about 3 meters from the pi with the bluetooth dongle; before I was mostly at about 6 meters (with some furniture in between). Not sure if that really matters, because I did try closer by back then with no result. I can sync the scale with my phone from across the room, so I don't think distance is that limited.

@jovandeginste That distance should be no problem if i remember correctly for Bluetooth LE the distance was about 10m (at least that is what they recommend for a good connection), obstacles of any kind reduce this distance obviously.

@MarcHeinrich Yes I put the scale back upstairs in the bedroom while the Pi with the BT dongle is downstairs in the living room, and now I no longer see the scale. I could try with different locations, but I probably will just by a Pi Zero W for this specifically. Anyone running this code on such (or similar/smaller) devices?

DjZU commented

I upvote for the models/BS410.ini, etc structure since @keptenkurk wanted no more messing up in the main .ini.
I run this code on an Intel NUC but I did try with a RPI3 successfully. Don't know about the Pi Zero W though.

I'm running this on a Pi model B, works perfectly there (but the location is not perfect)

Great thread. Thanks @jovandeginste for your perseverence and @MarcHeinrich and @DjZU for helping you out. Debugging this is indeed a real pain. I was abroad on training, sorry i couldn't bump in earlier. I would love to add support for the BS410, it will help others.
So now i'll first read back and try to comprehend what exactly differs from the BS440. Probably need your help somewhere :-)

@keptenkurk no problem, I don't think you will need my help - most stuff is in this thread or in my fork/branch. Or remains to be implemented.

I will gladly try your solution when you have something ready!

Ok, I guess I see what's going on. It seems the differences boil down to:

  • increasing the connect delay to 8
  • using public instead of random (the 410 uses a public address type see )
  • the characteristics (look at them as a kind of configuration register) seem to be the same (8a22, 8a21, 8a82) but the corresponding handles (indeed, a shortcut for the long chararcteristic) are different. When the value "0200" is written to a characteristic (handle) we tell the device it can send data ("indications") and we'll be expecting it. A callback routine is started which catches these message(s) which have a handle 1 less than the handle registered to so we can identify them.
    so: send 0200 to handle 0x1d and wait (non blocking) for any replies which carry handle 0x1c.
    This is done for the 3 different handles.
    Characteristic 8a81 (handle 0x1a in your case) is a bit different: When writing "02" followed by the unix timestamp the scale syncs its RTC so that the time/date of the weighings is correct. But this also signals the scale to start sending the stored data (which should be 60 lines), if the 410 also supports stored data.
    The errror i made is that registering to the characteristics works fine (the appropriate handle is looked up and registered to). In the callback routine however these handles are hardcoded and - in your case - wrong.

Pygatt actually looks up the handles to be used when issuing "device.subscribe"
DEBUG get_handle Looking up handle for characteristic 00008a22-0000-1000-8000-00805f9b34fb
and shows all the available handles.

So i guess in the end only the address type (public vs random) should become selectable

@DjZU

no more messing up in the main .ini.

As for the communication part that's just fine. I didn't like that the ini file was used to enable/disable and configure all kinds of post processing code. That's why i separated that.

@keptenkurk did you sync your scale with your phone first too? I noticed a time discrepency:

Before I synced time with your tool:

236290675,80.7,19.3,45.0,4.8,59.7,1800,25.5

After:

1498594761,80.7,19.3,45.0,4.8,59.7,1800,25.5

If you don't see this discrepency, would this mean that my scale uses a different time encoding?

Okay I had a hunch and did some calculations. The first entry were the seconds not in epoch, but since 1/1/2010: www.timeanddate.com

236.304.000 seconds (currently)

Nope, i never used a phone.
Funny. The 1498594761 is the timedate according to the unix timestamp (see http://www.unixtimestamp.com/index.php).
Ahh.. your answer just popped up.
So we need to recalculate that too!

@DjZU @MarcHeinrich any of you used a phone to sync the scale? :) We need some verification ;-)
Not that it is very important, but it would be nice to know, and useful for people who DID use a phone before ... (otherwise the times don't add up)

@keptenkurk how the hell (excuse my french) did you figure out this was the time in seconds then? I'm still in the dark about how you knew what to send with that 0x23 handle ....

Time (since 1/1/2010) + 1262304000 = Unix timestamp.

@jovandeginste

how the hell (excuse my french) did you figure out this was the time in seconds then?

Ah... well thats easy. As i figured i was not the first one to try this i found this thread on stackoverflow. That guy snooped the BT traffic his phone was sending and started figuring out the values.

@jovandeginste I did use a phone to sync the scale and it works fine
The thing about the timestamp is if you saw a few you will recognize them if you see an other one
To know the handle a look in the ble communication log (captured by the android system) helps a lot at least for me

@MarcHeinrich Okay I never managed to capture the bluetooth hci log - my phone doesn't seem to support that :(

The think is, my timestamp was not in epoch seconds (which I would recognize immediately)... Were all your timestamps in epoch seconds then? Because that would be another difference then..

@keptenkurk ah of course, those were the "edmundo" links I failed to click....!

@jovandeginste all timestamps i had were in epoch time so it could be a difference

@jovandeginste but if thats the case it should be a static difference and that would be easy

@keptenkurk
One quick question aside from this scale.
A problem i encountered while trying to find out about an other ble connection is how to get the information about the measurement data, more precisely where they are in the messages. Do you have some experience how to do this?

@MarcHeinrich yes, of course easy difference. Like the handles, basically.

@MarcHeinrich

One quick question aside from this scale.

Personally, no. From what i read is snooping the easiest (only?) way if no documentation is available, and then just changing parts of the measurements and observe the changes in the data..

DjZU commented

@keptenkurk I started working on the code before reading your message. I created a PR #57 for your information but understood you'll go a more clever way.

Work so far is in the BS410 branch, but need to find out where i broke things... :-)

@keptenkurk can you not loop over the interesting_characteristics here instead of repeating the lines?

@keptenkurk obvious change, but here you should substitute the literal BS440 for the now configured device_model ;-)

@keptenkurk could you not set the offset to 0 by default, and still substract it here so the code becomes cleaner?

@keptenkurk I'm really not good with Python, but in Ruby I would assign the parameter here to a variable outside the loop (or even outside the function) and reuse it, something like:

def connect_device(address):
	    device_connected = False
	    tries = 3
	    device = None
            if device_model == 'BS410':
                address_type = pygatt.BLEAddressType.public
            else:
                address_type = pygatt.BLEAddressType.random
             
	    while not device_connected and tries > 0:
	        try:
	            device = adapter.connect(address, 8, address_type)	            
	            device_connected = True
	        except pygatt.exceptions.NotConnectedError:
	            tries -= 1
	    return device

(No idea if this is valid Python)

Not sure what you mean... The device_model is read from the .ini file so that's outside the function..(?)
The BS410 commit works on my BS440 with the mentioned parameter set to BS440. Could you have it a go on your BS410 with the parameter set accordingly?

The way it works now is that the Characteristics of interest are hard coded (defined as constants) but the corresponding handles are looked up first and saved and used in the callback routines where indications are received.
As far as time is concerned: The Unix timestamp of current time minus 1262304000 is written to the device. However when dealing with the data still a Unix timestamp is expected so the current plugins need to be aware of that (and might need to utilize the same parameter to fix this)

I'm off now... if things respond well i'll merge the BS410 branch into master

@jovandeginste Just now read your other suggestions. I'll polish it somewhat tonight before merging into master :-)

Cool, I can test tonight or tomorrow.

I did a quick test, and everything looks to be OK. I'll verify the log later today, to see if the timestamps check out too.

Great! Thanks for trying,,,

These are the received timestamps:

$ grep timestamp BS440.log  | awk '{print $9}' | uniq | sed 's/,$//' | while read d
do
  rd=$d
  [[ "$d" -lt 1000000000 ]] && rd=$((d+1262304000))
  echo -n "$d "
  date -d @$rd
done

Result:

236494566 vr jun 30 06:56:06 CEST 2017
1498595974 di jun 27 22:39:34 CEST 2017
1498595387 di jun 27 22:29:47 CEST 2017
[...]
1498594761 di jun 27 22:19:21 CEST 2017
236290675 di jun 27 22:17:55 CEST 2017
236290070 di jun 27 22:07:50 CEST 2017
236289928 di jun 27 22:05:28 CEST 2017
[...]

Dates seem ok, was however wondering if the offset should not be added back (eg. before writing it into the csv or other plugins)

Right, I cloned the branch yesterday afternoon, so I didn't get your last commit. I'll try again later :-)

Right... yeah the times sent to the plugins is now always unix timestamp. This required the decoding to know if tha data was from a BS410. So i moved everything into BS440.py.
Haha... if you're able to come up with scripts like that Python is a breeze :-)

I tested the last change too, and everything works. I vote for merging this :-) We can cleanup a bit more later. See my new PR's :-)

Just out of curiosity what is the bluetooth name which is displayed for this scale?
I was wondering because the BS440,444 and 430 all have the same bluetooth name(device name) if i remember correctly which was "medisana"

Where should I see this?

Ok the Device name can be read from characteristic UUID: 2a00 from service with UUID: 1800
What would also be interresting is the model number string this is read from UUID: 2a24 from service with UUID: 180a

Maybe these could be used for a generall recognition for the scales so a specific mac isn't needed (just an idea)

Could we add those handlers to the code and let them print it to the log? Would probably be useful for later models, and help debugging faster?

@jovandeginste Did you read the 2 characteristics and got the value/name?

@MarcHeinrich No, no time yet; I'll try to get to it before the end of the weekend. My Pi Zero W has arrived today, so I have an incentive ... :-)

@MarcHeinrich
I've been trying to read the Characteristics:
00002a00-0000-1000-8000-00805f9b34fb responds with 0202B6 followed by the MAC address in reversed byte order
00002a24-0000-1000-8000-00805f9b34fb is not recognized (or i must be doing something wrong)
Don't really get the different services you mention...

I got the code running on my Pi Zero W (woohoow!); however, didn't get response for the 2a00 UUID. What's the easiest way to get some feedback there?

Add something like this when connection is established (i.e. device is non zero):


Char_name = 00002a00-0000-1000-8000-00805f9b34fb

while True:
    wait_for_device(device_name)
    device = connect_device(ble_address)
    if device: 
        log.info('reading device name')
        response = device.char_read(Char_name)
        log.info(response)

you can also use device.char_read_handle(characteristic_handle) to get info by handle instead of full UUID. The methods can be found here

2017-07-05 22:01:52,107 INFO     <module> reading device name
2017-07-05 22:01:52,383 INFO     <module> 0203B F4B85EC73A7E

Is this not exactly what 'lescan' tells me?

Exactly...

@keptenkurk
From which scale did you read the "0202B6" was this a BS440?

That's interesting maybe i mixed something up but i am pretty sure my scale send me the device name "medisana" when i read the 2a00 characteristic
Either way i think that the "0203B" should be the default name for the BS410 scale then

For the 2a24 it is possible that the scale does not have this one it came just to my mind when i thought about getting a characteristic name for this device type so no mac needed to be entered before connecting, because the name or something like it would signal which scale type it is

Yes. It's a BS440. The idea is quite allright. Would save another configuration parameter.

@keptenkurk just to make things right
I checked the name of my device(BS440) and it showed me the same as yours.
I don't really know where i had this "medisana" name from