add yeelight dimmer

This commit is contained in:
Staars 2022-02-11 18:03:22 +01:00
parent 7f6b6529c7
commit 9749c9af59
3 changed files with 119 additions and 59 deletions

View File

@ -236,6 +236,7 @@ struct mi_sensor_t{
uint32_t NMT:1;
uint32_t motion:1;
uint32_t Btn:1;
uint32_t knob:1;
uint32_t door:1;
uint32_t leak:1;
};
@ -254,6 +255,8 @@ struct mi_sensor_t{
uint32_t motion:1;
uint32_t noMotion:1;
uint32_t Btn:1;
uint32_t knob:1;
uint32_t longpress:1; //needs no extra feature bit, because knob is sufficient
uint32_t door:1;
uint32_t leak:1;
};
@ -285,11 +288,15 @@ struct mi_sensor_t{
}; // MJ_HT_V1, LYWSD0x
struct {
uint16_t events; //"alarms" since boot
uint32_t NMT; // no motion time in seconds for the MJYD2S
uint32_t NMT; // no motion time in seconds for the MJYD2S and NLIGHT
};
struct {
uint16_t Btn;
uint8_t leak;
uint8_t Btn; // number starting with 0
uint8_t BtnType; // 0 -single, 1 - double, 2 - hold
uint8_t leak; // the leak sensor is the only non-RC device so far with a button fuctionality, so we handle it here
int8_t dimmer;
uint8_t pressed; // dimmer knob pressed while rotating
uint8_t longpress; // dimmer knob pressed without rotating
};
uint8_t door;
};
@ -315,9 +322,9 @@ struct mi_sensor_t{
#define D_CMND_MI32 "MI32"
const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|Key|"/*Time|Battery|Unit|Beacon|*/"Cfg|Option";
const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|Key|Cfg|Option";
void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, /*&CmndMi32Time, &CmndMi32Battery, &CmndMi32Unit, &CmndMi32Beacon,*/ &CmndMi32Cfg, &CmndMi32Option };
void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, &CmndMi32Cfg, &CmndMi32Option };
#define FLORA 1
#define MJ_HT_V1 2
@ -327,7 +334,7 @@ void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, /*&CmndMi32Time, &C
#define CGD1 6
#define NLIGHT 7
#define MJYD2S 8
#define YEERC 9
#define YLYK01 9
#define MHOC401 10
#define MHOC303 11
#define ATC 12
@ -346,7 +353,7 @@ const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
0x0576, // CGD1
0x03dd, // NLIGHT
0x07f6, // MJYD2S
0x0153, // yee-rc
0x0153, // YLYK01, old name yee-rc
0x0387, // MHO-C401
0x06d3, // MHO-C303
0x0a1c, // ATC -> this is a fake ID
@ -364,7 +371,7 @@ const char kMI32DeviceType5[] PROGMEM = "CGG1";
const char kMI32DeviceType6[] PROGMEM = "CGD1";
const char kMI32DeviceType7[] PROGMEM = "NLIGHT";
const char kMI32DeviceType8[] PROGMEM = "MJYD2S";
const char kMI32DeviceType9[] PROGMEM = "YEERC";
const char kMI32DeviceType9[] PROGMEM = "YLYK01"; //old name yeerc
const char kMI32DeviceType10[] PROGMEM ="MHOC401";
const char kMI32DeviceType11[] PROGMEM ="MHOC303";
const char kMI32DeviceType12[] PROGMEM ="ATC";
@ -382,6 +389,8 @@ const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|got no ser
const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Start scanning";
const char kMI32_HKInfoMsg[] PROGMEM = "HAP core started|HAP core did not start!!|HAP controller disconnected|HAP controller connected|HAP outlet added";
const char kMI32_ButtonMsg[] PROGMEM = "Single|Double|Hold"; //mapping: in Tasmota: 1,2,3 ; for HomeKit and Xiaomi 0,1,2
/*********************************************************************************************\
* enumerations
\*********************************************************************************************/

View File

@ -22,13 +22,15 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
0.9.5.1 20220209 changed - rename YEERC to YLYK01, add dimmer YLKG08 (incl. YLKG07), change button report scheme
-------
0.9.5.0 20211016 changed - major rewrite, added mi32cfg (file and command), Homekit-Bridge,
extended GUI,
removed BLOCK, PERIOD, TIME, UNIT, BATTERY and PAGE -> replaced via Berry-Support
-------
0.9.1.7 20201116 changed - small bugfixes, add BLOCK and OPTION command, send BLE scan via MQTT
-------
0.9.1.0 20200712 changed - add lights and yeerc, add pure passive mode with decryption,
0.9.1.0 20200712 changed - add lights and YLYK01, add pure passive mode with decryption,
lots of refactoring
-------
0.9.0.1 20200706 changed - adapt to new NimBLE-API, tweak scan process
@ -44,7 +46,7 @@
#ifdef USE_MI_ESP32
#ifdef USE_ENERGY_SENSOR
// #define USE_MI_ESP32_ENERGY //perpare for some GUI extensions
// #define USE_MI_ESP32_ENERGY //prepare for some GUI extensions
#endif
#define XSNS_62 62
@ -256,6 +258,7 @@ void MI32AddKey(mi_bindKey_t keyMAC){
_sensor.key = _key;
unknownMAC=false;
_sensor.status.hasWrongKey = 0;
AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) _sensor.key, 16);
}
}
if(unknownMAC){
@ -278,7 +281,7 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
mi_beacon_t *_beacon = (mi_beacon_t *)_buf;
uint8_t nonce[13]; //v3:13, v5:12
uint32_t nonceLen =12; // most devices are v5
uint32_t nonceLen = 12; // most devices are v5
uint8_t tag[4] = {0};
const unsigned char authData[1] = {0x11};
size_t dataLen = _bufSize - 11 ; // _bufsize - frame - type - frame.counter - MAC
@ -288,13 +291,6 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
return -2;
}
// uint8_t _testBuf[] = {0x58,0x30,0xb6,0x03,0x36,0x8b,0x98,0xc5,0x41,0x24,0xf8,0x8b,0xb8,0xf2,0x66,0x13,0x51,0x00,0x00,0x00,0xd6};
// uint8_t _testKey[] = {0xb8,0x53,0x07,0x51,0x58,0x48,0x8d,0x3d,0x3c,0x97,0x7c,0xa3,0x9a,0x5b,0x5e,0xa9};
// _beacon = (mi_beacon_t *)_testBuf;
// _bufSize = sizeof(_testBuf);
// dataLen = _bufSize - 11 ; // _bufsize - frame - type - frame.counter - MAC
uint32_t _version = (uint32_t)_beacon->frame.version;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: encrypted msg from %s with version:%u"),kMI32DeviceType[MIBLEsensors[_slot].type-1],_version);
@ -335,8 +331,7 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
for (uint32_t i = 0; i<5; i++){
nonce[i+8] = _beacon->MAC[i];
}
tag[0] = _buf[_bufSize-1];
// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) nonce, 13);
// tag[0] = _buf[_bufSize-1]; // it is unclear, if this value is a checksum
dataLen -= 4;
}
else{
@ -345,7 +340,6 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
br_aes_small_ctrcbc_keys keyCtx;
br_aes_small_ctrcbc_init(&keyCtx, MIBLEsensors[_slot].key, 16);
// br_aes_small_ctrcbc_init(&keyCtx, _testKey, 16);
br_ccm_context ctx;
br_ccm_init(&ctx, &keyCtx.vtable);
@ -355,9 +349,9 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
br_ccm_run(&ctx, 0, _payload, dataLen);
if(br_ccm_check_tag(&ctx, &tag)) return 0;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: decrypted in %.2f mSec"),enctime);
// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) _payload, dataLen);
if(_version == 3 && _payload[1] == 0x10) return 0; // no known way to really verify decryption, but 0x10 is expected here for button events
return -1; // wrong key ... maybe corrupt data packet too
}
@ -457,9 +451,13 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
_newSensor.feature.bat=1;
_newSensor.NMT=0;
break;
case YEERC: case YLKG08:
_newSensor.feature.Btn=1;
_newSensor.Btn=99;
case YLYK01: case YLKG08:
_newSensor.feature.Btn = 1;
_newSensor.Btn = 99;
if(_type == YLKG08){
_newSensor.feature.knob = 1;
_newSensor.dimmer = 0;
}
#ifdef USE_MI_HOMEKIT
_newSensor.button_hap_service[0] = nullptr;
#endif //USE_MI_HOMEKIT
@ -1222,40 +1220,46 @@ if(decryptRet!=0){
MIBLEsensors[_slot].lastTime = millis();
switch(_payload.type){
case 0x01:
if(_payload.Btn.type == 4){ //knob dimmer
if(_payload.Btn.type == 4){ //dimmer knob rotation
MIBLEsensors[_slot].eventType.knob = 1;
if(_payload.Btn.num == 0){
if(_payload.Btn.value<128){
AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate right: %u"),_payload.Btn.value);
}
else{
AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate left: %u"),256 - _payload.Btn.value);
}
MIBLEsensors[_slot].pressed = 0;
MIBLEsensors[_slot].dimmer = _payload.Btn.value;
}
else if(_payload.Btn.num<128){
AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate right: %u"),_payload.Btn.num);
else {
MIBLEsensors[_slot].pressed = 1;
MIBLEsensors[_slot].dimmer = _payload.Btn.num;
}
else{
AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate left: %u"),256 - _payload.Btn.num);
}
return; //TODO: implement MQTT later
MI32.mode.shallTriggerTele = 1;
break; //To-Do: Map to HomeKit somehow or wait for real support of this device class in HomeKit
}
if(_payload.Btn.num == 1 && MIBLEsensors[_slot].feature.knob){ //dimmer knob long press
MIBLEsensors[_slot].longpress = _payload.Btn.value;
MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].eventType.longpress = 1;
#ifdef USE_MI_HOMEKIT
if((void**)MIBLEsensors[_slot].button_hap_service[0] != nullptr){
mi_homekit_update_value(MIBLEsensors[_slot].button_hap_service[0], (float)2.0f, 0x01); // only one button, long press = 2
}
#endif //USE_MI_HOMEKIT
break;
}
// single, double, long
MIBLEsensors[_slot].Btn = _payload.Btn.num;
if(MIBLEsensors[_slot].feature.knob){
MIBLEsensors[_slot].BtnType = _payload.Btn.value - 1;
}
else{
MIBLEsensors[_slot].BtnType = _payload.Btn.type;
}
MIBLEsensors[_slot].Btn=_payload.Btn.num + (_payload.Btn.type/2)*6;
MIBLEsensors[_slot].eventType.Btn = 1;
MI32.mode.shallTriggerTele = 1;
#ifdef USE_MI_HOMEKIT
{
// {uint32_t _button = _payload.Btn.num + (_payload.Btn.type/2)*6;
uint32_t _singleLong = 0;
if(MIBLEsensors[_slot].Btn>5){
MIBLEsensors[_slot].Btn = MIBLEsensors[_slot].Btn - 6;
_singleLong = 2;
}
if(MIBLEsensors[_slot].Btn>5) break; //
if(MIBLEsensors[_slot].Btn>5) break; // hard coded limit for now
if((void**)MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn] != nullptr){
// AddLog(LOG_LEVEL_DEBUG,PSTR("Send Button %u: SingleLong:%u, pointer: %x"), MIBLEsensors[_slot].Btn,_singleLong,MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn] );
mi_homekit_update_value(MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn], (float)_singleLong, 0x01);
}
}
mi_homekit_update_value(MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn], (float)MIBLEsensors[_slot].BtnType, 0x01);
}
#endif //USE_MI_HOMEKIT
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn );
break;
@ -1774,8 +1778,19 @@ void MI32sendWidget(uint32_t slot){
WSContentSend_P(PSTR("</p>"));
}
}
if(_sensor.feature.knob){
if(_sensor.pressed == 0) {
WSContentSend_P(PSTR("<p>Dimmer Steps: %d</p>"),_sensor.dimmer);
}
else {
WSContentSend_P(PSTR("<p>Dimmer Steps pressed: %d</p>"),_sensor.dimmer);
}
WSContentSend_P(PSTR("<p>Long: %u</p>"),_sensor.longpress);
}
if(_sensor.feature.Btn){
if(_sensor.Btn<12) WSContentSend_P(PSTR("<p>Last Button: %u</p>"),_sensor.Btn);
char _message[16];
GetTextIndexed(_message, sizeof(_message), _sensor.BtnType, kMI32_ButtonMsg);
if(_sensor.Btn<12) WSContentSend_P(PSTR("<p>Button%u: %s</p>"),_sensor.Btn,_message);
}
if(_sensor.feature.motion){
WSContentSend_P(PSTR("<p>Events: %u</p>"),_sensor.events);
@ -1797,7 +1812,7 @@ void MI32sendWidget(uint32_t slot){
WSContentSend_P(PSTR("<p>Leak !!!</p>"));
}
else{
WSContentSend_P(PSTR("<p>no leak</p>"));
WSContentSend_P(PSTR("<p>No leak</p>"));
}
}
WSContentSend_P(PSTR("</div>"));
@ -1990,7 +2005,29 @@ void MI32Show(bool json)
#endif //USE_HOME_ASSISTANT
){
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"Btn\":%u"),MIBLEsensors[i].Btn);
ResponseAppend_P(PSTR("\"Button%u\":%u"),MIBLEsensors[i].Btn,MIBLEsensors[i].BtnType + 1); //internal type is Xiaomi/Homekit 0,1,2 -> Tasmota 1,2,3
}
}
if (MIBLEsensors[i].feature.knob){
if(MIBLEsensors[i].eventType.knob
#ifdef USE_HOME_ASSISTANT
||(hass_mode==2)
#endif //USE_HOME_ASSISTANT
){
MI32ShowContinuation(&commaflg);
char _pressed[3] = {'_','P',0};
if (MIBLEsensors[i].pressed == 0){
_pressed[0] = 0;
}
ResponseAppend_P(PSTR("\"Dimmer%s\":%d"),_pressed, MIBLEsensors[i].dimmer);
}
if(MIBLEsensors[i].eventType.longpress
#ifdef USE_HOME_ASSISTANT
||(hass_mode==2)
#endif //USE_HOME_ASSISTANT
){
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"Hold\":%d"), MIBLEsensors[i].longpress);
}
}
} // minimal summary
@ -1998,14 +2035,14 @@ void MI32Show(bool json)
if(MIBLEsensors[i].eventType.motion || !MI32.mode.triggeredTele){
if(MI32.mode.triggeredTele) {
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"motion\":1")); // only real-time
ResponseAppend_P(PSTR("\"Motion\":1")); // only real-time
}
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"Events\":%u"),MIBLEsensors[i].events);
}
else if(MIBLEsensors[i].eventType.noMotion && MI32.mode.triggeredTele){
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"motion\":0"));
ResponseAppend_P(PSTR("\"Motion\":0"));
}
}
@ -2013,7 +2050,7 @@ void MI32Show(bool json)
if(MIBLEsensors[i].eventType.door || !MI32.mode.triggeredTele){
if(MI32.mode.triggeredTele) {
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"DOOR\":%u"),MIBLEsensors[i].door); // only real-time
ResponseAppend_P(PSTR("\"Door\":%u"),MIBLEsensors[i].door);
}
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"Events\":%u"),MIBLEsensors[i].events);
@ -2131,7 +2168,7 @@ void MI32Show(bool json)
if(MIBLEsensors[i].bat!=0x00){
WSContentSend_PD(HTTP_BATTERY, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat);
}
if (MIBLEsensors[i].type==YEERC){
if (MIBLEsensors[i].type==YLYK01){
WSContentSend_PD(HTTP_LASTBUTTON, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].Btn);
}
}

View File

@ -40,7 +40,7 @@ static bool MIBridgeWasNeverConnected = true;
#define CGD1 6
#define NLIGHT 7
#define MJYD2S 8
#define YEERC 9
#define YLYK01 9
#define MHOC401 10
#define MHOC303 11
#define ATC 12
@ -221,7 +221,7 @@ static void MI32_bridge_thread_entry(void *p)
MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
break;
}
case YEERC:
case YLYK01:
{
bridge_cfg.cid = HAP_CID_PROGRAMMABLE_SWITCH;
hap_serv_t * _label = hap_serv_service_label_create(1);
@ -237,6 +237,20 @@ static void MI32_bridge_thread_entry(void *p)
}
}
break;
case YLKG08: //without the dimmer function due to lack of HomeKit support
{
bridge_cfg.cid = HAP_CID_PROGRAMMABLE_SWITCH;
hap_serv_t * _label = hap_serv_service_label_create(1);
hap_acc_add_serv(accessory, _label);
hap_serv_t * _newSwitch = hap_serv_stateless_programmable_switch_create(0);
const uint8_t _validVals[] = {0,1,2};
hap_char_add_valid_vals(hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT), _validVals, 3);
hap_char_t *_index = hap_char_service_label_index_create(1);
hap_serv_add_char(_newSwitch,_index);
hap_acc_add_serv(accessory, _newSwitch);
MI32saveHAPhandles(i,1000,hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT));
}
break;
case SJWS01L:
service = hap_serv_leak_sensor_create(0);
hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);