add-on repo
@ -1,100 +1,100 @@
|
||||
# Xiaomi Mi Scale Add On for Home Assistant
|
||||
|
||||
Add-On for [HomeAssistant](https://www.home-assistant.io/) to read weight measurements from Xiaomi Body Scales.
|
||||
|
||||
## Supported Scales:
|
||||
Name | Model | Picture
|
||||
--- | --- | :---:
|
||||
[Mi Smart Scale 2](https://www.mi.com/global/scale) | XMTZC04HM | ![Mi Scale_2](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Smart_Scale_2_Thumb.png)
|
||||
[Mi Body Composition Scale](https://www.mi.com/global/mi-body-composition-scale/) | XMTZC02HM | ![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Body_Composition_Scale_Thumb.png)
|
||||
[Mi Body Composition Scale 2](https://c.mi.com/thread-2289389-1-0.html) | XMTZC05HM | ![Mi Body Composition Scale 2](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Body_Composition_Scale_2_Thumb.png)
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
1. Retrieve the scale's MAC Address from the Xiaomi Mi Fit App:
|
||||
![MAC Address](Screenshots/MAC_Address.png)
|
||||
|
||||
2. Clone this repository
|
||||
`git clone https://github.com/lolouk44/xiaomi_mi_scale_ha_add_on`
|
||||
|
||||
3. Create a new directory xiaomi_mi_scale in the folder addons in your Home Assistant installation and place all files in it via SSH / Samba
|
||||
|
||||
![Add-On](Screenshots/addon.png)
|
||||
|
||||
4. Open Home Assistant and navigate to add-on store and clock the reload button on the top right corner. Now you should see the Xiaomi Mi Scale as a local add-on
|
||||
![Add-On Store](Screenshots/addon_store.png)
|
||||
|
||||
5. Install the add-on (takes a while as the container is built locally)
|
||||
|
||||
6. Edit the Configuration
|
||||
|
||||
|
||||
Option | Type | Required | Description
|
||||
--- | --- | --- | ---
|
||||
HCI_DEV | string | No | Bluetooth hci device to use. Defaults to hci0
|
||||
MISCALE_MAC | string | Yes | Mac address of your scale
|
||||
MQTT_PREFIX | string | No | MQTT Topic Prefix. Defaults to miscale
|
||||
MQTT_HOST | string | Yes | MQTT Server (defaults to 127.0.0.1)
|
||||
MQTT_USERNAME | string | No | Username for MQTT server (comment out if not required)
|
||||
MQTT_PASSWORD | string | No | Password for MQTT (comment out if not required)
|
||||
MQTT_PORT | int | No | Defaults to 1883
|
||||
TIME_INTERVAL | int | No | Time in sec between each query to the scale, to allow other applications to use the Bluetooth module. Defaults to 30
|
||||
MQTT_DISCOVERY | bool | No | MQTT Discovery for Home Assistant Defaults to true
|
||||
MQTT_DISCOVERY_PREFIX | string | No | MQTT Discovery Prefix for Home Assistant. Defaults to homeassistant
|
||||
|
||||
|
||||
Auto-gender selection/config -- This is used to create the calculations such as BMI, Water/Bone Mass etc...
|
||||
Up to 3 users possible as long as weights do not overlap!
|
||||
|
||||
Option | Type | Required | Description
|
||||
--- | --- | --- | ---
|
||||
USER1_GT | int | Yes | If the weight is greater than this number, we'll assume that we're weighing User #1
|
||||
USER1_SEX | string | Yes | male / female
|
||||
USER1_NAME | string | Yes | Name of the user
|
||||
USER1_HEIGHT | int | Yes | Height (in cm) of the user
|
||||
USER1_DOB | string | Yes | DOB (in yyyy-mm-dd format)
|
||||
USER2_LT | int | No | If the weight is less than this number, we'll assume that we're weighing User #2
|
||||
USER2_SEX | string | No | male / female
|
||||
USER2_NAME | string | No | Name of the user
|
||||
USER2_HEIGHT | int | No |Height (in cm) of the user
|
||||
USER2_DOB | string | No | DOB (in yyyy-mm-dd format)
|
||||
USER3_SEX | string | No | male / female
|
||||
USER3_NAME | string | No | Name of the user
|
||||
USER3_HEIGHT | int | No |Height (in cm) of the user
|
||||
USER3_DOB | string | No | DOB (in yyyy-mm-dd format)
|
||||
|
||||
|
||||
7. Start the add-on
|
||||
|
||||
|
||||
## Home-Assistant Setup:
|
||||
Under the `sensor` block, enter as many blocks as users configured in your environment variables:
|
||||
|
||||
```yaml
|
||||
- platform: mqtt
|
||||
name: "Example Name Weight"
|
||||
state_topic: "miScale/USER_NAME/weight"
|
||||
value_template: "{{ value_json['Weight'] }}"
|
||||
unit_of_measurement: "kg"
|
||||
json_attributes_topic: "miScale/USER_NAME/weight"
|
||||
icon: mdi:scale-bathroom
|
||||
|
||||
- platform: mqtt
|
||||
name: "Example Name BMI"
|
||||
state_topic: "miScale/USER_NAME/weight"
|
||||
value_template: "{{ value_json['BMI'] }}"
|
||||
icon: mdi:human-pregnant
|
||||
|
||||
```
|
||||
|
||||
![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/HA_Lovelace_Card.png)
|
||||
|
||||
![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/HA_Lovelace_Card_Details.png)
|
||||
|
||||
## Acknowledgements:
|
||||
Thanks to @syssi (https://gist.github.com/syssi/4108a54877406dc231d95514e538bde9) and @prototux (https://github.com/wiecosystem/Bluetooth) for their initial code
|
||||
|
||||
Special thanks to [@ned-kelly](https://github.com/ned-kelly) for his help turning a "simple" python script into a fully fledged docker container
|
||||
|
||||
Thanks to [@bpaulin](https://github.com/bpaulin) for his PRs and collaboration
|
||||
# Xiaomi Mi Scale Add On for Home Assistant
|
||||
|
||||
Add-On for [HomeAssistant](https://www.home-assistant.io/) to read weight measurements from Xiaomi Body Scales.
|
||||
|
||||
## Supported Scales:
|
||||
Name | Model | Picture
|
||||
--- | --- | :---:
|
||||
[Mi Smart Scale 2](https://www.mi.com/global/scale) | XMTZC04HM | ![Mi Scale_2](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Smart_Scale_2_Thumb.png)
|
||||
[Mi Body Composition Scale](https://www.mi.com/global/mi-body-composition-scale/) | XMTZC02HM | ![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Body_Composition_Scale_Thumb.png)
|
||||
[Mi Body Composition Scale 2](https://c.mi.com/thread-2289389-1-0.html) | XMTZC05HM | ![Mi Body Composition Scale 2](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/Mi_Body_Composition_Scale_2_Thumb.png)
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
1. Retrieve the scale's MAC Address from the Xiaomi Mi Fit App:
|
||||
![MAC Address](Screenshots/MAC_Address.png)
|
||||
|
||||
2. Clone this repository
|
||||
`git clone https://github.com/lolouk44/xiaomi_mi_scale_ha_add_on`
|
||||
|
||||
3. Create a new directory xiaomi_mi_scale in the folder addons in your Home Assistant installation and place all files in it via SSH / Samba
|
||||
|
||||
![Add-On](Screenshots/addon.png)
|
||||
|
||||
4. Open Home Assistant and navigate to add-on store and clock the reload button on the top right corner. Now you should see the Xiaomi Mi Scale as a local add-on
|
||||
![Add-On Store](Screenshots/addon_store.png)
|
||||
|
||||
5. Install the add-on (takes a while as the container is built locally)
|
||||
|
||||
6. Edit the Configuration
|
||||
|
||||
|
||||
Option | Type | Required | Description
|
||||
--- | --- | --- | ---
|
||||
HCI_DEV | string | No | Bluetooth hci device to use. Defaults to hci0
|
||||
MISCALE_MAC | string | Yes | Mac address of your scale
|
||||
MQTT_PREFIX | string | No | MQTT Topic Prefix. Defaults to miscale
|
||||
MQTT_HOST | string | Yes | MQTT Server (defaults to 127.0.0.1)
|
||||
MQTT_USERNAME | string | No | Username for MQTT server (comment out if not required)
|
||||
MQTT_PASSWORD | string | No | Password for MQTT (comment out if not required)
|
||||
MQTT_PORT | int | No | Defaults to 1883
|
||||
TIME_INTERVAL | int | No | Time in sec between each query to the scale, to allow other applications to use the Bluetooth module. Defaults to 30
|
||||
MQTT_DISCOVERY | bool | No | MQTT Discovery for Home Assistant Defaults to true
|
||||
MQTT_DISCOVERY_PREFIX | string | No | MQTT Discovery Prefix for Home Assistant. Defaults to homeassistant
|
||||
|
||||
|
||||
Auto-gender selection/config -- This is used to create the calculations such as BMI, Water/Bone Mass etc...
|
||||
Up to 3 users possible as long as weights do not overlap!
|
||||
|
||||
Option | Type | Required | Description
|
||||
--- | --- | --- | ---
|
||||
USER1_GT | int | Yes | If the weight is greater than this number, we'll assume that we're weighing User #1
|
||||
USER1_SEX | string | Yes | male / female
|
||||
USER1_NAME | string | Yes | Name of the user
|
||||
USER1_HEIGHT | int | Yes | Height (in cm) of the user
|
||||
USER1_DOB | string | Yes | DOB (in yyyy-mm-dd format)
|
||||
USER2_LT | int | No | If the weight is less than this number, we'll assume that we're weighing User #2. Defaults to USER1_GT Value
|
||||
USER2_SEX | string | No | male / female. Defaults to female
|
||||
USER2_NAME | string | No | Name of the user. Defaults to Serena
|
||||
USER2_HEIGHT | int | No |Height (in cm) of the user. Defaults to 95
|
||||
USER2_DOB | string | No | DOB (in yyyy-mm-dd format). Defaults to 1990-01-01
|
||||
USER3_SEX | string | No | male / female. Defaults to female
|
||||
USER3_NAME | string | No | Name of the user. Defaults to Missy
|
||||
USER3_HEIGHT | int | No |Height (in cm) of the user. Defaults to 150
|
||||
USER3_DOB | string | No | DOB (in yyyy-mm-dd format). Defaults to 1990-01-01
|
||||
|
||||
|
||||
7. Start the add-on
|
||||
|
||||
|
||||
## Home-Assistant Setup:
|
||||
Under the `sensor` block, enter as many blocks as users configured in your environment variables:
|
||||
|
||||
```yaml
|
||||
- platform: mqtt
|
||||
name: "Example Name Weight"
|
||||
state_topic: "miScale/USER_NAME/weight"
|
||||
value_template: "{{ value_json['Weight'] }}"
|
||||
unit_of_measurement: "kg"
|
||||
json_attributes_topic: "miScale/USER_NAME/weight"
|
||||
icon: mdi:scale-bathroom
|
||||
|
||||
- platform: mqtt
|
||||
name: "Example Name BMI"
|
||||
state_topic: "miScale/USER_NAME/weight"
|
||||
value_template: "{{ value_json['BMI'] }}"
|
||||
icon: mdi:human-pregnant
|
||||
|
||||
```
|
||||
|
||||
![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/HA_Lovelace_Card.png)
|
||||
|
||||
![Mi Scale](https://github.com/lolouk44/xiaomi_mi_scale/blob/master/Screenshots/HA_Lovelace_Card_Details.png)
|
||||
|
||||
## Acknowledgements:
|
||||
Thanks to @syssi (https://gist.github.com/syssi/4108a54877406dc231d95514e538bde9) and @prototux (https://github.com/wiecosystem/Bluetooth) for their initial code
|
||||
|
||||
Special thanks to [@ned-kelly](https://github.com/ned-kelly) for his help turning a "simple" python script into a fully fledged docker container
|
||||
|
||||
Thanks to [@bpaulin](https://github.com/bpaulin) for his PRs and collaboration
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -1,74 +1,74 @@
|
||||
{
|
||||
"name": "Xiaomi Mi Scale",
|
||||
"version": "0.1.6",
|
||||
"slug": "xiaomi_mi_scale",
|
||||
"description": "Read weight measurements from Xiamomi scale via BLE",
|
||||
"url": "https://github.com/lolouk44/xiaomi_mi_scale_ha_add_on",
|
||||
"image": "lolouk44/xiaomi-mi-scale",
|
||||
"arch": ["armhf", "armv7", "aarch64", "amd64", "i386"],
|
||||
"startup": "before",
|
||||
"boot": "auto",
|
||||
"panel_admin": false,
|
||||
"host_network": true,
|
||||
"privileged": ["NET_ADMIN", "SYS_ADMIN"],
|
||||
|
||||
"options": {
|
||||
"HCI_DEV": "hci0",
|
||||
"MISCALE_MAC": "00:00:00:00:00:00",
|
||||
"MQTT_PREFIX": "miScale",
|
||||
"MQTT_HOST": "192.168.0.1",
|
||||
"MQTT_USERNAME": "",
|
||||
"MQTT_PASSWORD": "",
|
||||
"MQTT_PORT": 1883,
|
||||
"TIME_INTERVAL": 30,
|
||||
"MQTT_DISCOVERY": true,
|
||||
"MQTT_DISCOVERY_PREFIX": "homeassistant",
|
||||
|
||||
"USER1_GT": 70,
|
||||
"USER1_SEX": "male",
|
||||
"USER1_NAME": "Jo",
|
||||
"USER1_HEIGHT": 175,
|
||||
"USER1_DOB": "1990-01-01",
|
||||
|
||||
"USER2_LT": 35,
|
||||
"USER2_SEX": "female",
|
||||
"USER2_NAME": "Serena",
|
||||
"USER2_HEIGHT": 95,
|
||||
"USER2_DOB": "1990-01-01",
|
||||
|
||||
"USER3_SEX": "female",
|
||||
"USER3_NAME": "Missy",
|
||||
"USER3_HEIGHT": 150,
|
||||
"USER3_DOB": "1990-01-01"
|
||||
|
||||
},
|
||||
"schema": {
|
||||
"HCI_DEV": "str",
|
||||
"MISCALE_MAC": "str",
|
||||
"MQTT_PREFIX": "str",
|
||||
"MQTT_HOST": "str",
|
||||
"MQTT_USERNAME": "str",
|
||||
"MQTT_PASSWORD": "str",
|
||||
"MQTT_PORT": "int",
|
||||
"TIME_INTERVAL": "int",
|
||||
"MQTT_DISCOVERY": "bool",
|
||||
"MQTT_DISCOVERY_PREFIX": "str",
|
||||
|
||||
"USER1_GT": "int",
|
||||
"USER1_SEX": "str",
|
||||
"USER1_NAME": "str",
|
||||
"USER1_HEIGHT": "int",
|
||||
"USER1_DOB": "str",
|
||||
|
||||
"USER2_LT": "int?",
|
||||
"USER2_SEX": "str?",
|
||||
"USER2_NAME": "str?",
|
||||
"USER2_HEIGHT": "int?",
|
||||
"USER2_DOB": "str?",
|
||||
|
||||
"USER3_SEX": "str?",
|
||||
"USER3_NAME": "str?",
|
||||
"USER3_HEIGHT": "int?",
|
||||
"USER3_DOB": "str?"
|
||||
}
|
||||
{
|
||||
"name": "Xiaomi Mi Scale",
|
||||
"version": "0.1.6",
|
||||
"slug": "xiaomi_mi_scale",
|
||||
"description": "Read weight measurements from Xiamomi scale via BLE",
|
||||
"url": "https://github.com/lolouk44/xiaomi_mi_scale_ha_add_on",
|
||||
"image": "lolouk44/xiaomi-mi-scale",
|
||||
"arch": ["armhf", "armv7", "aarch64", "amd64", "i386"],
|
||||
"startup": "before",
|
||||
"boot": "auto",
|
||||
"panel_admin": false,
|
||||
"host_network": true,
|
||||
"privileged": ["NET_ADMIN", "SYS_ADMIN"],
|
||||
|
||||
"options": {
|
||||
"HCI_DEV": "hci0",
|
||||
"MISCALE_MAC": "00:00:00:00:00:00",
|
||||
"MQTT_PREFIX": "miScale",
|
||||
"MQTT_HOST": "192.168.0.1",
|
||||
"MQTT_USERNAME": "user",
|
||||
"MQTT_PASSWORD": "passwd",
|
||||
"MQTT_PORT": 1883,
|
||||
"TIME_INTERVAL": 30,
|
||||
"MQTT_DISCOVERY": true,
|
||||
"MQTT_DISCOVERY_PREFIX": "homeassistant",
|
||||
|
||||
"USER1_GT": 70,
|
||||
"USER1_SEX": "male",
|
||||
"USER1_NAME": "Jo",
|
||||
"USER1_HEIGHT": 175,
|
||||
"USER1_DOB": "1990-01-01",
|
||||
|
||||
"USER2_LT": 35,
|
||||
"USER2_SEX": "female",
|
||||
"USER2_NAME": "Serena",
|
||||
"USER2_HEIGHT": 95,
|
||||
"USER2_DOB": "1990-01-01",
|
||||
|
||||
"USER3_SEX": "female",
|
||||
"USER3_NAME": "Missy",
|
||||
"USER3_HEIGHT": 150,
|
||||
"USER3_DOB": "1990-01-01"
|
||||
|
||||
},
|
||||
"schema": {
|
||||
"HCI_DEV": "str?",
|
||||
"MISCALE_MAC": "str",
|
||||
"MQTT_PREFIX": "str?",
|
||||
"MQTT_HOST": "str",
|
||||
"MQTT_USERNAME": "str?",
|
||||
"MQTT_PASSWORD": "str?",
|
||||
"MQTT_PORT": "int?",
|
||||
"TIME_INTERVAL": "int?",
|
||||
"MQTT_DISCOVERY": "bool?",
|
||||
"MQTT_DISCOVERY_PREFIX": "str?",
|
||||
|
||||
"USER1_GT": "int",
|
||||
"USER1_SEX": "str",
|
||||
"USER1_NAME": "str",
|
||||
"USER1_HEIGHT": "int",
|
||||
"USER1_DOB": "str",
|
||||
|
||||
"USER2_LT": "int?",
|
||||
"USER2_SEX": "str?",
|
||||
"USER2_NAME": "str?",
|
||||
"USER2_HEIGHT": "int?",
|
||||
"USER2_DOB": "str?",
|
||||
|
||||
"USER3_SEX": "str?",
|
||||
"USER3_NAME": "str?",
|
||||
"USER3_HEIGHT": "int?",
|
||||
"USER3_DOB": "str?"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
@ -1,242 +1,334 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import binascii
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from bluepy import btle
|
||||
from bluepy.btle import Scanner, BTLEDisconnectError, BTLEManagementError, DefaultDelegate
|
||||
import paho.mqtt.publish as publish
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
import Xiaomi_Scale_Body_Metrics
|
||||
|
||||
|
||||
|
||||
# First Log msg
|
||||
sys.stdout.write(' \n')
|
||||
sys.stdout.write('-------------------------------------\n')
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Starting Xiaomi mi Scale...\n")
|
||||
|
||||
# Configuraiton...
|
||||
# Trying To Load Config From options.json (HA Add-On)
|
||||
try:
|
||||
with open('/data/options.json') as json_file:
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Loading Config From Add-On Options...\n")
|
||||
data = json.load(json_file)
|
||||
MISCALE_MAC = data["MISCALE_MAC"]
|
||||
MQTT_USERNAME = None if(data["MQTT_USERNAME"] == "") else data["MQTT_USERNAME"]
|
||||
MQTT_PASSWORD = None if(data["MQTT_PASSWORD"] == "") else data["MQTT_PASSWORD"]
|
||||
MQTT_HOST = data["MQTT_HOST"]
|
||||
MQTT_PORT = int(data["MQTT_PORT"])
|
||||
MQTT_PREFIX = data["MQTT_PREFIX"]
|
||||
TIME_INTERVAL = int(data["TIME_INTERVAL"])
|
||||
MQTT_DISCOVERY = data["MQTT_DISCOVERY"]
|
||||
MQTT_DISCOVERY_PREFIX = data["MQTT_DISCOVERY_PREFIX"]
|
||||
HCI_DEV = data["HCI_DEV"][-1]
|
||||
|
||||
# User Variables...
|
||||
USER1_GT = int(data["USER1_GT"])
|
||||
USER1_SEX = data["USER1_SEX"]
|
||||
USER1_NAME = data["USER1_NAME"]
|
||||
USER1_HEIGHT = int(data["USER1_HEIGHT"])
|
||||
USER1_DOB = data["USER1_DOB"]
|
||||
|
||||
USER2_LT = int(data["USER2_LT"])
|
||||
USER2_SEX = data["USER2_SEX"]
|
||||
USER2_NAME = data["USER2_NAME"]
|
||||
USER2_HEIGHT = int(data["USER2_HEIGHT"])
|
||||
USER2_DOB = data["USER2_DOB"]
|
||||
|
||||
USER3_SEX = data["USER3_SEX"]
|
||||
USER3_NAME = data["USER3_NAME"]
|
||||
USER3_HEIGHT = int(data["USER3_HEIGHT"])
|
||||
USER3_DOB = data["USER3_DOB"]
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Config Loaded...\n")
|
||||
|
||||
# Failed to open options.json, Loading Config From Environment (Not HA Add-On)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Loading Config From OS Environment...\n")
|
||||
MISCALE_MAC = os.getenv('MISCALE_MAC', '')
|
||||
MQTT_USERNAME = os.getenv('MQTT_USERNAME', 'username')
|
||||
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD', None)
|
||||
MQTT_HOST = os.getenv('MQTT_HOST', '127.0.0.1')
|
||||
MQTT_PORT = int(os.getenv('MQTT_PORT', 1883))
|
||||
MQTT_PREFIX = os.getenv('MQTT_PREFIX', 'miscale')
|
||||
TIME_INTERVAL = int(os.getenv('TIME_INTERVAL', 30))
|
||||
MQTT_DISCOVERY = os.getenv('MQTT_DISCOVERY',True)
|
||||
MQTT_DISCOVERY_PREFIX = os.getenv('MQTT_DISCOVERY_PREFIX','homeassistant')
|
||||
HCI_DEV = os.getenv('HCI_DEV', 'hci0')[-1]
|
||||
|
||||
# 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)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Config Loaded...\n")
|
||||
|
||||
OLD_MEASURE = ''
|
||||
|
||||
def discovery():
|
||||
for MQTTUser in (USER1_NAME,USER2_NAME,USER3_NAME):
|
||||
message = '{"name": "' + MQTTUser + ' Weight",'
|
||||
message+= '"state_topic": "miScale/' + MQTTUser + '/weight","value_template": "{{ value_json.Weight }}","unit_of_measurement": "kg",'
|
||||
message+= '"json_attributes_topic": "miScale/' + MQTTUser + '/weight","icon": "mdi:scale-bathroom"}'
|
||||
publish.single(
|
||||
MQTT_DISCOVERY_PREFIX + '/sensor/' + MQTT_PREFIX + '/' + MQTTUser + '/config',
|
||||
message,
|
||||
retain=True,
|
||||
hostname=MQTT_HOST,
|
||||
port=MQTT_PORT,
|
||||
auth={'username':MQTT_USERNAME, 'password':MQTT_PASSWORD}
|
||||
)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Discovery Completed...\n")
|
||||
|
||||
|
||||
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
|
||||
if dev.addr == MISCALE_MAC.lower() and isNewDev:
|
||||
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:
|
||||
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:
|
||||
data2 = bytes.fromhex(data[4:])
|
||||
ctrlByte1 = data2[1]
|
||||
isStabilized = ctrlByte1 & (1<<5)
|
||||
hasImpedance = ctrlByte1 & (1<<1)
|
||||
|
||||
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 and isStabilized:
|
||||
if OLD_MEASURE != round(measured, 2) + int(miimpedance):
|
||||
self._publish(round(measured, 2), unit, str(datetime.today().strftime('%Y-%m-%d-%H:%M:%S')), hasImpedance, miimpedance)
|
||||
OLD_MEASURE = round(measured, 2) + int(miimpedance)
|
||||
|
||||
|
||||
def _publish(self, weight, unit, mitdatetime, hasImpedance, miimpedance):
|
||||
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 hasImpedance:
|
||||
lib = Xiaomi_Scale_Body_Metrics.bodyMetrics(weight, height, age, sex, int(miimpedance))
|
||||
bodyscale = ['Obese', 'Overweight', 'Thick-set', 'Lack-exerscise', 'Balanced', 'Balanced-muscular', 'Skinny', 'Balanced-skinny', 'Skinny-muscular']
|
||||
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()) + '"'
|
||||
message += ',"Body Type":"' + str(bodyscale[lib.getBodyType()]) + '"'
|
||||
message += ',"Metabolic Age":"' + "{:.0f}".format(lib.getMetabolicAge()) + '"'
|
||||
|
||||
message += ',"TimeStamp":"' + mitdatetime + '"'
|
||||
message += '}'
|
||||
try:
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Publishing data to topic {MQTT_PREFIX + '/' + user + '/weight'}: {message}\n")
|
||||
publish.single(
|
||||
MQTT_PREFIX + '/' + user + '/weight',
|
||||
message,
|
||||
# qos=1, #Removed qos=1 as incorrect connection details will result in the client waiting for ack from broker
|
||||
retain=True,
|
||||
hostname=MQTT_HOST,
|
||||
port=MQTT_PORT,
|
||||
auth={'username':MQTT_USERNAME, 'password':MQTT_PASSWORD}
|
||||
)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Data Published ...\n")
|
||||
except Exception as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Could not publish to MQTT: {error}\n")
|
||||
raise
|
||||
|
||||
def main():
|
||||
if MQTT_DISCOVERY:
|
||||
discovery()
|
||||
BluetoothFailCounter = 0
|
||||
while True:
|
||||
try:
|
||||
scanner = btle.Scanner(HCI_DEV).withDelegate(ScanProcessor())
|
||||
scanner.scan(5) # Adding passive=True to try and fix issues on RPi devices
|
||||
except BTLEDisconnectError as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - btle disconnected: {error}\n")
|
||||
pass
|
||||
except BTLEManagementError as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Bluetooth connection error: {error}\n")
|
||||
if BluetoothFailCounter >= 4:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - 5+ Bluetooth connection errors. Resetting Bluetooth...\n")
|
||||
cmd = 'hciconfig hci0 reset'
|
||||
ps = subprocess.Popen(cmd, shell=True)
|
||||
time.sleep(30)
|
||||
BluetoothFailCounter = 0
|
||||
else:
|
||||
BluetoothFailCounter+=1
|
||||
pass
|
||||
except Exception as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Error while running the script: {error}\n")
|
||||
pass
|
||||
else:
|
||||
BluetoothFailCounter = 0
|
||||
time.sleep(TIME_INTERVAL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import binascii
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from bluepy import btle
|
||||
from bluepy.btle import Scanner, BTLEDisconnectError, BTLEManagementError, DefaultDelegate
|
||||
import paho.mqtt.publish as publish
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
import Xiaomi_Scale_Body_Metrics
|
||||
|
||||
|
||||
|
||||
# First Log msg
|
||||
sys.stdout.write(' \n')
|
||||
sys.stdout.write('-------------------------------------\n')
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Starting Xiaomi mi Scale...\n")
|
||||
|
||||
# Configuraiton...
|
||||
# Trying To Load Config From options.json (HA Add-On)
|
||||
try:
|
||||
with open('/data/options.json') as json_file:
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Loading Config From Add-On Options...\n")
|
||||
data = json.load(json_file)
|
||||
try:
|
||||
MISCALE_MAC = data["MISCALE_MAC"]
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - MAC Address not provided...\n")
|
||||
raise
|
||||
try:
|
||||
MQTT_USERNAME = data["MQTT_USERNAME"]
|
||||
except:
|
||||
MQTT_USERNAME = None
|
||||
pass
|
||||
try:
|
||||
MQTT_PASSWORD = data["MQTT_PASSWORD"]
|
||||
except:
|
||||
MQTT_PASSWORD = None
|
||||
pass
|
||||
try:
|
||||
MQTT_HOST = data["MQTT_HOST"]
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - MQTT Host not provided...\n")
|
||||
raise
|
||||
try:
|
||||
MQTT_PORT = int(data["MQTT_PORT"])
|
||||
except:
|
||||
MQTT_PORT = 1883
|
||||
pass
|
||||
try:
|
||||
MQTT_PREFIX = data["MQTT_PREFIX"]
|
||||
except:
|
||||
MQTT_PREFIX = "miScale"
|
||||
pass
|
||||
try:
|
||||
TIME_INTERVAL = int(data["TIME_INTERVAL"])
|
||||
except:
|
||||
TIME_INTERVAL = 30
|
||||
pass
|
||||
try:
|
||||
MQTT_DISCOVERY = data["MQTT_DISCOVERY"]
|
||||
except:
|
||||
MQTT_DISCOVERY = True
|
||||
pass
|
||||
try:
|
||||
MQTT_DISCOVERY_PREFIX = data["MQTT_DISCOVERY_PREFIX"]
|
||||
except:
|
||||
MQTT_DISCOVERY_PREFIX = "homeassistant"
|
||||
pass
|
||||
try:
|
||||
HCI_DEV = data["HCI_DEV"][-1]
|
||||
except:
|
||||
HCI_DEV = "hci0"[-1]
|
||||
pass
|
||||
try:
|
||||
USER1_GT = int(data["USER1_GT"])
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - USER1_GT not provided...\n")
|
||||
raise
|
||||
try:
|
||||
USER1_SEX = data["USER1_SEX"]
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - USER1_SEX not provided...\n")
|
||||
raise
|
||||
try:
|
||||
USER1_NAME = data["USER1_NAME"]
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - USER1_NAME not provided...\n")
|
||||
raise
|
||||
try:
|
||||
USER1_HEIGHT = int(data["USER1_HEIGHT"])
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - USER1_HEIGHT not provided...\n")
|
||||
raise
|
||||
try:
|
||||
USER1_DOB = data["USER1_DOB"]
|
||||
except:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - USER1_DOB not provided...\n")
|
||||
raise
|
||||
try:
|
||||
USER2_LT = int(data["USER2_LT"])
|
||||
except:
|
||||
USER2_LT = USER1_GT
|
||||
pass
|
||||
try:
|
||||
USER2_SEX = data["USER2_SEX"]
|
||||
except:
|
||||
USER2_SEX = "female"
|
||||
pass
|
||||
try:
|
||||
USER2_NAME = data["USER2_NAME"]
|
||||
except:
|
||||
USER2_NAME = "Serena"
|
||||
pass
|
||||
try:
|
||||
USER2_HEIGHT = int(data["USER2_HEIGHT"])
|
||||
except:
|
||||
USER2_HEIGHT = 95
|
||||
pass
|
||||
try:
|
||||
USER2_DOB = data["USER2_DOB"]
|
||||
except:
|
||||
USER2_DOB = "1990-01-01"
|
||||
pass
|
||||
try:
|
||||
USER3_SEX = data["USER3_SEX"]
|
||||
except:
|
||||
USER3_SEX = "female"
|
||||
pass
|
||||
try:
|
||||
USER3_NAME = data["USER3_NAME"]
|
||||
except:
|
||||
USER3_NAME = "Missy"
|
||||
pass
|
||||
try:
|
||||
USER3_HEIGHT = int(data["USER3_HEIGHT"])
|
||||
except:
|
||||
USER3_HEIGHT = 150
|
||||
pass
|
||||
try:
|
||||
USER3_DOB = data["USER3_DOB"]
|
||||
except:
|
||||
USER3_DOB = "1990-01-01"
|
||||
pass
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Config Loaded...\n")
|
||||
|
||||
# Failed to open options.json, Loading Config From Environment (Not HA Add-On)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Loading Config From OS Environment...\n")
|
||||
MISCALE_MAC = os.getenv('MISCALE_MAC', '')
|
||||
MQTT_USERNAME = os.getenv('MQTT_USERNAME', 'username')
|
||||
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD', None)
|
||||
MQTT_HOST = os.getenv('MQTT_HOST', '127.0.0.1')
|
||||
MQTT_PORT = int(os.getenv('MQTT_PORT', 1883))
|
||||
MQTT_PREFIX = os.getenv('MQTT_PREFIX', 'miscale')
|
||||
TIME_INTERVAL = int(os.getenv('TIME_INTERVAL', 30))
|
||||
MQTT_DISCOVERY = os.getenv('MQTT_DISCOVERY',True)
|
||||
MQTT_DISCOVERY_PREFIX = os.getenv('MQTT_DISCOVERY_PREFIX','homeassistant')
|
||||
HCI_DEV = os.getenv('HCI_DEV', 'hci0')[-1]
|
||||
|
||||
# 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)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Config Loaded...\n")
|
||||
|
||||
OLD_MEASURE = ''
|
||||
|
||||
def discovery():
|
||||
for MQTTUser in (USER1_NAME,USER2_NAME,USER3_NAME):
|
||||
message = '{"name": "' + MQTTUser + ' Weight",'
|
||||
message+= '"state_topic": "miScale/' + MQTTUser + '/weight","value_template": "{{ value_json.Weight }}","unit_of_measurement": "kg",'
|
||||
message+= '"json_attributes_topic": "miScale/' + MQTTUser + '/weight","icon": "mdi:scale-bathroom"}'
|
||||
publish.single(
|
||||
MQTT_DISCOVERY_PREFIX + '/sensor/' + MQTT_PREFIX + '/' + MQTTUser + '/config',
|
||||
message,
|
||||
retain=True,
|
||||
hostname=MQTT_HOST,
|
||||
port=MQTT_PORT,
|
||||
auth={'username':MQTT_USERNAME, 'password':MQTT_PASSWORD}
|
||||
)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Discovery Completed...\n")
|
||||
|
||||
|
||||
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
|
||||
if dev.addr == MISCALE_MAC.lower() and isNewDev:
|
||||
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:
|
||||
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:
|
||||
data2 = bytes.fromhex(data[4:])
|
||||
ctrlByte1 = data2[1]
|
||||
isStabilized = ctrlByte1 & (1<<5)
|
||||
hasImpedance = ctrlByte1 & (1<<1)
|
||||
|
||||
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 and isStabilized:
|
||||
if OLD_MEASURE != round(measured, 2) + int(miimpedance):
|
||||
self._publish(round(measured, 2), unit, str(datetime.today().strftime('%Y-%m-%d-%H:%M:%S')), hasImpedance, miimpedance)
|
||||
OLD_MEASURE = round(measured, 2) + int(miimpedance)
|
||||
|
||||
|
||||
def _publish(self, weight, unit, mitdatetime, hasImpedance, miimpedance):
|
||||
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 hasImpedance:
|
||||
lib = Xiaomi_Scale_Body_Metrics.bodyMetrics(weight, height, age, sex, int(miimpedance))
|
||||
bodyscale = ['Obese', 'Overweight', 'Thick-set', 'Lack-exerscise', 'Balanced', 'Balanced-muscular', 'Skinny', 'Balanced-skinny', 'Skinny-muscular']
|
||||
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()) + '"'
|
||||
message += ',"Body Type":"' + str(bodyscale[lib.getBodyType()]) + '"'
|
||||
message += ',"Metabolic Age":"' + "{:.0f}".format(lib.getMetabolicAge()) + '"'
|
||||
|
||||
message += ',"TimeStamp":"' + mitdatetime + '"'
|
||||
message += '}'
|
||||
try:
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Publishing data to topic {MQTT_PREFIX + '/' + user + '/weight'}: {message}\n")
|
||||
publish.single(
|
||||
MQTT_PREFIX + '/' + user + '/weight',
|
||||
message,
|
||||
# qos=1, #Removed qos=1 as incorrect connection details will result in the client waiting for ack from broker
|
||||
retain=True,
|
||||
hostname=MQTT_HOST,
|
||||
port=MQTT_PORT,
|
||||
auth={'username':MQTT_USERNAME, 'password':MQTT_PASSWORD}
|
||||
)
|
||||
sys.stdout.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Data Published ...\n")
|
||||
except Exception as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Could not publish to MQTT: {error}\n")
|
||||
raise
|
||||
|
||||
def main():
|
||||
if MQTT_DISCOVERY:
|
||||
discovery()
|
||||
BluetoothFailCounter = 0
|
||||
while True:
|
||||
try:
|
||||
scanner = btle.Scanner(HCI_DEV).withDelegate(ScanProcessor())
|
||||
scanner.scan(5) # Adding passive=True to try and fix issues on RPi devices
|
||||
except BTLEDisconnectError as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - btle disconnected: {error}\n")
|
||||
pass
|
||||
except BTLEManagementError as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Bluetooth connection error: {error}\n")
|
||||
if BluetoothFailCounter >= 4:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - 5+ Bluetooth connection errors. Resetting Bluetooth...\n")
|
||||
cmd = 'hciconfig hci0 reset'
|
||||
ps = subprocess.Popen(cmd, shell=True)
|
||||
time.sleep(30)
|
||||
BluetoothFailCounter = 0
|
||||
else:
|
||||
BluetoothFailCounter+=1
|
||||
pass
|
||||
except Exception as error:
|
||||
sys.stderr.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Error while running the script: {error}\n")
|
||||
pass
|
||||
else:
|
||||
BluetoothFailCounter = 0
|
||||
time.sleep(TIME_INTERVAL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,155 +1,155 @@
|
||||
class bodyScales:
|
||||
def __init__(self, age, height, sex, weight, scaleType='xiaomi'):
|
||||
self.age = age
|
||||
self.height = height
|
||||
self.sex = sex
|
||||
self.weight = weight
|
||||
|
||||
if scaleType == 'xiaomi':
|
||||
self.scaleType = 'xiaomi'
|
||||
else:
|
||||
self.scaleType = 'holtek'
|
||||
|
||||
# Get BMI scale
|
||||
def getBMIScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
# Amazfit/new mi fit
|
||||
#return [18.5, 24, 28]
|
||||
# Old mi fit // amazfit for body figure
|
||||
return [18.5, 25.0, 28.0, 32.0]
|
||||
elif self.scaleType == 'holtek':
|
||||
return [18.5, 25.0, 30.0]
|
||||
|
||||
# Get fat percentage scale
|
||||
def getFatPercentageScale(self):
|
||||
# The included tables where quite strange, maybe bogus, replaced them with better ones...
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'min': 0, 'max': 12, 'female': [12.0, 21.0, 30.0, 34.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 12, 'max': 14, 'female': [15.0, 24.0, 33.0, 37.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 14, 'max': 16, 'female': [18.0, 27.0, 36.0, 40.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 16, 'max': 18, 'female': [20.0, 28.0, 37.0, 41.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 18, 'max': 40, 'female': [21.0, 28.0, 35.0, 40.0], 'male': [11.0, 17.0, 22.0, 27.0]},
|
||||
{'min': 40, 'max': 60, 'female': [22.0, 29.0, 36.0, 41.0], 'male': [12.0, 18.0, 23.0, 28.0]},
|
||||
{'min': 60, 'max': 100, 'female': [23.0, 30.0, 37.0, 42.0], 'male': [14.0, 20.0, 25.0, 30.0]},
|
||||
]
|
||||
|
||||
elif self.scaleType == 'holtek':
|
||||
scales = [
|
||||
{'min': 0, 'max': 21, 'female': [18, 23, 30, 35], 'male': [8, 14, 21, 25]},
|
||||
{'min': 21, 'max': 26, 'female': [19, 24, 30, 35], 'male': [10, 15, 22, 26]},
|
||||
{'min': 26, 'max': 31, 'female': [20, 25, 31, 36], 'male': [11, 16, 21, 27]},
|
||||
{'min': 31, 'max': 36, 'female': [21, 26, 33, 36], 'male': [13, 17, 25, 28]},
|
||||
{'min': 36, 'max': 41, 'female': [22, 27, 34, 37], 'male': [15, 20, 26, 29]},
|
||||
{'min': 41, 'max': 46, 'female': [23, 28, 35, 38], 'male': [16, 22, 27, 30]},
|
||||
{'min': 46, 'max': 51, 'female': [24, 30, 36, 38], 'male': [17, 23, 29, 31]},
|
||||
{'min': 51, 'max': 56, '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 muscle mass scale
|
||||
def getMuscleMassScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'min': {'male': 170, 'female': 160}, 'female': [36.5, 42.6], 'male': [49.4, 59.5]},
|
||||
{'min': {'male': 160, 'female': 150}, 'female': [32.9, 37.6], 'male': [44.0, 52.5]},
|
||||
{'min': {'male': 0, 'female': 0}, 'female': [29.1, 34.8], 'male': [38.5, 46.6]},
|
||||
]
|
||||
elif self.scaleType == 'holtek':
|
||||
scales = [
|
||||
{'min': {'male': 170, 'female': 170}, 'female': [36.5, 42.5], 'male': [49.5, 59.4]},
|
||||
{'min': {'male': 160, 'female': 160}, 'female': [32.9, 37.5], 'male': [44.0, 52.4]},
|
||||
{'min': {'male': 0, 'female': 0}, 'female': [29.1, 34.7], 'male': [38.5, 46.5]}
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.height >= scale['min'][self.sex]:
|
||||
return scale[self.sex]
|
||||
|
||||
|
||||
|
||||
# Get water percentage scale
|
||||
def getWaterPercentageScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
if self.sex == 'male':
|
||||
return [55.0, 65.1]
|
||||
elif self.sex == 'female':
|
||||
return [45.0, 60.1]
|
||||
elif self.scaleType == 'holtek':
|
||||
return [53, 67]
|
||||
|
||||
|
||||
# Get visceral fat scale
|
||||
def getVisceralFatScale(self):
|
||||
# Actually the same in mi fit/amazfit and holtek's sdk
|
||||
return [10.0, 15.0]
|
||||
|
||||
|
||||
# Get bone mass scale
|
||||
def getBoneMassScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'male': {'min': 75.0, 'scale': [2.0, 4.2]}, 'female': {'min': 60.0, 'scale': [1.8, 3.9]}},
|
||||
{'male': {'min': 60.0, 'scale': [1.9, 4.1]}, 'female': {'min': 45.0, 'scale': [1.5, 3.8]}},
|
||||
{'male': {'min': 0.0, 'scale': [1.6, 3.9]}, 'female': {'min': 0.0, 'scale': [1.3, 3.6]}},
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.weight >= scale[self.sex]['min']:
|
||||
return scale[self.sex]['scale']
|
||||
|
||||
elif self.scaleType == 'holtek':
|
||||
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 BMR scale
|
||||
def getBMRScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
coefficients = {
|
||||
'male': {30: 21.6, 50: 20.07, 100: 19.35},
|
||||
'female': {30: 21.24, 50: 19.53, 100: 18.63}
|
||||
}
|
||||
elif self.scaleType == 'holtek':
|
||||
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]
|
||||
|
||||
|
||||
# Get protein scale (hardcoded in mi fit)
|
||||
def getProteinPercentageScale(self):
|
||||
# Actually the same in mi fit and holtek's sdk
|
||||
return [16, 20]
|
||||
|
||||
# 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 Body Score scale
|
||||
def getBodyScoreScale(self):
|
||||
# very bad, bad, normal, good, better
|
||||
return [50.0, 60.0, 80.0, 90.0]
|
||||
|
||||
# Return body type scale
|
||||
def getBodyTypeScale(self):
|
||||
return ['obese', 'overweight', 'thick-set', 'lack-exerscise', 'balanced', 'balanced-muscular', 'skinny', 'balanced-skinny', 'skinny-muscular']
|
||||
|
||||
class bodyScales:
|
||||
def __init__(self, age, height, sex, weight, scaleType='xiaomi'):
|
||||
self.age = age
|
||||
self.height = height
|
||||
self.sex = sex
|
||||
self.weight = weight
|
||||
|
||||
if scaleType == 'xiaomi':
|
||||
self.scaleType = 'xiaomi'
|
||||
else:
|
||||
self.scaleType = 'holtek'
|
||||
|
||||
# Get BMI scale
|
||||
def getBMIScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
# Amazfit/new mi fit
|
||||
#return [18.5, 24, 28]
|
||||
# Old mi fit // amazfit for body figure
|
||||
return [18.5, 25.0, 28.0, 32.0]
|
||||
elif self.scaleType == 'holtek':
|
||||
return [18.5, 25.0, 30.0]
|
||||
|
||||
# Get fat percentage scale
|
||||
def getFatPercentageScale(self):
|
||||
# The included tables where quite strange, maybe bogus, replaced them with better ones...
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'min': 0, 'max': 12, 'female': [12.0, 21.0, 30.0, 34.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 12, 'max': 14, 'female': [15.0, 24.0, 33.0, 37.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 14, 'max': 16, 'female': [18.0, 27.0, 36.0, 40.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 16, 'max': 18, 'female': [20.0, 28.0, 37.0, 41.0], 'male': [7.0, 16.0, 25.0, 30.0]},
|
||||
{'min': 18, 'max': 40, 'female': [21.0, 28.0, 35.0, 40.0], 'male': [11.0, 17.0, 22.0, 27.0]},
|
||||
{'min': 40, 'max': 60, 'female': [22.0, 29.0, 36.0, 41.0], 'male': [12.0, 18.0, 23.0, 28.0]},
|
||||
{'min': 60, 'max': 100, 'female': [23.0, 30.0, 37.0, 42.0], 'male': [14.0, 20.0, 25.0, 30.0]},
|
||||
]
|
||||
|
||||
elif self.scaleType == 'holtek':
|
||||
scales = [
|
||||
{'min': 0, 'max': 21, 'female': [18, 23, 30, 35], 'male': [8, 14, 21, 25]},
|
||||
{'min': 21, 'max': 26, 'female': [19, 24, 30, 35], 'male': [10, 15, 22, 26]},
|
||||
{'min': 26, 'max': 31, 'female': [20, 25, 31, 36], 'male': [11, 16, 21, 27]},
|
||||
{'min': 31, 'max': 36, 'female': [21, 26, 33, 36], 'male': [13, 17, 25, 28]},
|
||||
{'min': 36, 'max': 41, 'female': [22, 27, 34, 37], 'male': [15, 20, 26, 29]},
|
||||
{'min': 41, 'max': 46, 'female': [23, 28, 35, 38], 'male': [16, 22, 27, 30]},
|
||||
{'min': 46, 'max': 51, 'female': [24, 30, 36, 38], 'male': [17, 23, 29, 31]},
|
||||
{'min': 51, 'max': 56, '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 muscle mass scale
|
||||
def getMuscleMassScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'min': {'male': 170, 'female': 160}, 'female': [36.5, 42.6], 'male': [49.4, 59.5]},
|
||||
{'min': {'male': 160, 'female': 150}, 'female': [32.9, 37.6], 'male': [44.0, 52.5]},
|
||||
{'min': {'male': 0, 'female': 0}, 'female': [29.1, 34.8], 'male': [38.5, 46.6]},
|
||||
]
|
||||
elif self.scaleType == 'holtek':
|
||||
scales = [
|
||||
{'min': {'male': 170, 'female': 170}, 'female': [36.5, 42.5], 'male': [49.5, 59.4]},
|
||||
{'min': {'male': 160, 'female': 160}, 'female': [32.9, 37.5], 'male': [44.0, 52.4]},
|
||||
{'min': {'male': 0, 'female': 0}, 'female': [29.1, 34.7], 'male': [38.5, 46.5]}
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.height >= scale['min'][self.sex]:
|
||||
return scale[self.sex]
|
||||
|
||||
|
||||
|
||||
# Get water percentage scale
|
||||
def getWaterPercentageScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
if self.sex == 'male':
|
||||
return [55.0, 65.1]
|
||||
elif self.sex == 'female':
|
||||
return [45.0, 60.1]
|
||||
elif self.scaleType == 'holtek':
|
||||
return [53, 67]
|
||||
|
||||
|
||||
# Get visceral fat scale
|
||||
def getVisceralFatScale(self):
|
||||
# Actually the same in mi fit/amazfit and holtek's sdk
|
||||
return [10.0, 15.0]
|
||||
|
||||
|
||||
# Get bone mass scale
|
||||
def getBoneMassScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
scales = [
|
||||
{'male': {'min': 75.0, 'scale': [2.0, 4.2]}, 'female': {'min': 60.0, 'scale': [1.8, 3.9]}},
|
||||
{'male': {'min': 60.0, 'scale': [1.9, 4.1]}, 'female': {'min': 45.0, 'scale': [1.5, 3.8]}},
|
||||
{'male': {'min': 0.0, 'scale': [1.6, 3.9]}, 'female': {'min': 0.0, 'scale': [1.3, 3.6]}},
|
||||
]
|
||||
|
||||
for scale in scales:
|
||||
if self.weight >= scale[self.sex]['min']:
|
||||
return scale[self.sex]['scale']
|
||||
|
||||
elif self.scaleType == 'holtek':
|
||||
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 BMR scale
|
||||
def getBMRScale(self):
|
||||
if self.scaleType == 'xiaomi':
|
||||
coefficients = {
|
||||
'male': {30: 21.6, 50: 20.07, 100: 19.35},
|
||||
'female': {30: 21.24, 50: 19.53, 100: 18.63}
|
||||
}
|
||||
elif self.scaleType == 'holtek':
|
||||
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]
|
||||
|
||||
|
||||
# Get protein scale (hardcoded in mi fit)
|
||||
def getProteinPercentageScale(self):
|
||||
# Actually the same in mi fit and holtek's sdk
|
||||
return [16, 20]
|
||||
|
||||
# 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 Body Score scale
|
||||
def getBodyScoreScale(self):
|
||||
# very bad, bad, normal, good, better
|
||||
return [50.0, 60.0, 80.0, 90.0]
|
||||
|
||||
# Return body type scale
|
||||
def getBodyTypeScale(self):
|
||||
return ['obese', 'overweight', 'thick-set', 'lack-exerscise', 'balanced', 'balanced-muscular', 'skinny', 'balanced-skinny', 'skinny-muscular']
|
||||
|
@ -1,24 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
export MISCALE_MAC=00:00:00:00:00:00 # Mac address of your scale
|
||||
export MQTT_PREFIX=miScale
|
||||
|
||||
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 )"
|
||||
#!/bin/bash
|
||||
|
||||
export MISCALE_MAC=00:00:00:00:00:00 # Mac address of your scale
|
||||
export MQTT_PREFIX=miScale
|
||||
|
||||
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
|
5
repository.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Lolouk44 Add-Ons",
|
||||
"url": "https://github.com/lolouk44/hassio-addons",
|
||||
"maintainer": "lolouk44"
|
||||
}
|