From 7ec0c417b38869809abe07cff0acfd2034d4b333 Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Sat, 30 Oct 2021 11:19:44 +0200 Subject: [PATCH 1/6] Added support for Mi Scale V1 --- tasmota/xsns_62_esp32_mi_ble.ino | 174 ++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 4 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index cb9548e6e..a1f520aec 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -261,6 +261,36 @@ struct PVVXPacket_t { uint8_t flags; }; +struct MiScaleV1Packet_t { + //uint8_t size; // = 14 + //uint8_t uid; // = 0x16, 16-bit UUID + //uint16_t UUID; // = 0x181D + uint8_t status; // bit 0 lbs, 4 jin, 5, stabilized, 7, weight removed + uint16_t weight; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +struct MiScaleV2Packet_t { + //uint8_t size; // = 17 + //uint8_t uid; // = 0x16, 16-bit UUID + //uint16_t UUID; // = 0x181B + uint8_t weight_unit; + uint8_t status; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint16_t impedance; + uint16_t weight; +}; + #pragma pack(0) struct mi_sensor_t{ @@ -286,6 +316,7 @@ struct mi_sensor_t{ uint32_t events:1; uint32_t pairing:1; uint32_t light:1; // binary light sensor + uint32_t scale:1; }; uint32_t raw; } feature; @@ -304,6 +335,7 @@ struct mi_sensor_t{ uint32_t Btn:1; uint32_t PairBtn:1; uint32_t light:1; // binary light sensor + uint32_t scale:1; }; uint32_t raw; } eventType; @@ -333,6 +365,16 @@ struct mi_sensor_t{ union { uint8_t bat; // many values seem to be hard-coded garbage (LYWSD0x, GCD1) }; + union { + struct { + uint8_t has_impedance; + uint8_t stabilized; + uint8_t weight_removed; + char weight_unit[4]; // kg, lbs, jin or empty when unknown + float weight; + uint16_t impedance; + }; + }; }; struct MAC_t { @@ -380,8 +422,10 @@ void (*const MI32_Commands[])(void) PROGMEM = { #define MI_MHOC303 12 #define MI_ATC 13 #define MI_DOOR 14 +#define MI_SCALE_V1 15 +#define MI_SCALE_V2 16 -#define MI_MI32_TYPES 14 //count this manually +#define MI_MI32_TYPES 16 //count this manually const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ 0x0000, // Unkown @@ -396,8 +440,10 @@ const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ 0x0153, // yee-rc 0x0387, // MHO-C401 0x06d3, // MHO-C303 - 0x0a1c, // ATC -> this is a fake ID - 0x098b // door/window sensor + 0x0a1c, // ATC -> this is a fake ID + 0x098b, // door/window sensor + 0x181d, // Mi Scale V1 + 0x181b // Mi Scale V2 }; const char kMI32DeviceType0[] PROGMEM = "Unknown"; @@ -414,7 +460,9 @@ const char kMI32DeviceType10[] PROGMEM ="MHOC401"; const char kMI32DeviceType11[] PROGMEM ="MHOC303"; const char kMI32DeviceType12[] PROGMEM ="ATC"; const char kMI32DeviceType13[] PROGMEM ="DOOR"; -const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,kMI32DeviceType13}; +const char kMI32DeviceType14[] PROGMEM ="MISCALEV1"; +const char kMI32DeviceType15[] PROGMEM ="MISCALEV2"; +const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,kMI32DeviceType13,kMI32DeviceType14,kMI32DeviceType15}; typedef int BATREAD_FUNCTION(int slot); typedef int UNITWRITE_FUNCTION(int slot, int unit); @@ -987,6 +1035,11 @@ int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) case 0x181a: { //ATC MI32ParseATCPacket(ServiceData, ServiceDataLength, addr, RSSI); } break; + case 0x181d: // Mi Scale V1 + case 0x181b: // Mi Scale V2 + { + MI32ParseMiScalePacket(ServiceData, ServiceDataLength, addr, RSSI, UUID); + } break; default:{ } break; @@ -1438,6 +1491,10 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter) _newSensor.feature.light=1; _newSensor.feature.bat=1; break; + case MI_SCALE_V1: + case MI_SCALE_V2: + _newSensor.feature.scale=1; + break; default: _newSensor.hum=NAN; _newSensor.feature.temp=1; @@ -1639,6 +1696,87 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad } } +void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t *addr, int RSSI, int UUID){ + MiScaleV1Packet_t *_packetV1 = (MiScaleV1Packet_t*)_buf; + MiScaleV2Packet_t *_packetV2 = (MiScaleV2Packet_t*)_buf; + + // Mi Scale V1 + if (length == 10 && UUID == 0x181d){ // 14-1-1-2 + uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); + if(_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + MIBLEsensors[_slot].RSSI=RSSI; + MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + MIBLEsensors[_slot].eventType.scale = 1; + + MIBLEsensors[_slot].stabilized = (_packetV1->status & (1 << 5)) ? 1 : 0; + MIBLEsensors[_slot].weight_removed = (_packetV1->status & (1 << 7)) ? 1 : 0; + + if (_packetV1->status & (1 << 0)) { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("lbs")); + MIBLEsensors[_slot].weight = (float)_packetV1->weight / 100.0f; + } else if(_packetV1->status & (1 << 4)) { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("jin")); + MIBLEsensors[_slot].weight = (float)_packetV1->weight / 100.0f; + } else { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("kg")); + MIBLEsensors[_slot].weight = (float)_packetV1->weight / 200.0f; + } + + if (MIBLEsensors[_slot].weight_removed) { + MIBLEsensors[_slot].weight = 0.0f; + } + + if(MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + } + } + + // Mi Scale V2 + else if (length == 13 && UUID == 0x181b){ // 17-1-1-2 + uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); + if(_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + MIBLEsensors[_slot].RSSI=RSSI; + MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + MIBLEsensors[_slot].eventType.scale = 1; + + MIBLEsensors[_slot].has_impedance = (_packetV2->status & (1 << 1)) ? 1 : 0; + MIBLEsensors[_slot].stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; + MIBLEsensors[_slot].weight_removed = (_packetV2->status & (1 << 7)) ? 1 : 0; + + if (_packetV2->weight_unit & (1 << 4)) { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("jin")); + MIBLEsensors[_slot].weight = (float)_packetV2->weight / 100.0f; + } else if(_packetV2->weight_unit == 3) { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("lbs")); + MIBLEsensors[_slot].weight = (float)_packetV2->weight / 100.0f; + } else if(_packetV2->weight_unit == 2) { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("kg")); + MIBLEsensors[_slot].weight = (float)_packetV2->weight / 200.0f; + } else { + strcpy(MIBLEsensors[_slot].weight_unit, PSTR("")); + MIBLEsensors[_slot].weight = (float)_packetV2->weight / 100.0f; + } + + if (MIBLEsensors[_slot].weight_removed) { + MIBLEsensors[_slot].weight = 0.0f; + } + + if(MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + } + } +} + //////////////////////////////////////////////////////////// // this SHOULD parse any MI payload. int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ @@ -2380,6 +2518,10 @@ const char HTTP_NMT[] PROGMEM = "{s}%s No motion{m}> %u seconds{e}"; const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}"; const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; const char HTTP_MI32_LIGHT[] PROGMEM = "{s}%s" " Light" "{m}%d{e}"; +const char HTTP_MISCALE_WEIGHT[] PROGMEM = "{s}%s" " Weight" "{m}%*_f %s{e}"; +const char HTTP_MISCALE_IMPEDANCE[] PROGMEM = "{s}%s" " Impedance" "{m}%u{e}"; +const char HTTP_MISCALE_WEIGHT_REMOVED[] PROGMEM = "{s}%s" " Weight removed" "{m}%s{e}"; +const char HTTP_MISCALE_STABILIZED[] PROGMEM = "{s}%s" " Stabilized" "{m}%s{e}"; //const char HTTP_NEEDKEY[] PROGMEM = "{s}%s feature.scale){ + if(p->eventType.scale || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ){ + ResponseAppend_P(PSTR(",\"weight_removed\":%u"), p->weight_removed); + ResponseAppend_P(PSTR(",\"stabilized\":%u"), p->stabilized); + ResponseAppend_P(PSTR(",\"weight_unit\":\"%s\""), p->weight_unit); + ResponseAppend_P(PSTR(",\"weight\":%*_f"), + Settings->flag2.weight_resolution, &p->weight); + if (p->has_impedance){ + ResponseAppend_P(PSTR(",\"impedance\":%u"), p->impedance); + } + } + } if (p->feature.Btn){ if(p->eventType.Btn || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate #ifdef USE_HOME_ASSISTANT @@ -3205,6 +3363,14 @@ void MI32Show(bool json) if (p->feature.light){ WSContentSend_PD(HTTP_MI32_LIGHT, typeName, p->light); } + if (p->feature.scale){ + WSContentSend_PD(HTTP_MISCALE_WEIGHT, typeName, Settings->flag2.weight_resolution, &p->weight, p->weight_unit); + if (p->has_impedance) { + WSContentSend_PD(HTTP_MISCALE_IMPEDANCE, typeName, p->impedance); + } + WSContentSend_PD(HTTP_MISCALE_WEIGHT_REMOVED, typeName, p->weight_removed? PSTR("yes") : PSTR("no")); + WSContentSend_PD(HTTP_MISCALE_STABILIZED, typeName, p->stabilized ? PSTR("yes") : PSTR("no")); + } if(p->bat!=0x00){ WSContentSend_PD(HTTP_BATTERY, typeName, p->bat); From 2a53f3ba1dbfbee2489fd5419634192a01adb769 Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Thu, 16 Dec 2021 00:44:49 +0100 Subject: [PATCH 2/6] Use only stabilized weight packets when directBridgeMode is disabled --- tasmota/xsns_62_esp32_mi_ble.ino | 60 ++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index bb42778c3..abbfeee69 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -1700,23 +1700,30 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad } } -void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t *addr, int RSSI, int UUID){ +void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t *addr, int RSSI, int UUID) { MiScaleV1Packet_t *_packetV1 = (MiScaleV1Packet_t*)_buf; MiScaleV2Packet_t *_packetV2 = (MiScaleV2Packet_t*)_buf; + uint8_t stabilized = 0; + uint8_t weight_removed = 0; // Mi Scale V1 - if (length == 10 && UUID == 0x181d){ // 14-1-1-2 - uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); - if(_slot==0xff) return; + if (length == 10 && UUID == 0x181d) { // 14-1-1-2 + stabilized = (_packetV1->status & (1 << 5)) ? 1 : 0; + weight_removed = (_packetV1->status & (1 << 7)) ? 1 : 0; + if (!MI32.option.directBridgeMode && (!stabilized || weight_removed)) + return; - if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); + if (_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())) { if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); - MIBLEsensors[_slot].RSSI=RSSI; - MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + MIBLEsensors[_slot].RSSI = RSSI; + MIBLEsensors[_slot].needkey = KEY_NOT_REQUIRED; MIBLEsensors[_slot].eventType.scale = 1; - MIBLEsensors[_slot].stabilized = (_packetV1->status & (1 << 5)) ? 1 : 0; - MIBLEsensors[_slot].weight_removed = (_packetV1->status & (1 << 7)) ? 1 : 0; + MIBLEsensors[_slot].stabilized = stabilized; + MIBLEsensors[_slot].weight_removed = weight_removed; if (_packetV1->status & (1 << 0)) { strcpy(MIBLEsensors[_slot].weight_unit, PSTR("lbs")); @@ -1741,19 +1748,24 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t } // Mi Scale V2 - else if (length == 13 && UUID == 0x181b){ // 17-1-1-2 - uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); - if(_slot==0xff) return; + else if (length == 13 && UUID == 0x181b) { // 17-1-1-2 + stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; + weight_removed = (_packetV2->status & (1 << 7)) ? 1 : 0; + if (!MI32.option.directBridgeMode && (!stabilized || weight_removed)) + return; - if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); + if (_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())) { if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); - MIBLEsensors[_slot].RSSI=RSSI; - MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + MIBLEsensors[_slot].RSSI = RSSI; + MIBLEsensors[_slot].needkey = KEY_NOT_REQUIRED; MIBLEsensors[_slot].eventType.scale = 1; MIBLEsensors[_slot].has_impedance = (_packetV2->status & (1 << 1)) ? 1 : 0; - MIBLEsensors[_slot].stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; - MIBLEsensors[_slot].weight_removed = (_packetV2->status & (1 << 7)) ? 1 : 0; + MIBLEsensors[_slot].stabilized = stabilized; + MIBLEsensors[_slot].weight_removed = weight_removed; if (_packetV2->weight_unit & (1 << 4)) { strcpy(MIBLEsensors[_slot].weight_unit, PSTR("jin")); @@ -2731,8 +2743,10 @@ void MI32GetOneSensorJson(int slot, int hidename){ ||(hass_mode==2) #endif //USE_HOME_ASSISTANT ){ - ResponseAppend_P(PSTR(",\"weight_removed\":%u"), p->weight_removed); - ResponseAppend_P(PSTR(",\"stabilized\":%u"), p->stabilized); + if (MI32.option.directBridgeMode) { + ResponseAppend_P(PSTR(",\"weight_removed\":%u"), p->weight_removed); + ResponseAppend_P(PSTR(",\"stabilized\":%u"), p->stabilized); + } ResponseAppend_P(PSTR(",\"weight_unit\":\"%s\""), p->weight_unit); ResponseAppend_P(PSTR(",\"weight\":%*_f"), Settings->flag2.weight_resolution, &p->weight); @@ -3369,8 +3383,10 @@ void MI32Show(bool json) if (p->has_impedance) { WSContentSend_PD(HTTP_MISCALE_IMPEDANCE, typeName, p->impedance); } - WSContentSend_PD(HTTP_MISCALE_WEIGHT_REMOVED, typeName, p->weight_removed? PSTR("yes") : PSTR("no")); - WSContentSend_PD(HTTP_MISCALE_STABILIZED, typeName, p->stabilized ? PSTR("yes") : PSTR("no")); + if (MI32.option.directBridgeMode) { + WSContentSend_PD(HTTP_MISCALE_WEIGHT_REMOVED, typeName, p->weight_removed? PSTR("yes") : PSTR("no")); + WSContentSend_PD(HTTP_MISCALE_STABILIZED, typeName, p->stabilized ? PSTR("yes") : PSTR("no")); + } } if(p->bat!=0x00){ @@ -3438,4 +3454,4 @@ bool Xsns62(uint8_t function) #endif // CONFIG_IDF_TARGET_ESP32 or CONFIG_IDF_TARGET_ESP32C3 #endif // ESP32 -#endif +#endif \ No newline at end of file From dc1fa0e50d87ccad3ae61ea345d7ba7d37c20785 Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Sat, 18 Dec 2021 20:43:17 +0100 Subject: [PATCH 3/6] Added feature.impedance for V2 to separate impedance and impedance_stabilized value from V1 and changed stabilized/has_impedance to weight_stabilized/impedance_stabilized --- tasmota/xsns_62_esp32_mi_ble.ino | 54 ++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index abbfeee69..1c37323d1 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -280,7 +280,7 @@ struct MiScaleV2Packet_t { //uint8_t uid; // = 0x16, 16-bit UUID //uint16_t UUID; // = 0x181B uint8_t weight_unit; - uint8_t status; + uint8_t status; // bit 14 impedance stabilized uint16_t year; uint8_t month; uint8_t day; @@ -317,6 +317,7 @@ struct mi_sensor_t{ uint32_t pairing:1; uint32_t light:1; // binary light sensor uint32_t scale:1; + uint32_t impedance:1; }; uint32_t raw; } feature; @@ -367,11 +368,11 @@ struct mi_sensor_t{ }; union { struct { - uint8_t has_impedance; - uint8_t stabilized; + uint8_t weight_stabilized; uint8_t weight_removed; char weight_unit[4]; // kg, lbs, jin or empty when unknown float weight; + uint8_t impedance_stabilized; uint16_t impedance; }; }; @@ -1492,8 +1493,11 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter) _newSensor.feature.bat=1; break; case MI_SCALE_V1: + _newSensor.feature.scale=1; + break; case MI_SCALE_V2: _newSensor.feature.scale=1; + _newSensor.feature.impedance=1; break; default: _newSensor.hum=NAN; @@ -1703,14 +1707,15 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t *addr, int RSSI, int UUID) { MiScaleV1Packet_t *_packetV1 = (MiScaleV1Packet_t*)_buf; MiScaleV2Packet_t *_packetV2 = (MiScaleV2Packet_t*)_buf; - uint8_t stabilized = 0; + uint8_t weight_stabilized = 0; uint8_t weight_removed = 0; + uint8_t impedance_stabilized = 0; // Mi Scale V1 if (length == 10 && UUID == 0x181d) { // 14-1-1-2 - stabilized = (_packetV1->status & (1 << 5)) ? 1 : 0; + weight_stabilized = (_packetV1->status & (1 << 5)) ? 1 : 0; weight_removed = (_packetV1->status & (1 << 7)) ? 1 : 0; - if (!MI32.option.directBridgeMode && (!stabilized || weight_removed)) + if (!MI32.option.directBridgeMode && (!weight_stabilized || weight_removed)) return; uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); @@ -1722,7 +1727,7 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].needkey = KEY_NOT_REQUIRED; MIBLEsensors[_slot].eventType.scale = 1; - MIBLEsensors[_slot].stabilized = stabilized; + MIBLEsensors[_slot].weight_stabilized = weight_stabilized; MIBLEsensors[_slot].weight_removed = weight_removed; if (_packetV1->status & (1 << 0)) { @@ -1749,9 +1754,10 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t // Mi Scale V2 else if (length == 13 && UUID == 0x181b) { // 17-1-1-2 - stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; + weight_stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; weight_removed = (_packetV2->status & (1 << 7)) ? 1 : 0; - if (!MI32.option.directBridgeMode && (!stabilized || weight_removed)) + impedance_stabilized = (_packetV2->status & (1 << 1)) ? 1 : 0; + if (!MI32.option.directBridgeMode && (!weight_stabilized || weight_removed /* || !impedance_stabilized */)) return; uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); @@ -1763,9 +1769,10 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].needkey = KEY_NOT_REQUIRED; MIBLEsensors[_slot].eventType.scale = 1; - MIBLEsensors[_slot].has_impedance = (_packetV2->status & (1 << 1)) ? 1 : 0; - MIBLEsensors[_slot].stabilized = stabilized; + MIBLEsensors[_slot].weight_stabilized = weight_stabilized; MIBLEsensors[_slot].weight_removed = weight_removed; + MIBLEsensors[_slot].impedance_stabilized = impedance_stabilized; + MIBLEsensors[_slot].impedance = _packetV2->impedance; if (_packetV2->weight_unit & (1 << 4)) { strcpy(MIBLEsensors[_slot].weight_unit, PSTR("jin")); @@ -1783,6 +1790,7 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t if (MIBLEsensors[_slot].weight_removed) { MIBLEsensors[_slot].weight = 0.0f; + MIBLEsensors[_slot].impedance = 0; } if(MI32.option.directBridgeMode) { @@ -2535,9 +2543,10 @@ const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e} const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; const char HTTP_MI32_LIGHT[] PROGMEM = "{s}%s" " Light" "{m}%d{e}"; const char HTTP_MISCALE_WEIGHT[] PROGMEM = "{s}%s" " Weight" "{m}%*_f %s{e}"; -const char HTTP_MISCALE_IMPEDANCE[] PROGMEM = "{s}%s" " Impedance" "{m}%u{e}"; const char HTTP_MISCALE_WEIGHT_REMOVED[] PROGMEM = "{s}%s" " Weight removed" "{m}%s{e}"; -const char HTTP_MISCALE_STABILIZED[] PROGMEM = "{s}%s" " Stabilized" "{m}%s{e}"; +const char HTTP_MISCALE_WEIGHT_STABILIZED[] PROGMEM = "{s}%s" " Weight stabilized" "{m}%s{e}"; +const char HTTP_MISCALE_IMPEDANCE[] PROGMEM = "{s}%s" " Impedance" "{m}%u{e}"; +const char HTTP_MISCALE_IMPEDANCE_STABILIZED[] PROGMEM = "{s}%s" " Impedance stabilized" "{m}%s{e}"; //const char HTTP_NEEDKEY[] PROGMEM = "{s}%s
weight_removed); - ResponseAppend_P(PSTR(",\"stabilized\":%u"), p->stabilized); + ResponseAppend_P(PSTR(",\"weight_stabilized\":%u"), p->weight_stabilized); } ResponseAppend_P(PSTR(",\"weight_unit\":\"%s\""), p->weight_unit); ResponseAppend_P(PSTR(",\"weight\":%*_f"), Settings->flag2.weight_resolution, &p->weight); - if (p->has_impedance){ + if (p->feature.impedance) { + if (MI32.option.directBridgeMode) { + ResponseAppend_P(PSTR(",\"impedance_stabilized\":%u"), p->impedance_stabilized); + } ResponseAppend_P(PSTR(",\"impedance\":%u"), p->impedance); } } @@ -3380,15 +3392,17 @@ void MI32Show(bool json) } if (p->feature.scale){ WSContentSend_PD(HTTP_MISCALE_WEIGHT, typeName, Settings->flag2.weight_resolution, &p->weight, p->weight_unit); - if (p->has_impedance) { - WSContentSend_PD(HTTP_MISCALE_IMPEDANCE, typeName, p->impedance); - } if (MI32.option.directBridgeMode) { WSContentSend_PD(HTTP_MISCALE_WEIGHT_REMOVED, typeName, p->weight_removed? PSTR("yes") : PSTR("no")); - WSContentSend_PD(HTTP_MISCALE_STABILIZED, typeName, p->stabilized ? PSTR("yes") : PSTR("no")); + WSContentSend_PD(HTTP_MISCALE_WEIGHT_STABILIZED, typeName, p->weight_stabilized ? PSTR("yes") : PSTR("no")); + } + if (p->feature.impedance) { + WSContentSend_PD(HTTP_MISCALE_IMPEDANCE, typeName, p->impedance); + if (MI32.option.directBridgeMode) { + WSContentSend_PD(HTTP_MISCALE_IMPEDANCE_STABILIZED, typeName, p->impedance_stabilized? PSTR("yes") : PSTR("no")); + } } } - if(p->bat!=0x00){ WSContentSend_PD(HTTP_BATTERY, typeName, p->bat); } From ecb3a07d2f3aa0180a1283533f882ce6a3f81fcf Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Sun, 19 Dec 2021 11:58:41 +0100 Subject: [PATCH 4/6] Changed json property names to better match the other properties --- tasmota/xsns_62_esp32_mi_ble.ino | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index 1c37323d1..5b4fa8eed 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -2753,17 +2753,17 @@ void MI32GetOneSensorJson(int slot, int hidename){ #endif //USE_HOME_ASSISTANT ){ if (MI32.option.directBridgeMode) { - ResponseAppend_P(PSTR(",\"weight_removed\":%u"), p->weight_removed); - ResponseAppend_P(PSTR(",\"weight_stabilized\":%u"), p->weight_stabilized); + ResponseAppend_P(PSTR(",\"WeightRemoved\":%u"), p->weight_removed); + ResponseAppend_P(PSTR(",\"WeightStabilized\":%u"), p->weight_stabilized); } - ResponseAppend_P(PSTR(",\"weight_unit\":\"%s\""), p->weight_unit); - ResponseAppend_P(PSTR(",\"weight\":%*_f"), + ResponseAppend_P(PSTR(",\"WeightUnit\":\"%s\""), p->weight_unit); + ResponseAppend_P(PSTR(",\"Weight\":%*_f"), Settings->flag2.weight_resolution, &p->weight); if (p->feature.impedance) { if (MI32.option.directBridgeMode) { - ResponseAppend_P(PSTR(",\"impedance_stabilized\":%u"), p->impedance_stabilized); + ResponseAppend_P(PSTR(",\"ImpedanceStabilized\":%u"), p->impedance_stabilized); } - ResponseAppend_P(PSTR(",\"impedance\":%u"), p->impedance); + ResponseAppend_P(PSTR(",\"Impedance\":%u"), p->impedance); } } } From a0ba50d38db43c869de92c5b2f8ab300527b87f9 Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Sun, 19 Dec 2021 11:59:49 +0100 Subject: [PATCH 5/6] Added Weight and Impedance support to Home Assistant auto discovery --- tasmota/xsns_62_esp32_mi_ble.ino | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index 5b4fa8eed..841de87a0 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -3012,6 +3012,16 @@ const char *classes[] = { "", //- empty device class "Firmware", "", + + // 11 + "", //- empty device class + "Weight", + "", // Will be set to p->weight_unit + + // 12 + "", //- empty device class + "Impedance", + "Ohm", }; @@ -3123,6 +3133,16 @@ void MI32DiscoveryOneMISensor(){ continue; } break; + case 11: // weight + if (!p->feature.scale){ // Mi Scale V1 and V2 only + continue; + } + break; + case 12: // impedance + if (!p->feature.impedance){ // Mi Scale V2 only + continue; + } + break; } /* @@ -3157,9 +3177,9 @@ void MI32DiscoveryOneMISensor(){ //"\"uniq_id\":\"%s_%s\"," - unique for this data, id, classes[i+1], //"\"unit_of_meas\":\"%s\"," - the measure of this type of data - (classes[i+2][0]?"\"unit_of_meas\":\"":""), - classes[i+2], - (classes[i+2][0]?"\",":""), + ((i/3==11)||classes[i+2][0]?"\"unit_of_meas\":\"":""), + (i/3==11)?p->weight_unit:classes[i+2], + ((i/3==11)||classes[i+2][0]?"\",":""), //"\"val_tpl\":\"{{ %s%s }}") // e.g. Temperature // inverted binary - {{ 'off' if value_json.posn else 'on' }} // binary - {{ 'on' if value_json.posn else 'off' }} From d13381f1306661dcca687721ecbcb977720d1e38 Mon Sep 17 00:00:00 2001 From: Milenko Mitrovic Date: Sun, 30 Jan 2022 00:25:22 +0100 Subject: [PATCH 6/6] Send MQTT message when weight/impedance is stabilized or direct bridge mode is enabled. Fixed impedance measurement on V2 scale. --- tasmota/xsns_62_esp32_mi_ble.ino | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino index 841de87a0..2ee034c06 100644 --- a/tasmota/xsns_62_esp32_mi_ble.ino +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -1729,7 +1729,7 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].weight_stabilized = weight_stabilized; MIBLEsensors[_slot].weight_removed = weight_removed; - + if (_packetV1->status & (1 << 0)) { strcpy(MIBLEsensors[_slot].weight_unit, PSTR("lbs")); MIBLEsensors[_slot].weight = (float)_packetV1->weight / 100.0f; @@ -1745,10 +1745,8 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].weight = 0.0f; } - if(MI32.option.directBridgeMode) { - MIBLEsensors[_slot].shallSendMQTT = 1; - MI32.mode.shallTriggerTele = 1; - } + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; } } @@ -1757,7 +1755,7 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t weight_stabilized = (_packetV2->status & (1 << 5)) ? 1 : 0; weight_removed = (_packetV2->status & (1 << 7)) ? 1 : 0; impedance_stabilized = (_packetV2->status & (1 << 1)) ? 1 : 0; - if (!MI32.option.directBridgeMode && (!weight_stabilized || weight_removed /* || !impedance_stabilized */)) + if (!MI32.option.directBridgeMode && (!weight_stabilized || weight_removed)) return; uint32_t _slot = MIBLEgetSensorSlot(addr, UUID, 0); @@ -1771,8 +1769,6 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].weight_stabilized = weight_stabilized; MIBLEsensors[_slot].weight_removed = weight_removed; - MIBLEsensors[_slot].impedance_stabilized = impedance_stabilized; - MIBLEsensors[_slot].impedance = _packetV2->impedance; if (_packetV2->weight_unit & (1 << 4)) { strcpy(MIBLEsensors[_slot].weight_unit, PSTR("jin")); @@ -1788,15 +1784,23 @@ void MI32ParseMiScalePacket(const uint8_t * _buf, uint32_t length, const uint8_t MIBLEsensors[_slot].weight = (float)_packetV2->weight / 100.0f; } - if (MIBLEsensors[_slot].weight_removed) { + if (weight_removed) { MIBLEsensors[_slot].weight = 0.0f; - MIBLEsensors[_slot].impedance = 0; } - if(MI32.option.directBridgeMode) { - MIBLEsensors[_slot].shallSendMQTT = 1; - MI32.mode.shallTriggerTele = 1; + MIBLEsensors[_slot].impedance = 0; + if (MI32.option.directBridgeMode || impedance_stabilized) + { + MIBLEsensors[_slot].impedance_stabilized = impedance_stabilized; + MIBLEsensors[_slot].impedance = _packetV2->impedance; + + if (weight_removed) { + MIBLEsensors[_slot].impedance = 0; + } } + + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; } } }