Integration of Medisana BS440 Connected scale

In this subforum you can show projects you have made, or you are busy with. Please create your own topic.
kman
Posts: 165
Joined: Wednesday 30 September 2015 19:33
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by kman » Friday 21 April 2017 10:54

@Keptenkurk:
I have resolved the wireshark/btsnoop_hci.log issue that I mentioned above - the issue was wireshark version (1.6.x) version that I was using - I changed this is to version 1.12.1 and I am able to see write_cmd, write_req messages. But, please do let me know if you have used any other tools (other than BLE scanner app that you mention on your blog). Thanks.

franzelare
Posts: 148
Joined: Thursday 19 February 2015 22:48
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by franzelare » Saturday 22 April 2017 8:52

@kman, forget all the unknown, see where it states that it is connected to the MAC of your device, from there is gets interesting
than you see just a few writes and the device will start dumping data...

i did the same for the medisana blood pressure tester
next plan is to try to do the same trick for the nest protect smoke sensor

kman
Posts: 165
Joined: Wednesday 30 September 2015 19:33
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by kman » Sunday 23 April 2017 15:28

@franzelare - thanks for that - yes, I see that. So, in your domoticz if you go to "setup->more options -> data push -> http", are you able to see all the devices against your virtual sensors from the drop down of "device name" - so, when you fill them in, it comes up in that table in that screen? (it will be really fascinating if it does, with http_post possible, data would be accessible remotely across the web-world!)

¬¬¬
I just got my Medisana Weighing Scale delivered - so, thought of trying all the suggestions made here:
ble_address: D5:E8:XX:XX:XX:XX
device_name: 0203B 363F412DE8D5
1. with the latest (git hub master) after receiving all the processindication messages, I get this python error, and I don't see any data on my domoticz. What am I missing?
Traceback (most recent call last):
File "BS440.py", line 172, in <module>
device.disconnect()
File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/device.py", line 17, in wrapper
return func(self, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/device.py", line 52, in disconnect
self._backend.disconnect(self)
File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/gatttool.py", line 52, in wrapper
raise NotConnectedError()
pygatt.exceptions.NotConnectedError
BS440_master.log
BS440_master.log
(13.51 KiB) Downloaded 33 times
Under the plugins directory, I have removed the "_" from the three files BS440domoticz.ini, BS440domoticz.py, BS440domoticz.png. And in BS440domoticz.ini, I have not made any chages, so it contains as below. So, what am I missing? Why am I not seeing any data in Domoticz?
# BSdomoticz440.ini
# Initialization file for plugin BS440domoticz.py
# Change to your preference
# Store this file in the working folder of BS440domoticz.py
#
# The persons section lists all the scale users
# The list maybe extended to 8 persons
# Domoticz index is only used if domoticz IP is set
[Person1]
username: John

[Person2]
username: Jill

# DomoticzIP: IP adres of Domoticz server
# Assumes unauthenticated access when on local LAN
# set in Domoticz|setup|Settings|System|Local Networks
# hardware_name is optional (default: Medisana)
domoticz_url: 127.0.0.1:8080
#hardware_name:
2. So, went back to version 1.0 of BS440 on git-hub. Here it doesn't even get as far as processindication - it fails at the time of connection.

Is this a problem with 1.0 version of BS440.py due to my medisana scale's device name having a space "0203B 363F412DE8D5" ? (as latest version of BS440.py seems to connect fine, and even process-indication messages are received -except for the data to be seen on domoticz)
python BS440.py
Traceback (most recent call last):
File "BS440.py", line 106, in <module>
device = connect_device(ble_address)
File "BS440.py", line 55, in connect_device
device = adapter.connect(address, 5, 'random')
File "/usr/local/lib/python2.7/dist-packages/pygatt/backends/gatttool/gatttool.py", line 376, in connect
cmd = 'connect {0} {1}'.format(self._address, address_type.name)
AttributeError: 'str' object has no attribute 'name'
BS440-1.0.log
BS440-1.0.log
(614 Bytes) Downloaded 31 times

kman
Posts: 165
Joined: Wednesday 30 September 2015 19:33
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by kman » Sunday 23 April 2017 15:48

@franzelare: Just noticed Keptenkurk's response(Apr 6) to my earlier message, it might be that the domoticz plugin is broken on the latest version - may be that explains why I don't see data on domoticz after process indication messages; if you don't mind please could you attach your working "BS440" version here? Many thanks.
Keptenkurk wrote: ....
....
But the Domoticz plugin hasn't been tested yet (at least by me) after converting Trixwood's work to the plugin style. So might stil be broken. Really didn't find the chance to give it a go yet but certainly will.
/paul
[/quote]

kman
Posts: 165
Joined: Wednesday 30 September 2015 19:33
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by kman » Sunday 23 April 2017 19:09

@trixwood
As I said in my previous post, on the latest vesion after processindication messages, I was not seeing any data in domoticz as the process halted after the execution of 'device.disconnect'. Just to progress on getting the data onto domoticz, for now, I have commented out the below line from BS440.py (will need to check this later)
device.disconnect()
Now, atleast it gets to the domoticzplugin code, but the plugin is definitely broken as I encounter quite a few bugs (don't think it has been tested).

Fix #1 : to add 'Domoticz' section in the .ini
[Domoticz] ---------------------------> this is missing in BS440domoticz.ini file
domoticz_url: 192.168.1.4:8080
hardware_name: Medisana
Fix#2: BS440domoticz.py - line 184.
change
if config.has_section(personsection):
to
if pluginconfig.has_section(personsection):
Now, I am stuck here, any help is most appreciated.
Traceback (most recent call last):
File "BS440.py", line 192, in <module>
plugin.execute(config, persondata, weightdatasorted, bodydatasorted)
File "plugins/BS440domoticz.py", line 200, in execute
hardwareid = self.exists_hardware(hardwarename)
File "plugins/BS440domoticz.py", line 41, in exists_hardware
response = self.open_url(url_hardware % (domoticzurl))
NameError: global name 'url_hardware' is not defined

Itschi
Posts: 12
Joined: Saturday 16 May 2015 14:58
Target OS: Windows
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by Itschi » Wednesday 17 May 2017 21:13

Did you resolve the problem? I get the same exception "pygatt.exceptions.NotConnectedError" after reading the scale data.

franzelare
Posts: 148
Joined: Thursday 19 February 2015 22:48
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by franzelare » Sunday 13 May 2018 22:19

anyone worked on posting the data to fitbit through the API?

BenSunnyField
Posts: 2
Joined: Sunday 15 July 2018 13:25
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by BenSunnyField » Sunday 15 July 2018 13:29

@Franzelare I see you got the Medisana BU530 working, would you like to share that code?
It is exactly what I want to do so your code would be very welcome.

User avatar
dre68
Posts: 26
Joined: Friday 30 January 2015 21:18
Target OS: Raspberry Pi
Domoticz version: latest
Location: Noordwijk, Netherlands
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by dre68 » Monday 30 July 2018 22:18

I am trying to get the medisana solution of ketpenkurk runnning, but I run into the following error when starting BS440.sevice:

Jul 30 21:56:58 DomoticzPi3 python[18565]: File "BS440.py", line 2, in <module>
Jul 30 21:56:58 DomoticzPi3 python[18565]: import pygatt.backends
Jul 30 21:56:58 DomoticzPi3 python[18565]: ImportError: No module named pygatt.backends

Anyone knows how to fix this?

BenSunnyField
Posts: 2
Joined: Sunday 15 July 2018 13:25
Target OS: Linux
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by BenSunnyField » Saturday 04 August 2018 22:01

Try installing PyGatt
https://github.com/peplin/pygatt

User avatar
dre68
Posts: 26
Joined: Friday 30 January 2015 21:18
Target OS: Raspberry Pi
Domoticz version: latest
Location: Noordwijk, Netherlands
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by dre68 » Tuesday 07 August 2018 20:53

PyGatt was installed, did a reinstall according to your link but to no avail
Still error:

-- Logs begin at Thu 2016-11-03 18:16:42 CET. --
Aug 07 20:46:48 DomoticzPi3 systemd[1]: bs440.service: Unit entered failed state .
Aug 07 20:46:48 DomoticzPi3 systemd[1]: bs440.service: Failed with result 'exit- code'.
Aug 07 20:48:56 DomoticzPi3 systemd[1]: Started Run BS440 bluetooth scale monito r.
Aug 07 20:48:56 DomoticzPi3 python[771]: Traceback (most recent call last):
Aug 07 20:48:56 DomoticzPi3 python[771]: File "BS440.py", line 2, in <module>
Aug 07 20:48:56 DomoticzPi3 python[771]: import pygatt.backends
Aug 07 20:48:56 DomoticzPi3 python[771]: ImportError: No module named pygatt.bac kends
Aug 07 20:48:56 DomoticzPi3 systemd[1]: bs440.service: Main process exited, code =exited, status=1/FAILURE
Aug 07 20:48:56 DomoticzPi3 systemd[1]: bs440.service: Unit entered failed state .
Aug 07 20:48:56 DomoticzPi3 systemd[1]: bs440.service: Failed with result 'exit- code'.

franzelare
Posts: 148
Joined: Thursday 19 February 2015 22:48
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by franzelare » Saturday 25 August 2018 14:02

BenSunnyField wrote:
Sunday 15 July 2018 13:29
@Franzelare I see you got the Medisana BU530 working, would you like to share that code?
It is exactly what I want to do so your code would be very welcome.
sorry for the late reply, did not read the forum for a while.
I have 1 file scanning for both devices at the same time, and when detecting 1, connecting and processing.
Issue is that both measurements should be separated by 2 minutes or so, otherwise the second is missing sometimes.
I did not make a file that adds the new devices the first time, so when you run this the first time without the ini file, domoticz will add id that you manually have to fill in into the BU530.domoticz.ini

Medisana.py

Code: Select all

from __future__ import print_function
import pygatt.backends
import logging
from ConfigParser import SafeConfigParser
import time
import subprocess
from struct import *
from binascii import hexlify
from BU530decode import *
from BU530domoticz import *
from BS440decode import *
from BS440domoticz import *


def processIndication1(handle, values):
    '''
    Indication handler
    receives indication and stores values into result Dict
    (see medisanaBLE for Dict definition)
    handle: byte
    value: bytearray
    '''
    if handle == 0x39:
        result = decodeBlood(handle, values)
        if result not in blooddata:
            log.info(str(result))
            blooddata.append(result)
        else:
            log.info('Duplicate blooddata record')
    else:
        log.debug('Unhandled Indication encountered')


def processIndication2(handle, values):
    '''
    Indication handler
    receives indication and stores values into result Dict
    (see medisanaBLE for Dict definition)
    handle: byte
    value: bytearray
    '''
    if handle == 0x25:
        result = decodePerson(handle, values)
        if result not in persondata:
            log.info(str(result))
            persondata.append(result)
        else:
            log.info('Duplicate persondata record')
    elif handle == 0x1b:
        result = decodeWeight(handle, values)
        if result not in weightdata:
            log.info(str(result))
            weightdata.append(result)
        else:
            log.info('Duplicate weightdata record')
    elif handle == 0x1e:
        result = decodeBody(handle, values)
        if result not in bodydata:
            log.info(str(result))
            bodydata.append(result)
        else:
            log.info('Duplicate bodydata record')
    else:
        log.debug('Unhandled Indication encountered')


def wait_for_device(devname1, devname2):
    found = False
    found1 = False
    found2 = False
    while not found:
        try:
            devicescan=adapter.scan()
            found1 = [device for device in devicescan 
                     if devname1 in (device['name'] or '')]
            if found1:
                 found = found1
            found2 = [device for device in devicescan 
                      if devname2 in (device['name'] or '')]
            if found2:
                found = found2
        except pygatt.exceptions.BLEError:
            adapter.reset()
    return(found1, found2)


def connect_device1(address):
    device1_connected = False
    tries = 3
    device1 = None
    while not device1_connected and tries > 0:
        try:
            device1 = adapter.connect(address, 5, pygatt.BLEAddressType.public)
            device1_connected = True
        except pygatt.exceptions.NotConnectedError:
            tries -= 1
    return device1

def connect_device2(address):
    device2_connected = False
    tries = 3
    device2 = None
    while not device2_connected and tries > 0:
        try:
            device2 = adapter.connect(address, 5, pygatt.BLEAddressType.random)
            device2_connected = True
        except pygatt.exceptions.NotConnectedError:
            tries -= 1
    return device2


def init_ble_mode():
    p = subprocess.Popen("sudo btmgmt le on", stdout=subprocess.PIPE,
                         shell=True)
    (output, err) = p.communicate()
    if not err:
        log.info(output)
        return True
    else:
        log.info(err)
        return False

'''
Main program loop
'''
config = SafeConfigParser()
config.read('/home/pi/medisana/Medisana.ini')

# set up logging
numeric_level = getattr(logging,
                        config.get('Program', 'loglevel').upper(),
                        None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level,
                    format='%(asctime)s %(levelname)-8s %(funcName)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename=config.get('Program', 'logfile'),
                    filemode='w')
log = logging.getLogger(__name__)

ble_address1 = config.get('Blood', 'ble_address1')
device1_name = config.get('Blood', 'device1_name')

ble_address2 = config.get('Scale', 'ble_address2')
device2_name = config.get('Scale', 'device2_name')

'''
Start BLE comms and run that forever
'''
log.info('Medisana Started')
if not init_ble_mode():
    sys.exit()

adapter = pygatt.backends.GATTToolBackend()
adapter.start()

while True:
    devicestatus1, devicestatus2 = wait_for_device(device1_name, device2_name)
    if devicestatus1:
        device1 = connect_device1(ble_address1)
        if device1:
            blooddata = []
            continue_comms = True
            '''
            subscribe to characteristics and have processIndication
            process the data received.
            '''
            try:
                device1.subscribe('00002a35-0000-1000-8000-00805f9b34fb',
                             callback=processIndication1,
                             indication=True)
            except pygatt.exceptions.NotConnectedError:
                continue_comms = False

            if continue_comms:
                try:
                    device1.char_write_handle(0x3a, [0x02, 0x00],
                                         wait_for_response=True)
                except pygatt.exceptions.NotificationTimeout:
                    pass
                except pygatt.exceptions.NotConnectedError:
                    continue_comms = False
                if continue_comms:
                    log.info('Waiting for notifications for another 30 seconds')
                    time.sleep(30)
                    device1.disconnect()
                    log.info('Done receiving data from blood pressure sensor')
                    # process data if all received well
                    if blooddata:
#                        print("blood data start sending to domoticz")
                        UpdateDomoticzBlood(config, blooddata)
                    else:
                        log.error('Unreliable data received. Unable to process')

    if devicestatus2:
        device2 = connect_device2(ble_address2)
        if device2:
            persondata = []
            weightdata = []
            bodydata = []
            continue_comms = True
            '''
            subscribe to characteristics and have processIndication
            process the data received.
            '''
            try:
                device2.subscribe('00008a22-0000-1000-8000-00805f9b34fb',
                             callback=processIndication2,
                             indication=True)
                device2.subscribe('00008a21-0000-1000-8000-00805f9b34fb',
                             callback=processIndication2,
                             indication=True)
                device2.subscribe('00008a82-0000-1000-8000-00805f9b34fb',
                             callback=processIndication2,
                             indication=True)
            except pygatt.exceptions.NotConnectedError:
                continue_comms = False

            '''
            Send the unix timestamp in little endian order preceded by 02 as
            bytearray to handle 0x23. This will resync the scale's RTC.
            While waiting for a response notification, which will never
            arrive, the scale will emit 30 Indications on 0x1b and 0x1e each.
            '''
            if continue_comms:
                timestamp = bytearray(pack('<I', int(time.time())))
                timestamp.insert(0, 2)
                try:
                    device2.char_write_handle(0x23, timestamp,
                                         wait_for_response=True)
                except pygatt.exceptions.NotificationTimeout:
                    pass
                except pygatt.exceptions.NotConnectedError:
                    continue_comms = False
                if continue_comms:
                    log.info('Waiting for notifications for another 30 seconds')
                    time.sleep(120)
                    device2.disconnect()
                    log.info('Done receiving data from scale')
                    # process data if all received well
                    if persondata and weightdata and bodydata:
                        # Sort scale output by timestamp to retrieve most recent three results
                        weightdatasorted = sorted(weightdata, key=lambda k: k['timestamp'], reverse=True)
                        bodydatasorted = sorted(bodydata, key=lambda k: k['timestamp'], reverse=True)
                        UpdateDomoticz(config, persondata, weightdatasorted, bodydatasorted)
                    else:
                        log.error('Unreliable data received. Unable to process')

BU530.domoticz.ini

Code: Select all

[Person1]
systolic_id = 184
diastolic_id = 185
indicator_id = 186
pulserate_id = 187
arrhythmia_id = 188
BU530decode.py

Code: Select all

from struct import *
import sys
'''
Reads Medisana BU530 blood pressure sensor hex Indication and decodes values from hex data string and returns a dictionary with decoded data
'''

def decodeBlood(handle, values):
    '''
    decodeBlood
    Handle: 0x39
    Byte[0] = 0x1e
    Returns:
        valid (True, False)
        timestamp (unix timestamp date and time of measurement)
        person (1)   
        SyS = mmHg
        Dia = mmHg
        Pull = per min
        Indicator = 
        Arrhythmia = 
    '''
    data = unpack('BBxBxBxxBBBBBxBxxBx', bytes(values[0:19]))
#    print(data)
    retDict = {}
    retDict["valid"] = (handle == 0x39 and data[0] == 0x1e)
    retDict["SyS"] = data[1]
    retDict["Dia"] = data[2]
    retDict["Indicator"] =  data[3]
    retDict["Date"] = (data[4] , data[5] , data[6])
    retDict["Time"] = (data[7] , data[8])
    retDict["Pull"] = data[9]
    retDict["Arrhythmia"] = data[10]
    return retDict
BU530domoticz.py

Code: Select all

'''
BU530domoticz.py
Update blood pressure monitor value to Domoticz home automation system
'''

import urllib
import base64
import logging
import time
import traceback
import json
from ConfigParser import *

configDomoticz = SafeConfigParser()
configDomoticz.read('/home/pi/medisana/BU530.domoticz.ini')

def UpdateDomoticzBlood(config, persondata):
    log = logging.getLogger(__name__)
    domoticzurl = config.get('Domoticz', 'domoticz_url')
    url_blood = 'http://%s/json.htm?type=command&param=udevice&idx=%s&nvalue=0&svalue=%s'    

    def open_url(url):
        log.debug('Opening url: %s' % (url))
        try:
            response = urllib.urlopen(url)
        except Exception, e:
            log.error('Failed to send data to Domoticz (%s)' % (url))
            return '{}'
        return response

    try:
        log_update = 'Updating Domoticz for user preson 1 at index %s with '
#        print("start to send data")
        # send collected values to domoticz
        pressureSyS = persondata[0]['SyS']
#        print("collected SyS")
        hardwareid = configDomoticz.get('Person1', 'systolic_id')
#        print("collected hardwareid")
        log.info((log_update+'pressure %s') % (hardwareid, pressureSyS))
#        print("write to log")
        open_url(url_blood % (domoticzurl, hardwareid, pressureSyS))
#        print("send url")
#        print("next values")

        pressureDia = persondata[0]['Dia']
        hardwareid = configDomoticz.get('Person1', 'diastolic_id')
        log.info((log_update+'pressure %s') % (hardwareid, pressureDia))
        open_url(url_blood % (domoticzurl, hardwareid, pressureDia))

        pressureInd = persondata[0]['Indicator']
        hardwareid = configDomoticz.get('Person1', 'indicator_id')
        log.info((log_update+'pressure %s') % (hardwareid, pressureInd))
        open_url(url_blood % (domoticzurl, hardwareid, pressureInd))

        pulse = persondata[0]['Pull']
        hardwareid = configDomoticz.get('Person1', 'pulserate_id')
        log.info((log_update+'pulse %s') % (hardwareid, pulse))
        open_url(url_blood % (domoticzurl, hardwareid, pulse))

        pulseAr = persondata[0]['Arrhythmia']
        hardwareid = configDomoticz.get('Person1', 'arrhythmia_id')
        log.info((log_update+'pulse %s') % (hardwareid, pulseAr))
        open_url(url_blood % (domoticzurl, hardwareid, pulseAr))

        log.info('Domoticz succesfully updated')

    except:
        log.error('Unable to update Domoticz: Error sending data.')


franzelare
Posts: 148
Joined: Thursday 19 February 2015 22:48
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Integration of Medisana BS440 Connected scale

Post by franzelare » Saturday 25 August 2018 15:00

BenSunnyField wrote:
Sunday 15 July 2018 13:29
@Franzelare I see you got the Medisana BU530 working, would you like to share that code?
It is exactly what I want to do so your code would be very welcome.
one more note on the BU530
when the memory is full, it is not flushing out the oldest value but start over-writing the momery line by line.
so when you dump the memory , the last line is not the newest entry... but the script is always decoding the last line and sending that to domoticz, so the values in domoticz are the same every time.

I did not bother to fix that yet, so just clear the memory every now and then...
but this could be improved by looking for the entry with the newest time stamp, but that will add a lot of code and i have no clue how to do that in an easy way

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests