hassio-addons/mi-scale/src/body_score.py

182 lines
6.7 KiB
Python
Raw Normal View History

2020-07-01 02:18:55 +08:00
# Reverse engineered from amazfit's app (also known as Mi Fit)
from body_scales import bodyScales
class bodyScore:
def __init__(self, age, sex, height, weight, bmi, bodyfat, muscle, water, visceral_fat, bone, basal_metabolism, protein):
self.age = age
self.sex = sex
self.height = height
self.weight = weight
self.bmi = bmi
self.bodyfat = bodyfat
self.muscle = muscle
self.water = water
self.visceral_fat = visceral_fat
self.bone = bone
self.basal_metabolism = basal_metabolism
self.protein = protein
self.scales = bodyScales(age, height, sex, weight)
def getBodyScore(self):
score = 100
score -= self.getBmiDeductScore()
score -= self.getBodyFatDeductScore()
score -= self.getMuscleDeductScore()
score -= self.getWaterDeductScore()
score -= self.getVisceralFatDeductScore()
score -= self.getBoneDeductScore()
score -= self.getBasalMetabolismDeductScore()
if self.protein:
score -= self.getProteinDeductScore()
return score
def getMalus(self, data, min_data, max_data, max_malus, min_malus):
result = ((data - max_data) / (min_data - max_data)) * float(max_malus - min_malus)
if result >= 0.0:
return result
return 0.0
def getBmiDeductScore(self):
if not self.height >= 90:
# "BMI is not reasonable
return 0.0
bmi_low = 15.0
bmi_verylow = 14.0
bmi_normal = 18.5
bmi_overweight = 28.0
bmi_obese = 32.0
fat_scale = self.scales.getFatPercentageScale()
# Perfect range (bmi >= 18.5 and bodyfat not high for adults, bmi >= 15.0 for kids
if self.bmi >= 18.5 and self.age >= 18 and self.bodyfat < fat_scale[2]:
return 0.0
elif self.bmi >= bmi_verylow and self.age < 18 and self.bodyfat < fat_scale[2]:
return 0.0
# Extremely skinny (bmi < 14)
elif self.bmi <= bmi_verylow:
return 30.0
# Too skinny (bmi between 14 and 15)
elif self.bmi > bmi_verylow and self.bmi < bmi_low:
return self.getMalus(self.bmi, bmi_verylow, bmi_low, 30, 15) + 15.0
# Skinny (for adults, between 15 and 18.5)
elif self.bmi >= bmi_low and self.bmi < bmi_normal and self.age >= 18:
return self.getMalus(self.bmi, 15.0, 18.5, 15, 5) + 5.0
# Normal or high bmi but too much bodyfat
elif ((self.bmi >= bmi_low and self.age < 18) or (self.bmi >= bmi_normal and self.age >= 18)) and self.bodyfat >= fat_scale[2]:
# Obese
if self.bmi >= bmi_obese:
return 10.0
# Overweight
if self.bmi > bmi_overweight:
return self.getMalus(self.bmi, 28.0, 25.0, 5, 10) + 5.0
else:
return 0.0
def getBodyFatDeductScore(self):
scale = self.scales.getFatPercentageScale()
if self.sex == 'male':
best = scale[2] - 3.0
elif self.sex == 'female':
best = scale[2] - 2.0
# Slighly low in fat or low part or normal fat
if self.bodyfat >= scale[0] and self.bodyfat < best:
return 0.0
elif self.bodyfat >= scale[3]:
return 20.0
else:
# Sightly high body fat
if self.bodyfat < scale[3]:
return self.getMalus(self.bodyfat, scale[3], scale[2], 20, 10) + 10.0
# High part of normal fat
elif self.bodyfat <= scale[2]:
return self.getMalus(self.bodyfat, scale[2], best, 3, 9) + 3.0
# Very low in fat
elif self.bodyfat < scale[0]:
return self.getMalus(self.bodyfat, 1.0, scale[0], 3, 10) + 3.0
def getMuscleDeductScore(self):
scale = self.scales.getMuscleMassScale()
# For some reason, there's code to return self.calculate(muscle, normal[0], normal[0]+2.0, 3, 5) + 3.0
# if your muscle is between normal[0] and normal[0] + 2.0, but it's overwritten with 0.0 before return
if self.muscle >= scale[0]:
return 0.0
elif self.muscle < (scale[0] - 5.0):
return 10.0
else:
return self.getMalus(self.muscle, scale[0] - 5.0, scale[0], 10, 5) + 5.0
# No malus = normal or good; maximum malus (10.0) = less than normal-5.0;
# malus = between 5 and 10, on your water being between normal-5.0 and normal
def getWaterDeductScore(self):
scale = self.scales.getWaterPercentageScale()
if self.water >= scale[0]:
return 0.0
elif self.water <= (scale[0] - 5.0):
return 10.0
else:
return self.getMalus(self.water, scale[0] - 5.0, scale[0], 10, 5) + 5.0
# No malus = normal; maximum malus (15.0) = very high; malus = between 10 and 15
# with your visceral fat in your high range
def getVisceralFatDeductScore(self):
scale = self.scales.getVisceralFatScale()
if self.visceral_fat < scale[0]:
# For some reason, the original app would try to
# return 3.0 if vfat == 8 and 5.0 if vfat == 9
# but i's overwritten with 0.0 anyway before return
return 0.0
elif self.visceral_fat >= scale[1]:
return 15.0
else:
return self.getMalus(self.visceral_fat, scale[1], scale[0], 15, 10) + 10.0
def getBoneDeductScore(self):
scale = self.scales.getBoneMassScale()
if self.bone >= scale[0]:
return 0.0
elif self.bone <= (scale[0] - 0.3):
return 10.0
else:
return self.getMalus(self.bone, scale[0] - 0.3, scale[0], 10, 5) + 5.0
def getBasalMetabolismDeductScore(self):
# Get normal BMR
normal = self.scales.getBMRScale()[0]
if self.basal_metabolism >= normal:
return 0.0
elif self.basal_metabolism <= (normal - 300):
return 6.0
else:
# It's really + 5.0 in the app, but it's probably a mistake, should be 3.0
return self.getMalus(self.basal_metabolism, normal - 300, normal, 6, 3) + 5.0
# Get protein percentage malus
def getProteinDeductScore(self):
# low: 10,16; normal: 16,17
# Check limits
if self.protein > 17.0:
return 0.0
elif self.protein < 10.0:
return 10.0
else:
# Return values for low proteins or normal proteins
if self.protein <= 16.0:
return self.getMalus(self.protein, 10.0, 16.0, 10, 5) + 5.0
elif self.protein <= 17.0:
return self.getMalus(self.protein, 16.0, 17.0, 5, 3) + 3.0