lolouk44 / hassio-addons

Xiaomi Mi Scale Add On for Home Assistant
MIT License
125 stars 42 forks source link

Body Composition Scale 2 (XMTZC05HM) - Wrong results for Visceral Fat and Body Fat #14

Closed zibous closed 4 years ago

zibous commented 4 years ago

Select Add-On (place a lowercase x for the relevant add-on)

Describe the bug

Wrong results for Visceral Fat and Body Fat I have a Mi Body Composition Scale 2 (XMTZC05HM) and get wrong results when calculating, especially for Visceral Fat and Body Fat. If I query these values from other scales, then they are correct.

What can be the reason? Is it the scales or the application cannot be used for the Mi Body Composition Scale 2 (XMTZC05HM)?

To Reproduce Running python 3.8 on a raspberry pi

Expected behaviour Maybe based on the Mi Body Composition Scale 2 ??

Screenshots NOP

Scale (please complete the following information):

Device running Home Assistant (please complete the following information): Raspberry PI

Additional context

## Person 1:
{
    "Weight": "72.50",
    "BMI": "22.88",
    "Basal Metabolism": "1248.68",
    "Visceral Fat": "15.37", <-----  ! ERROR
    "Lean Body Mass": "57.20",
    "Body Fat": "23.76", <-----  ! ERROR
    "Water": "52.30",
    "Bone Mass": "2.87",
    "Muscle Mass": "52.40",
    "Protein": "19.98",
    "Body Type": "Balanced",
    "Metabolic Age": "41",
    "TimeStamp": "2020-09-03 11:59"
}

## Person 2:
{
    "Weight": "51.05",
    "BMI": "18.09",
    "Basal Metabolism": "930.34",
    "Visceral Fat": "1.00", <-----  ! ERROR
    "Lean Body Mass": "47.17",
    "Body Fat": "23.36", <-----  ! ERROR
    "Water": "52.57",
    "Bone Mass": "2.09",
    "Muscle Mass": "37.04",
    "Protein": "19.98",
    "Body Type": "Balanced-skinny",
    "Metabolic Age": "27",
    "TimeStamp": "2020-09-03 11:59"
}

Person 1: Height 178, Age 64.7, miimpedance: 502

Xiaomi_Scale.py mi.app Medisana TargetScale
Weight 72.50 72.50 72.50
BMI 22.88 22.60 22.80
Basal Metabolism 1.248.68 1.222.00 1569.00
Visceral Fat 15.37 14.00 6.10
Lean Body Mass 57.20 -- --
Body Fat 23.76 21.50 13.10
Water 52.30 53.80 55.40
Bone Mass 2.87 2.80 3.60
Muscle Mass 52.40 52.20 44.20
Protein 19.98 20.60 --
Body Type Balanced Balanced --
Metabolic Age 41 39 --

Person 2: Heigt 168, Age 62.71, miimpedance: 523

Xiaomi_Scale.py mi.app Medisana TargetScale
Weight 51.05 51.05 51.20
BMI 18.09 18.00 18.5
Basal Metabolism 930.34 934.00 1260.00
Visceral Fat 1.00 3.00 3.00
Lean Body Mass 47.17 -- --
Body Fat 23.36 19.03 14.60
Water 52.57 55.30 56.90
Bone Mass 2.87 2.00 2.65
Muscle Mass 37.04 39.00 32.56
Protein 19.98 20.60 --
Body Type Balanced-skinny Balanced-skinny --
Metabolic Age 27 26 --

Some other values also deviate, but this is not so critical.

What can I do so that the values are evaluated in a similar way to other scales?

lolouk44 commented 4 years ago

Hi Peter,

Thanks for the extensive details, this is really helpful. For info I have not written the formulas to calculate the various values/measures, I've got them from https://github.com/wiecosystem/Bluetooth The repo has not been updated in 11 months and I have no idea when the initial formulas were retrieved / extracted from a mi app. It is most likely that since these formulas were extracted, Xiaomi has updated them in their app so the results are different. I have however no android device and even then I have no idea how to extract that info from the mi app... If this is something you could do I'd welcome your help...

I thing I want to ask is: have you got your scale set to a unit other than kg? If so I have fixed an issue for non kg measures which I'm about the publish in the next few days

zibous commented 4 years ago

Thanks for the information. šŸ‘

I use unit "kg", so that is not the problem

I was surprised, because with

def _publish (self, weight, unit, mitdatetime, hasImpedance, miimpedance)

unit is passed, but not processed.

lolouk44 commented 4 years ago

unit is passed, but not processed.

indeed, something I've amended and will be published in the next few days for those of us not using "standard" metrics šŸ˜œ

zibous commented 4 years ago

I think it's based of the age parameter, because if I put it on the metabolic age, then I come close to the values of the other scales.

Another factor is that all calculations do not take physique body scales. There are some differences in the calculations for athletic physique. This is not taken into any of the calculations.

I set the state athletic physique for the other scales so they show approximately the same value as in professional measuring methods.

Here is a good source, next to Wikipedia, with an overview of the calculation methods. https://www.omnicalculator.com/health

lolouk44 commented 4 years ago

Thanks. I need to release the version I've modified for now then will look at changing some of the calculations. That link looks interesting so I'll defo look into it. Would be be able / available to run tests once I start working on it? (I have no ETA as this is a hoby and my work is starting to keep me real busy now that the summer period is over)

zibous commented 4 years ago

ok. I can make tests. It is easy to do as it is possible to simulate the scale values, it's easier like that than always standing on the scales and then evaluating the data. :) I suspect it is because of the age that the getFatPercentageScale are not correct. Will investigate this further.

zibous commented 4 years ago

@lolouk44

I'm not sure whether the processing with the ScanProcessor for the Mi Body Composition Scale 2 v2 (XMTZC05HM) works.

I don't understand the evaluation with isStabilized = ctrlByte1 & (1 << 5) and with hasImpedance = ctrlByte1 & (1 << 1), because actually the return value should be true / false?

I can't say whether the determination of the impedance value is correct?

The weight value is OK, it is the same as the scale shows.

Logdata:

-------------------------------------
2020-09-06 13:37:27 - Starting Xiaomi mi Scale...
2020-09-06 13:37:27 - Loading Config From Add-On Options...
2020-09-06 13:37:27 - Config Loaded...
2020-09-06 13:37:27 - Discovery Completed...

2020-09-06 13:37:28,450:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b1802a6e40709060d232ee9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]

2020-09-06 13:37:28,451:DEBUG:SDID:1
2020-09-06 13:37:28,451:DEBUG:DESC:Flags
2020-09-06 13:37:28,451:DEBUG:DATA:06
2020-09-06 13:37:28,451:DEBUG:SDID:2
2020-09-06 13:37:28,452:DEBUG:DESC:Incomplete 16b Services
2020-09-06 13:37:28,452:DEBUG:DATA:0000181b-0000-1000-8000-00805f9b34fb
2020-09-06 13:37:28,452:DEBUG:SDID:22
2020-09-06 13:37:28,452:DEBUG:DESC:16b Service Data
2020-09-06 13:37:28,452:DEBUG:DATA:1b1802a6e40709060d232ee9015a37
2020-09-06 13:37:28,453:DEBUG:Xiaomi V2 Scale

2020-09-06 13:37:28,453:DEBUG:ctrlByte1:166
2020-09-06 13:37:28,453:DEBUG:isStabilized ctrlByte1:166
2020-09-06 13:37:28,453:DEBUG:hasImpedance ctrlByte1:166

2020-09-06 13:37:28,454:DEBUG:Weight:70.85 kg
2020-09-06 13:37:28,454:DEBUG:Impedance:489 Ohm, isStabilized:32
2020-09-06 13:37:28,465:DEBUG:MI TIME:2020-09-06 13:35:46/2020-09-06 13:37:27, Newdata:False
2020-09-06 13:37:28,465:DEBUG:SDID:9
2020-09-06 13:37:28,465:DEBUG:DESC:Complete Local Name
2020-09-06 13:37:28,465:DEBUG:DATA:MIBFS
2020-09-06 13:37:28,466:DEBUG:SDID:255
2020-09-06 13:37:28,466:DEBUG:DESC:Manufacturer
2020-09-06 13:37:28,466:DEBUG:DATA:57015ccad34cee74

2020-09-06 13:38:02,727:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b180226e40709060d2605e9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]

2020-09-06 13:38:02,728:DEBUG:SDID:1
2020-09-06 13:38:02,729:DEBUG:DESC:Flags
2020-09-06 13:38:02,729:DEBUG:DATA:06
2020-09-06 13:38:02,729:DEBUG:SDID:2
2020-09-06 13:38:02,730:DEBUG:DESC:Incomplete 16b Services
2020-09-06 13:38:02,730:DEBUG:DATA:0000181b-0000-1000-8000-00805f9b34fb
2020-09-06 13:38:02,731:DEBUG:SDID:22
2020-09-06 13:38:02,731:DEBUG:DESC:16b Service Data
2020-09-06 13:38:02,732:DEBUG:DATA:1b180226e40709060d2605e9015a37
2020-09-06 13:38:02,732:DEBUG:Xiaomi V2 Scale

2020-09-06 13:38:02,732:DEBUG:ctrlByte1:38
2020-09-06 13:38:02,733:DEBUG:isStabilized ctrlByte1:38
2020-09-06 13:38:02,733:DEBUG:hasImpedance ctrlByte1:38

2020-09-06 13:38:02,734:DEBUG:Weight:70.85 kg
2020-09-06 13:38:02,734:DEBUG:Impedance:489 Ohm, isStabilized:32
2020-09-06 13:38:02,735:DEBUG:MI TIME:2020-09-06 13:38:05/2020-09-06 13:37:27, Newdata:True
2020-09-06 13:38:02,736:DEBUG:Data published 2020-09-06 13:38:05 70.85 kg, Impedance 489, Status 2
2020-09-06 13:38:02,743:DEBUG:Publishing Message:{"Weight":"70.85","BMI":"22.87","Basal Metabolism":"1225.45","Visceral Fat":"15.04","Lean Body Mass":"56.12","Body Fat":"21.92","Water":"53.56","Bone Mass":"2.81","Muscle Mass":"52.51","Protein":"20.54","Body Type":"Balanced","Metabolic Age":"40","TimeStamp":"2020-09-06 13:38:02"}
2020-09-06 13:38:02,754:DEBUG:SDID:9
2020-09-06 13:38:02,754:DEBUG:DESC:Complete Local Name
2020-09-06 13:38:02,755:DEBUG:DATA:MIBFS
2020-09-06 13:38:02,755:DEBUG:SDID:255
2020-09-06 13:38:02,755:DEBUG:DESC:Manufacturer
2020-09-06 13:38:02,756:DEBUG:DATA:57015ccad34cee74

2020-09-06 13:38:38,269:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b1802a6e40709060d2614e9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]

2020-09-06 13:38:38,270:DEBUG:SDID:1
2020-09-06 13:38:38,271:DEBUG:DESC:Flags
2020-09-06 13:38:38,271:DEBUG:DATA:06
2020-09-06 13:38:38,272:DEBUG:SDID:2
2020-09-06 13:38:38,272:DEBUG:DESC:Incomplete 16b Services
2020-09-06 13:38:38,273:DEBUG:DATA:0000181b-0000-1000-8000-00805f9b34fb
2020-09-06 13:38:38,273:DEBUG:SDID:22
2020-09-06 13:38:38,274:DEBUG:DESC:16b Service Data
2020-09-06 13:38:38,274:DEBUG:DATA:1b1802a6e40709060d2614e9015a37
2020-09-06 13:38:38,274:DEBUG:Xiaomi V2 Scale

2020-09-06 13:38:38,275:DEBUG:ctrlByte1:166
2020-09-06 13:38:38,275:DEBUG:isStabilized ctrlByte1:166
2020-09-06 13:38:38,276:DEBUG:hasImpedance ctrlByte1:166

2020-09-06 13:38:38,276:DEBUG:Weight:70.85 kg
2020-09-06 13:38:38,277:DEBUG:Impedance:489 Ohm, isStabilized:32
2020-09-06 13:38:38,278:DEBUG:MI TIME:2020-09-06 13:38:20/2020-09-06 13:38:02, Newdata:True
2020-09-06 13:38:38,278:DEBUG:Data published 2020-09-06 13:38:20 70.85 kg, Impedance 489, Status 2
2020-09-06 13:38:38,281:DEBUG:Publishing Message:{"Weight":"70.85","BMI":"22.87","Basal Metabolism":"1225.45","Visceral Fat":"15.04","Lean Body Mass":"56.12","Body Fat":"21.92","Water":"53.56","Bone Mass":"2.81","Muscle Mass":"52.51","Protein":"20.54","Body Type":"Balanced","Metabolic Age":"40","TimeStamp":"2020-09-06 13:38:38"}
2020-09-06 13:38:38,291:DEBUG:SDID:9
2020-09-06 13:38:38,292:DEBUG:DESC:Complete Local Name
2020-09-06 13:38:38,292:DEBUG:DATA:MIBFS
2020-09-06 13:38:38,293:DEBUG:SDID:255
2020-09-06 13:38:38,293:DEBUG:DESC:Manufacturer
2020-09-06 13:38:38,293:DEBUG:DATA:57015ccad34cee74
2020-09-06 13:39:14,291:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b1802a6e40709060d2614e9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]
2020-09-06 13:39:14,292:DEBUG:SDID:1
2020-09-06 13:39:14,293:DEBUG:DESC:Flags
2020-09-06 13:39:14,293:DEBUG:DATA:06
2020-09-06 13:39:14,294:DEBUG:SDID:2
2020-09-06 13:39:14,294:DEBUG:DESC:Incomplete 16b Services
2020-09-06 13:39:14,294:DEBUG:DATA:0000181b-0000-1000-8000-00805f9b34fb
2020-09-06 13:39:14,295:DEBUG:SDID:22
2020-09-06 13:39:14,295:DEBUG:DESC:16b Service Data
2020-09-06 13:39:14,296:DEBUG:DATA:1b1802a6e40709060d2614e9015a37
2020-09-06 13:39:14,296:DEBUG:Xiaomi V2 Scale

2020-09-06 13:39:14,297:DEBUG:ctrlByte1:166
2020-09-06 13:39:14,297:DEBUG:isStabilized ctrlByte1:166
2020-09-06 13:39:14,298:DEBUG:hasImpedance ctrlByte1:166

2020-09-06 13:39:14,298:DEBUG:Weight:70.85 kg
2020-09-06 13:39:14,298:DEBUG:Impedance:489 Ohm, isStabilized:32
2020-09-06 13:39:14,299:DEBUG:MI TIME:2020-09-06 13:38:20/2020-09-06 13:38:38, Newdata:False
2020-09-06 13:39:14,300:DEBUG:SDID:9
2020-09-06 13:39:14,300:DEBUG:DESC:Complete Local Name
2020-09-06 13:39:14,301:DEBUG:DATA:MIBFS
2020-09-06 13:39:14,301:DEBUG:SDID:255
2020-09-06 13:39:14,301:DEBUG:DESC:Manufacturer
2020-09-06 13:39:14,302:DEBUG:DATA:57015ccad34cee74
2020-09-06 13:39:48,141:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b1802a6e40709060d2614e9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]
2020-09-06 13:39:48,142:DEBUG:SDID:1
2020-09-06 13:39:48,142:DEBUG:DESC:Flags
2020-09-06 13:39:48,143:DEBUG:DATA:06
2020-09-06 13:39:48,143:DEBUG:SDID:2
2020-09-06 13:39:48,144:DEBUG:DESC:Incomplete 16b Services
2020-09-06 13:39:48,144:DEBUG:DATA:0000181b-0000-1000-8000-00805f9b34fb
2020-09-06 13:39:48,145:DEBUG:SDID:22
2020-09-06 13:39:48,145:DEBUG:DESC:16b Service Data
2020-09-06 13:39:48,145:DEBUG:DATA:1b1802a6e40709060d2614e9015a37
2020-09-06 13:39:48,146:DEBUG:Xiaomi V2 Scale

2020-09-06 13:39:48,146:DEBUG:ctrlByte1:166
2020-09-06 13:39:48,147:DEBUG:isStabilized ctrlByte1:166
2020-09-06 13:39:48,147:DEBUG:hasImpedance ctrlByte1:166

2020-09-06 13:39:48,148:DEBUG:Weight:70.85 kg
2020-09-06 13:39:48,148:DEBUG:Impedance:489 Ohm, isStabilized:32
2020-09-06 13:39:48,149:DEBUG:MI TIME:2020-09-06 13:38:20/2020-09-06 13:38:38, Newdata:False
2020-09-06 13:39:48,150:DEBUG:SDID:9
2020-09-06 13:39:48,150:DEBUG:DESC:Complete Local Name
2020-09-06 13:39:48,151:DEBUG:DATA:MIBFS
2020-09-06 13:39:48,151:DEBUG:SDID:255
2020-09-06 13:39:48,151:DEBUG:DESC:Manufacturer
2020-09-06 13:39:48,152:DEBUG:DATA:57015ccad34cee74

Modifications ScanProcessor:

import logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s:%(levelname)s:%(message)s"
)

OLD_MITIME = str(datetime.today().strftime('%Y-%m-%d %H:%M:%S'))

class ScanProcessor():
    def GetAge(self, d1):
        d1 = datetime.strptime(d1, "%Y-%m-%d")
        d2 = datetime.strptime(datetime.today().strftime('%Y-%m-%d'),'%Y-%m-%d')
        return abs((d2 - d1).days)/365

    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        global OLD_MEASURE
        global OLD_MITIME
        midatetime = ''

        if dev.addr == MISCALE_MAC.lower() and isNewDev:

            logging.debug("SCANDATA:{}".format(dev.getScanData()))

            for (sdid, desc, data) in dev.getScanData():

                logging.debug("SDID:{}".format(sdid))
                logging.debug("DESC:{}".format(desc))
                logging.debug("DATA:{}".format(data))

                ### Xiaomi V1 Scale ###
                if data.startswith('1d18') and sdid == 22:
                    measunit = data[4:6]
                    measured = int((data[8:10] + data[6:8]), 16) * 0.01
                    unit = ''
                    if measunit.startswith(('03', 'b3')): unit = 'lbs'
                    if measunit.startswith(('12', 'b2')): unit = 'jin'
                    if measunit.startswith(('22', 'a2')): unit = 'kg' ; measured = measured / 2
                    if unit:
                        if OLD_MEASURE != round(measured, 2):
                            self._publish(round(measured, 2), unit, str(datetime.today().strftime('%Y-%m-%d %H:%M:%S')), "", "")
                            OLD_MEASURE = round(measured, 2)

                ### Xiaomi V2 Scale ###
                if data.startswith('1b18') and sdid == 22:

                    logging.debug("Xiaomi V2 Scale")    
                    data2 = bytes.fromhex(data[4:])
                    ctrlByte1 = data2[1]

                    logging.debug("ctrlByte1:{}".format(ctrlByte1))

                    isStabilized = ctrlByte1 & (1<<5)
                    if(isStabilized):
                        logging.debug("isStabilized ctrlByte1:{}".format(ctrlByte1))

                    ## impedance
                    hasImpedance = ctrlByte1 & (1<<1)
                    if(hasImpedance):
                        logging.debug("hasImpedance ctrlByte1:{}".format(ctrlByte1))

                    ## weight
                    measured = int((data[28:30] + data[26:28]), 16) * 0.01

                    ## unit
                    unit = ''
                    measunit = data[4:6]
                    if measunit == "03": unit = 'lbs'
                    if measunit == "02": unit = 'kg' ; measured = measured / 2
                    logging.debug("Weight:{} {}".format(round(measured, 2), unit))

                    miimpedance = str(int((data[24:26] + data[22:24]), 16))
                    logging.debug("Impedance:{} Ohm, isStabilized:{}".format(miimpedance,isStabilized))

                    if unit and isStabilized and miimpedance:  

                       # get the timestamp                      
                       mitdatetime = datetime.strptime(str(int((data[10:12] + data[8:10]), 16)) + " " + str(int((data[12:14]), 16)) +" "+ str(int((data[14:16]), 16)) +" "+ str(int((data[16:18]), 16)) +" "+ str(int((data[18:20]), 16)) +" "+ str(int((data[20:22]), 16)), "%Y %m %d %H %M %S")

                       logging.debug("MI TIME:{}/{}, Newdata:{}".format(mitdatetime, OLD_MITIME, (str(mitdatetime) > OLD_MITIME)))

                       # check if we can publish
                       if (str(mitdatetime) > OLD_MITIME): 
                          logging.debug("Data published {} {} {}, Impedance {}, Status {}".format(mitdatetime, round(measured, 2),unit,miimpedance, hasImpedance))
                          self._publish(round(measured, 2), unit, str(datetime.today().strftime('%Y-%m-%d %H:%M:%S')), hasImpedance, miimpedance)
                          OLD_MITIME = str(datetime.today().strftime('%Y-%m-%d %H:%M:%S'))

isStabilized is a good idea, but at the moment I can only get data every 2 attempts and it is difficult to check when the weighing process is finished.

Could it be because the V2 version delivers different data?

2020-09-06 13:54:25,008:DEBUG:SCANDATA:[(1, 'Flags', '06'), (2, 'Incomplete 16b Services', '0000181b-0000-1000-8000-00805f9b34fb'), (22, '16b Service Data', '1b1802a6e40709060d2614e9015a37'), (9, 'Complete Local Name', 'MIBFS'), (255, 'Manufacturer', '57015ccad34cee74')]
lolouk44 commented 4 years ago

hmm not sure if your Scale V2 works differently than others. Minor issue with your changes above is that we will only publish if impedance exists. If you weight yourself with socks, there will be no impedance and therefore no publish, even though there is some weight measure available. I've also stopped using the timestamp from the scale as this is only set after using the mi app. for some reason I have to reset my scale every so often or the weight is not right and is about 2KG higher than my actual weight. Not sure if it's because my scale has become faulty or because it's stored upright and that messes up with the sensors...

lolouk44 commented 4 years ago

Was just checking the link you previously provided for the calculations (https://www.omnicalculator.com/health). The body fat formula does not take into account the impedance, whereas the xiaomi formula does.

When you say "If I query these values from other scales, then they are correct.", do you actually have other physical scales that provide these measures, or are you using different software applications with the same data input retrieved from the Xiaomi Scale?

zibous commented 4 years ago

When you say "If I query these values from other scales, then they are correct.", do you actually have other physical scales that provide these measures, or are you using different software applications with the same data input retrieved from the Xiaomi Scale?

One Scale (Medisana) use Software and Hardware, another one ony Hardware. Both give me correct results.

I have done various tests and all calculation methods give incorrect results, especially when they use age. The BMI method is only an approximation, the method that uses impedance also includes age and this has more weight than the impedance value.

fatdata-male

fatdata-female

But I see that all Github projects always use the same lib and wonder why nobody checks these calculations? I'm still looking ...

lolouk44 commented 4 years ago

Thanks. Just did a quick search for "body fat percentage formula using impedance" This link was in one of the returned results: https://link.springer.com/chapter/10.1007/978-3-540-73841-1_207

Interestingly it states

Bioelectrical Impedance Analysis (BIA) is an alternative for the calculation of % Body Fat (%BF), but, so far, it requires population specific predictive formulae [1]

where [1] is https://nyaspubs.onlinelibrary.wiley.com/doi/abs/10.1111/j.1749-6632.2000.tb06449.x So the Body Fat % is likely to remain an approximated value and as such will always be wrong?

The below 2 links also states that there are several caveats that can affect the accuracy of the measured impedance... https://www.verywellfit.com/bioelectrical-impedance-analysis-bia-3495551 https://en.wikipedia.org/wiki/Bioelectrical_impedance_analysis

So the end value would anyway need to be taken with a pinch of salt...

zibous commented 4 years ago

Now I understand the calculation methods because this doesn't show the value of reality, but only guidelines with which to compare to the values a group of people can be compared.

The statement is then: Compared to the others, you are better, the same or worse.

healthy-body-fat-ranges-for-adults-min

In summary, this means that the results are not considered values a measurement can be seen. So I cannot do this with my previous values that I have recorded from the existing scales.

Here is another example of how the impedance affects the values: impedance_v1

impedance

lolouk44 commented 4 years ago

So are you OK for me to close this then?

zibous commented 4 years ago

o.k

zibous commented 4 years ago

@lolouk44

I have now tried almost all the formulas, but have not come to any useful results.

Fortunately, I have over 2000 measurements from the past. With the cross-check of the recorded data and the calculated data, I only determined correction factors and in the future I will get the same values with the Mi Body Composition Scale 2 as with the other scales.

The analysis shows that the fat values, muscles are too high, water, bones too low are calculated.

For this I have adjusted the user settings and use the determined factors to correct the values:

USER1_GT = 60
USER1_SEX = "male"
USER1_NAME = "Peter"
USER1_HEIGHT = 175
USER1_WEIGHT = 68.00
USER1_ATHLETIC = True
USER1_ADJUSTMENTS = {
    "78.0": {"fat": 0.766, "visceral": 0.628, "water": 1.186, "bone": 1.271, "muscle": 0.750},
    "77.0": {"fat": 0.756, "visceral": 0.620, "water": 1.187, "bone": 1.276, "muscle": 0.758},
    "77.5": {"fat": 0.759, "visceral": 0.622, "water": 1.188, "bone": 1.276, "muscle": 0.756},
    "76.5": {"fat": 0.745, "visceral": 0.611, "water": 1.188, "bone": 1.285, "muscle": 0.767},
    "76.0": {"fat": 0.742, "visceral": 0.608, "water": 1.189, "bone": 1.289, "muscle": 0.770},
    "75.5": {"fat": 0.739, "visceral": 0.606, "water": 1.189, "bone": 1.289, "muscle": 0.774},
    "75.0": {"fat": 0.734, "visceral": 0.602, "water": 1.190, "bone": 1.294, "muscle": 0.780},
    "74.5": {"fat": 0.725, "visceral": 0.594, "water": 1.190, "bone": 1.263, "muscle": 0.786},
    "74.0": {"fat": 0.638, "visceral": 0.523, "water": 1.045, "bone": 1.312, "muscle": 0.825},
    "73.5": {"fat": 0.634, "visceral": 0.520, "water": 1.044, "bone": 1.317, "muscle": 0.823},
    "73.0": {"fat": 0.626, "visceral": 0.514, "water": 1.044, "bone": 1.317, "muscle": 0.833},
    "72.5": {"fat": 0.593, "visceral": 0.487, "water": 1.052, "bone": 1.321, "muscle": 0.839},
    "72.0": {"fat": 0.591, "visceral": 0.485, "water": 1.050, "bone": 1.326, "muscle": 0.843},
    "71.5": {"fat": 0.585, "visceral": 0.480, "water": 1.051, "bone": 1.326, "muscle": 0.848},
    "71.0": {"fat": 0.604, "visceral": 0.495, "water": 1.044, "bone": 1.331, "muscle": 0.845},
    "70.5": {"fat": 0.592, "visceral": 0.486, "water": 1.044, "bone": 1.300, "muscle": 0.854},
    "70.0": {"fat": 0.586, "visceral": 0.481, "water": 1.044, "bone": 1.304, "muscle": 0.858},
    "69.5": {"fat": 0.585, "visceral": 0.480, "water": 1.044, "bone": 1.304, "muscle": 0.861},
    "69.0": {"fat": 0.576, "visceral": 0.473, "water": 1.043, "bone": 1.309, "muscle": 0.867},
    "68.5": {"fat": 0.563, "visceral": 0.462, "water": 1.217, "bone": 1.300, "muscle": 0.886},
    "68.0": {"fat": 0.558, "visceral": 0.458, "water": 1.215, "bone": 1.304, "muscle": 0.892},
    "67.5": {"fat": 0.553, "visceral": 0.453, "water": 1.041, "bone": 1.324, "muscle": 0.895},
    "67.0": {"fat": 0.544, "visceral": 0.446, "water": 1.040, "bone": 1.324, "muscle": 0.898},
    "66.5": {"fat": 0.533, "visceral": 0.437, "water": 1.038, "bone": 1.292, "muscle": 0.913},
    "66.0": {"fat": 0.532, "visceral": 0.436, "water": 1.038, "bone": 1.296, "muscle": 0.913},
    "65.0": {"fat": 0.520, "visceral": 0.426, "water": 1.036, "bone": 1.301, "muscle": 0.926},
    "65.5": {"fat": 0.488, "visceral": 0.400, "water": 1.044, "bone": 1.296, "muscle": 0.930},
    "64.5": {"fat": 0.508, "visceral": 0.417, "water": 1.035, "bone": 1.306, "muscle": 0.936},
    "64.0": {"fat": 0.499, "visceral": 0.409, "water": 1.035, "bone": 1.306, "muscle": 0.941},
    "63.5": {"fat": 0.486, "visceral": 0.398, "water": 1.033, "bone": 1.316, "muscle": 0.952},
    "63.0": {"fat": 0.483, "visceral": 0.396, "water": 1.033, "bone": 1.316, "muscle": 0.954},
    "62.5": {"fat": 0.560, "visceral": 0.459, "water": 1.014, "bone": 1.326, "muscle": 0.948},
    "61.5": {"fat": 0.462, "visceral": 0.378, "water": 1.030, "bone": 1.288, "muscle": 0.970},
    "61.0": {"fat": 0.462, "visceral": 0.378, "water": 1.030, "bone": 1.288, "muscle": 0.970}
    }

It works quite well, only I have to set the age to 20 years.

    def round_to_value(self, number,roundto:float=0.5)->str:
        return str(round(float(number) / roundto) * roundto)

    def __recalibrate__(self):
        if self.adjustments and self.athletic:
            idx = self.round_to_value(self.weight)
            if idx in self.adjustments:
                cf = self.adjustments[idx]
                self.data['fat'] = round(float(self.data['fat'])*float(cf['fat']),2)
                self.data['visceral'] = round(float(self.data['visceral'])*float(cf['visceral']),2)
                self.data['water'] = round(float(self.data['water'])*float(cf['water']),2)
                self.data['bone'] = round(float(self.data['bone'])*float(cf['bone']),2)
                self.data['muscle'] = round(float(self.data['muscle'])*float(cf['muscle']),2)
lolouk44 commented 4 years ago

Thanks @zibous

If I get this right the corrections above are quite personal and work for "you", but probably can't be applied generally as they may not work for everyone? If so I take it you're happy with your modified version and there's nothing for me to do?

zibous commented 4 years ago

@lolouk44

I think it is suitable for certain types of people, then all the formulas and investigations that have been found do not take into account the ATHLETIC mode.

I record my values using 2 scales and regularly through a check at a professional institute, so I can say with a high degree of certainty that the values on my scales are approximately correct.

I do a lot of strength training and therefore have little fat but a high percentage of muscle. The methods present in the Lib are general and not intended for an athlete.

That's why I introduced USER1_ATHLETIC in the settings. If this is set, the correction data will be used, otherwise the normal calculation methods.

The same applies to the age, if the DOB is not set, then I use 20 years as the age.

Once I've cleaned up the code, I'll post this on Github. see: https://github.com/zibous/ha-miscale2

lolouk44 commented 4 years ago

Thanks for the feedback.

zibous commented 4 years ago

@lolouk44

Developer preview is now online. https://github.com/zibous/ha-miscale2

ralucat9 commented 1 year ago

Hi. I have an issue with the results of the scale. I have made an account introducing the personal data: age, height, kg and it gives a result. If i add a new profile with the same personal data it gives me a total diff value. Why is that?