Add support for docker containers, mqtt prefix, environment variables for config, and pip requirements
This commit is contained in:
parent
9d55f12105
commit
a54c3b133e
10 changed files with 219 additions and 44 deletions
183
src/Xiaomi_Scale.py
Normal file
183
src/Xiaomi_Scale.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
#############################################################################################
|
||||
# Code to read weight measurements fom Xiaomi Scale V2
|
||||
# (Framework is prsent to also read from Xiaomi Scale V1, though I do not own one to test so code has not been maintained)
|
||||
# Must be executed with Python 3 else body measurements are incorrect.
|
||||
# Must be executed as root, therefore best to schedule via crontab every 5 min (so as not to drain the battery):
|
||||
# */5 * * * * python3 /path-to-script/Xiaomi_Scale.py
|
||||
# Multi user possible as long as weitghs do not overlap, see lines 117-131
|
||||
#
|
||||
# Thanks to @syssi (https://gist.github.com/syssi/4108a54877406dc231d95514e538bde9) and @prototux (https://github.com/wiecosystem/Bluetooth) for their initial code
|
||||
#
|
||||
# Make sure you set your MQTT credentials below and user logic/data through the environment variables
|
||||
#
|
||||
#############################################################################################
|
||||
|
||||
|
||||
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import binascii
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
from bluepy import btle
|
||||
import paho.mqtt.client as mqtt
|
||||
from datetime import datetime
|
||||
|
||||
import Xiaomi_Scale_Body_Metrics
|
||||
|
||||
# Configuraiton...
|
||||
MISCALE_MAC = os.getenv('MISCALE_MAC', '')
|
||||
MQTT_USERNAME = os.getenv('MQTT_USERNAME', '')
|
||||
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD', '')
|
||||
MQTT_HOST = os.getenv('MQTT_HOST', '127.0.0.1')
|
||||
MQTT_PORT = os.getenv('MQTT_PORT', 1883)
|
||||
MQTT_TIMEOUT = os.getenv('MQTT_TIMEOUT', 60)
|
||||
MQTT_PREFIX = os.getenv('MQTT_PREFIX', '')
|
||||
|
||||
# User Variables...
|
||||
|
||||
USER1_GT = int(os.getenv('USER1_GT', '70')) # If the weight is greater than this number, we'll assume that we're weighing User #1
|
||||
USER1_SEX = os.getenv('USER1_SEX', 'male')
|
||||
USER1_NAME = os.getenv('USER1_NAME', 'David') # Name of the user
|
||||
USER1_HEIGHT = int(os.getenv('USER1_HEIGHT', '175')) # Height (in cm) of the user
|
||||
USER1_DOB = os.getenv('USER1_DOB', '1988-09-30') # DOB (in yyyy-mm-dd format)
|
||||
|
||||
USER2_LT = int(os.getenv('USER2_LT', '55')) # If the weight is less than this number, we'll assume that we're weighing User #2
|
||||
USER2_SEX = os.getenv('USER2_SEX', 'female')
|
||||
USER2_NAME = os.getenv('USER2_NAME', 'Joanne') # Name of the user
|
||||
USER2_HEIGHT = int(os.getenv('USER2_HEIGHT', '155')) # Height (in cm) of the user
|
||||
USER2_DOB = os.getenv('USER2_DOB', '1988-10-20') # DOB (in yyyy-mm-dd format)
|
||||
|
||||
USER3_SEX = os.getenv('USER3_SEX', 'male')
|
||||
USER3_NAME = os.getenv('USER3_NAME', 'Unknown User') # Name of the user
|
||||
USER3_HEIGHT = int(os.getenv('USER3_HEIGHT', '175')) # Height (in cm) of the user
|
||||
USER3_DOB = os.getenv('USER3_DOB', '1988-01-01') # DOB (in yyyy-mm-dd format)
|
||||
|
||||
|
||||
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):
|
||||
self.mqtt_client = None
|
||||
self.connected = False
|
||||
self._start_client()
|
||||
|
||||
def handleDiscovery(self, dev, isNewDev, isNewData):
|
||||
if dev.addr == MISCALE_MAC.lower() and isNewDev:
|
||||
# print (' Device: %s (%s), %d dBm %s. ' %
|
||||
# (
|
||||
# ANSI_WHITE + dev.addr + ANSI_OFF,
|
||||
# dev.addrType,
|
||||
# dev.rssi,
|
||||
# ('' if dev.connectable else '(not connectable)'))
|
||||
# , end='')
|
||||
for (sdid, desc, data) in dev.getScanData():
|
||||
### 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:
|
||||
print('')
|
||||
self._publish(round(measured, 2), unit, "", "")
|
||||
else:
|
||||
print("Scale is sleeping.")
|
||||
|
||||
### Xiaomi V2 Scale ###
|
||||
if data.startswith('1b18') and sdid == 22:
|
||||
measunit = data[4:6]
|
||||
measured = int((data[28:30] + data[26:28]), 16) * 0.01
|
||||
unit = ''
|
||||
|
||||
if measunit == "03": unit = 'lbs'
|
||||
if measunit == "02": unit = 'kg' ; measured = measured / 2
|
||||
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")
|
||||
miimpedance = str(int((data[24:26] + data[22:24]), 16))
|
||||
|
||||
|
||||
|
||||
if unit:
|
||||
print('')
|
||||
self._publish(round(measured, 2), unit, str(mitdatetime), miimpedance)
|
||||
else:
|
||||
print("Scale is sleeping.")
|
||||
|
||||
|
||||
if not dev.scanData:
|
||||
print ('\t(no data)')
|
||||
print
|
||||
|
||||
def _start_client(self):
|
||||
self.mqtt_client = mqtt.Client()
|
||||
self.mqtt_client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
|
||||
|
||||
def _on_connect(client, _, flags, return_code):
|
||||
self.connected = True
|
||||
#print("MQTT connection returned result: %s" % mqtt.connack_string(return_code))
|
||||
|
||||
self.mqtt_client.on_connect = _on_connect
|
||||
|
||||
self.mqtt_client.connect(MQTT_HOST, MQTT_PORT, MQTT_TIMEOUT)
|
||||
self.mqtt_client.loop_start()
|
||||
|
||||
def _publish(self, weight, unit, mitdatetime, miimpedance):
|
||||
if not self.connected:
|
||||
raise Exception('not connected to MQTT server')
|
||||
if int(weight) > USER1_GT:
|
||||
user = USER1_NAME
|
||||
height = USER1_HEIGHT
|
||||
age = self.GetAge(USER1_DOB)
|
||||
sex = USER1_SEX
|
||||
elif int(weight) < USER2_LT:
|
||||
user = USER2_NAME
|
||||
height = USER2_HEIGHT
|
||||
age = self.GetAge(USER2_DOB)
|
||||
sex = USER2_SEX
|
||||
else:
|
||||
user = USER3_NAME
|
||||
height = USER3_HEIGHT
|
||||
age = self.GetAge(USER3_DOB)
|
||||
sex = USER3_SEX
|
||||
lib = Xiaomi_Scale_Body_Metrics.bodyMetrics(weight, height, age, sex, 0)
|
||||
message = '{'
|
||||
message += '"Weight":"' + "{:.2f}".format(weight) + '"'
|
||||
message += ',"BMI":"' + "{:.2f}".format(lib.getBMI()) + '"'
|
||||
message += ',"Basal Metabolism":"' + "{:.2f}".format(lib.getBMR()) + '"'
|
||||
message += ',"Visceral Fat":"' + "{:.2f}".format(lib.getVisceralFat()) + '"'
|
||||
|
||||
if miimpedance:
|
||||
lib = Xiaomi_Scale_Body_Metrics.bodyMetrics(weight, height, age, sex, int(miimpedance))
|
||||
message += ',"Lean Body Mass":"' + "{:.2f}".format(lib.getLBMCoefficient()) + '"'
|
||||
message += ',"Body Fat":"' + "{:.2f}".format(lib.getFatPercentage()) + '"'
|
||||
message += ',"Water":"' + "{:.2f}".format(lib.getWaterPercentage()) + '"'
|
||||
message += ',"Bone Mass":"' + "{:.2f}".format(lib.getBoneMass()) + '"'
|
||||
message += ',"Muscle Mass":"' + "{:.2f}".format(lib.getMuscleMass()) + '"'
|
||||
message += ',"Protein":"' + "{:.2f}".format(lib.getProteinPercentage()) + '"'
|
||||
self.mqtt_client.publish(MQTT_PREFIX + '/' + user, weight, qos=1, retain=True)
|
||||
|
||||
message += ',"TimeStamp":"' + mitdatetime + '"'
|
||||
message += '}'
|
||||
self.mqtt_client.publish(MQTT_PREFIX + '/' + user + '/weight', message, qos=1, retain=True)
|
||||
print('\tSent data to topic %s: %s' % (MQTT_PREFIX + '/' + user + '/weight', message))
|
||||
|
||||
def main():
|
||||
|
||||
# while(True):
|
||||
scanner = btle.Scanner().withDelegate(ScanProcessor())
|
||||
|
||||
devices = scanner.scan(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
277
src/Xiaomi_Scale_Body_Metrics.py
Normal file
277
src/Xiaomi_Scale_Body_Metrics.py
Normal file
|
@ -0,0 +1,277 @@
|
|||
from math import floor
|
||||
|
||||
# Reverse engineered from the Mi Body Composition Scale's library, could also be used on some other scales such as iHealth
|
||||
|
||||
class bodyMetrics:
|
||||
def __init__(self, weight, height, age, sex, impedance):
|
||||
self.weight = weight
|
||||
self.height = height
|
||||
self.age = age
|
||||
self.sex = sex
|
||||
self.impedance = impedance
|
||||
|
||||
# Check for potential out of boundaries
|
||||
if self.height > 220:
|
||||
raise Exception("Height is too high (limit: >220cm)")
|
||||
elif weight < 10 or weight > 200:
|
||||
raise Exception("Weight is either too low or too high (limits: <10kg and >200kg)")
|
||||
elif age > 99:
|
||||
raise Exception("Age is too high (limit >99 years)")
|
||||
elif impedance > 3000:
|
||||
raise Exception("Impedance is too high (limit >3000ohm)")
|
||||
|
||||
# Set the value to a boundary if it overflows
|
||||
def checkValueOverflow(self, value, minimum, maximum):
|
||||
if value < minimum:
|
||||
return minimum
|
||||
elif value > maximum:
|
||||
return maximum
|
||||
else:
|
||||
return value
|
||||
|
||||
# Get LBM coefficient (with impedance)
|
||||
def getLBMCoefficient(self):
|
||||
lbm = (self.height * 9.058 / 100) * (self.height / 100)
|
||||
lbm += self.weight * 0.32 + 12.226
|
||||
lbm -= self.impedance * 0.0068
|
||||
lbm -= self.age * 0.0542
|
||||
return lbm
|
||||
|
||||
# Get BMR
|
||||
def getBMR(self):
|
||||
if self.sex == 'female':
|
||||
bmr = 864.6 + self.weight * 10.2036
|
||||
bmr -= self.height * 0.39336
|
||||
bmr -= self.age * 6.204
|
||||
else:
|
||||
bmr = 877.8 + self.weight * 14.916
|
||||
bmr -= self.height * 0.726
|
||||
bmr -= self.age * 8.976
|
||||
|
||||
# Capping
|
||||
if self.sex == 'female' and bmr > 2996:
|
||||
bmr = 5000
|
||||
elif self.sex == 'male' and bmr > 2322:
|
||||
bmr = 5000
|
||||
return self.checkValueOverflow(bmr, 500, 10000)
|
||||
|
||||
# Get BMR scale
|
||||
def getBMRScale(self):
|
||||
coefficients = {
|
||||
'female': {12: 34, 15: 29, 17: 24, 29: 22, 50: 20, 120: 19},
|
||||
'male': {12: 36, 15: 30, 17: 26, 29: 23, 50: 21, 120: 20}
|
||||
}
|
||||
|
||||
for age, coefficient in coefficients[self.sex].items():
|
||||
if self.age < age:
|
||||
return [self.weight * coefficient]
|
||||
break
|
||||
|
||||
# Get fat percentage
|
||||
def getFatPercentage(self):
|
||||
# Set a constant to remove from LBM
|
||||
if self.sex == 'female' and self.age <= 49:
|
||||
const = 9.25
|
||||
elif self.sex == 'female' and self.age > 49:
|
||||
const = 7.25
|
||||
else:
|
||||
const = 0.8
|
||||
|
||||
# Calculate body fat percentage
|
||||
LBM = self.getLBMCoefficient()
|
||||
|
||||
if self.sex == 'male' and self.weight < 61:
|
||||
coefficient = 0.98
|
||||
elif self.sex == 'female' and self.weight > 60:
|
||||
coefficient = 0.96
|
||||
if self.height > 160:
|
||||
coefficient *= 1.03
|
||||
elif self.sex == 'female' and self.weight < 50:
|
||||
coefficient = 1.02
|
||||
if self.height > 160:
|
||||
coefficient *= 1.03
|
||||
else:
|
||||
coefficient = 1.0
|
||||
fatPercentage = (1.0 - (((LBM - const) * coefficient) / self.weight)) * 100
|
||||
|
||||
# Capping body fat percentage
|
||||
if fatPercentage > 63:
|
||||
fatPercentage = 75
|
||||
return self.checkValueOverflow(fatPercentage, 5, 75)
|
||||
|
||||
# Get fat percentage scale
|
||||
def getFatPercentageScale(self):
|
||||
# The included tables where quite strange, maybe bogus, replaced them with better ones...
|
||||
scales = [
|
||||
{'min': 0, 'max': 20, 'female': [18, 23, 30, 35], 'male': [8, 14, 21, 25]},
|
||||
{'min': 21, 'max': 25, 'female': [19, 24, 30, 35], 'male': [10, 15, 22, 26]},
|
||||
{'min': 26, 'max': 30, 'female': [20, 25, 31, 36], 'male': [11, 16, 21, 27]},
|
||||
{'min': 31, 'max': 35, 'female': [21, 26, 33, 36], 'male': [13, 17, 25, 28]},
|
||||
{'min': 46, 'max': 40, 'female': [22, 27, 34, 37], 'male': [15, 20, 26, 29]},
|
||||
{'min': 41, 'max': 45, 'female': [23, 28, 35, 38], 'male': [16, 22, 27, 30]},
|
||||
{'min': 46, 'max': 50, 'female': [24, 30, 36, 38], 'male': [17, 23, 29, 31]},
|
||||
{'min': 51, 'max': 55, 'female': [26, 31, 36, 39], 'male': [19, 25, 30, 33]},
|
||||
{'min': 56, 'max': 100, 'female': [27, 32, 37, 40], 'male': [21, 26, 31, 34]},
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.age >= scale['min'] and self.age <= scale['max']:
|
||||
return scale[self.sex]
|
||||
|
||||
# Get water percentage
|
||||
def getWaterPercentage(self):
|
||||
waterPercentage = (100 - self.getFatPercentage()) * 0.7
|
||||
|
||||
if (waterPercentage <= 50):
|
||||
coefficient = 1.02
|
||||
else:
|
||||
coefficient = 0.98
|
||||
|
||||
# Capping water percentage
|
||||
if waterPercentage * coefficient >= 65:
|
||||
waterPercentage = 75
|
||||
return self.checkValueOverflow(waterPercentage * coefficient, 35, 75)
|
||||
|
||||
# Get water percentage scale
|
||||
def getWaterPercentageScale(self):
|
||||
return [53, 67]
|
||||
|
||||
# Get bone mass
|
||||
def getBoneMass(self):
|
||||
if self.sex == 'female':
|
||||
base = 0.245691014
|
||||
else:
|
||||
base = 0.18016894
|
||||
|
||||
boneMass = (base - (self.getLBMCoefficient() * 0.05158)) * -1
|
||||
|
||||
if boneMass > 2.2:
|
||||
boneMass += 0.1
|
||||
else:
|
||||
boneMass -= 0.1
|
||||
|
||||
# Capping boneMass
|
||||
if self.sex == 'female' and boneMass > 5.1:
|
||||
boneMass = 8
|
||||
elif self.sex == 'male' and boneMass > 5.2:
|
||||
boneMass = 8
|
||||
return self.checkValueOverflow(boneMass, 0.5 , 8)
|
||||
|
||||
# Get bone mass scale
|
||||
def getBoneMassScale(self):
|
||||
scales = [
|
||||
{'female': {'min': 60, 'optimal': 2.5}, 'male': {'min': 75, 'optimal': 3.2}},
|
||||
{'female': {'min': 45, 'optimal': 2.2}, 'male': {'min': 69, 'optimal': 2.9}},
|
||||
{'female': {'min': 0, 'optimal': 1.8}, 'male': {'min': 0, 'optimal': 2.5}}
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.weight >= scale[self.sex]['min']:
|
||||
return [scale[self.sex]['optimal']-1, scale[self.sex]['optimal']+1]
|
||||
|
||||
# Get muscle mass
|
||||
def getMuscleMass(self):
|
||||
muscleMass = self.weight - ((self.getFatPercentage() * 0.01) * self.weight) - self.getBoneMass()
|
||||
|
||||
# Capping muscle mass
|
||||
if self.sex == 'female' and muscleMass >= 84:
|
||||
muscleMass = 120
|
||||
elif self.sex == 'male' and muscleMass >= 93.5:
|
||||
muscleMass = 120
|
||||
|
||||
return self.checkValueOverflow(muscleMass, 10 ,120)
|
||||
|
||||
# Get muscle mass scale
|
||||
def getMuscleMassScale(self):
|
||||
scales = [
|
||||
{'min': 170, 'female': [36.5, 42.5], 'male': [49.5, 59.4]},
|
||||
{'min': 160, 'female': [32.9, 37.5], 'male': [44.0, 52.4]},
|
||||
{'min': 0, 'female': [29.1, 34.7], 'male': [38.5, 46.5]}
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.height >= scale['min']:
|
||||
return scale[self.sex]
|
||||
|
||||
# Get Visceral Fat
|
||||
def getVisceralFat(self):
|
||||
if self.sex == 'female':
|
||||
if self.weight > (13 - (self.height * 0.5)) * -1:
|
||||
subsubcalc = ((self.height * 1.45) + (self.height * 0.1158) * self.height) - 120
|
||||
subcalc = self.weight * 500 / subsubcalc
|
||||
vfal = (subcalc - 6) + (self.age * 0.07)
|
||||
else:
|
||||
subcalc = 0.691 + (self.height * -0.0024) + (self.height * -0.0024)
|
||||
vfal = (((self.height * 0.027) - (subcalc * self.weight)) * -1) + (self.age * 0.07) - self.age
|
||||
else:
|
||||
if self.height < self.weight * 1.6:
|
||||
subcalc = ((self.height * 0.4) - (self.height * (self.height * 0.0826))) * -1
|
||||
vfal = ((self.weight * 305) / (subcalc + 48)) - 2.9 + (self.age * 0.15)
|
||||
else:
|
||||
subcalc = 0.765 + self.height * -0.0015
|
||||
vfal = (((self.height * 0.143) - (self.weight * subcalc)) * -1) + (self.age * 0.15) - 5.0
|
||||
|
||||
return self.checkValueOverflow(vfal, 1 ,50)
|
||||
|
||||
# Get visceral fat scale
|
||||
def getVisceralFatScale(self):
|
||||
return [10, 15]
|
||||
|
||||
# Get BMI
|
||||
def getBMI(self):
|
||||
return self.checkValueOverflow(self.weight/((self.height/100)*(self.height/100)), 10, 90)
|
||||
|
||||
# Get BMI scale
|
||||
def getBMIScale(self):
|
||||
# Replaced library's version by mi fit scale, it seems better
|
||||
return [18.5, 25, 28, 32]
|
||||
|
||||
# Get ideal weight (just doing a reverse BMI, should be something better)
|
||||
def getIdealWeight(self):
|
||||
return self.checkValueOverflow((22*self.height)*self.height/10000, 5.5, 198)
|
||||
|
||||
# Get ideal weight scale (BMI scale converted to weights)
|
||||
def getIdealWeightScale(self):
|
||||
scale = []
|
||||
for bmiScale in self.getBMIScale():
|
||||
scale.append((bmiScale*self.height)*self.height/10000)
|
||||
return scale
|
||||
|
||||
# Get fat mass to ideal (guessing mi fit formula)
|
||||
def getFatMassToIdeal(self):
|
||||
mass = (self.weight * (self.getFatPercentage() / 100)) - (self.weight * (self.getFatPercentageScale()[2] / 100))
|
||||
if mass < 0:
|
||||
return {'type': 'to_gain', 'mass': mass*-1}
|
||||
else:
|
||||
return {'type': 'to_lose', 'mass': mass}
|
||||
|
||||
# Get protetin percentage (warn: guessed formula)
|
||||
def getProteinPercentage(self):
|
||||
proteinPercentage = 100 - (floor(self.getFatPercentage() * 100) / 100)
|
||||
proteinPercentage -= floor(self.getWaterPercentage() * 100) / 100
|
||||
proteinPercentage -= floor((self.getBoneMass()/self.weight*100) * 100) / 100
|
||||
return proteinPercentage
|
||||
|
||||
# Get protein scale (hardcoded in mi fit)
|
||||
def getProteinPercentageScale(self):
|
||||
return [16, 20]
|
||||
|
||||
# Get body type (out of nine possible)
|
||||
def getBodyType(self):
|
||||
if self.getFatPercentage() > self.getFatPercentageScale()[2]:
|
||||
factor = 0
|
||||
elif self.getFatPercentage() < self.getFatPercentageScale()[1]:
|
||||
factor = 2
|
||||
else:
|
||||
factor = 1
|
||||
|
||||
if self.getMuscleMass() > self.getMuscleMassScale()[1]:
|
||||
return 2 + (factor * 3)
|
||||
elif self.getMuscleMass() < self.getMuscleMassScale()[0]:
|
||||
return (factor * 3)
|
||||
else:
|
||||
return 1 + (factor * 3)
|
||||
|
||||
# Return body type scale
|
||||
def getBodyTypeScale(self):
|
||||
return ['obese', 'overweight', 'thick-set', 'lack-exerscise', 'balanced', 'balanced-muscular', 'skinny', 'balanced-skinny', 'skinny-muscular']
|
2
src/requirements.txt
Normal file
2
src/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
bluepy==1.3.0
|
||||
paho-mqtt==1.4.0
|
32
src/wrapper.sh
Normal file
32
src/wrapper.sh
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
export MISCALE_MAC=00:00:00:00:00:00 # Mac address of your scale
|
||||
export MQTT_PREFIX=miScale
|
||||
#export MQTT_HOST=<YOUR-IP> # MQTT Server (defaults to 127.0.0.1)
|
||||
#export MQTT_USERNAME= # Username for MQTT server (comment out if not required)
|
||||
#export MQTT_PASSWORD= # Password for MQTT (comment out if not required)
|
||||
#export MQTT_PORT= # Defaults to 1883
|
||||
#export MQTT_TIMEOUT=30 # Defaults to 60
|
||||
|
||||
# Auto-gender selection/config -- This is used to create the calculations such as BMI, Water/Bone Mass etc...
|
||||
# Multi user possible as long as weitghs do not overlap!
|
||||
|
||||
export USER1_GT=70 # If the weight is greater than this number, we'll assume that we're weighing User #1
|
||||
export USER1_SEX=male
|
||||
export USER1_NAME=Jo # Name of the user
|
||||
export USER1_HEIGHT=175 # Height (in cm) of the user
|
||||
export USER1_DOB=1990-01-01 # DOB (in yyyy-mm-dd format)
|
||||
|
||||
export USER2_LT=35 # If the weight is less than this number, we'll assume that we're weighing User #2
|
||||
export USER2_SEX=female
|
||||
export USER2_NAME=Sarah # Name of the user
|
||||
export USER2_HEIGHT=95 # Height (in cm) of the user
|
||||
export USER2_DOB=1990-01-01 # DOB (in yyyy-mm-dd format)
|
||||
|
||||
export USER3_SEX=female
|
||||
export USER3_NAME=Missy # Name of the user
|
||||
export USER3_HEIGHT=150 # Height (in cm) of the user
|
||||
export USER3_DOB=1990-01-01 # DOB (in yyyy-mm-dd format)
|
||||
|
||||
MY_PWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
python3 $MY_PWD/Xiaomi_Scale.py
|
Loading…
Add table
Add a link
Reference in a new issue