I recommend supporting the sonoff Bluetooth devices (s-mate and r5) to be integrated via Bluetooth that belongs to the Raspberry Pi or any local Home Assistant server. Also, this support should be added to esphome so we can add those Bluetooth (LE) devices to the sonoff devices that support ewelink remote control or any esp32 device.
They don’t seem to use the standard Bluetooth protocol, but a proprietary protocol called eWeLink-Remote
Yea, it’s a Bluetooth (LE).
is there any update on this topic?
I read on elsewhere something about s-mate switches are read-only sensors for home assistant via sonoff lan integration but I want to use it on automations as normal switch.
is this still not possible? (for s-mate2 switches)
Looks like there is thread on tasmota stating that it is BT LE-PHY - the only problem is to decode/understand switching info sent by scene controller. Maybe this will help to progress from hass standpoint - I assume a lot of people are waiting for some solution.
Yeah, I am the one who mentioned that and I replied to you there
With the help of Tasmota discussion I was managed to make template sensors for S-Mate remote. The basic idea is to do the XOR operation with formerly captured encrypted packets that belong to a specific button. It works stable on a Doit ESP32 devkit v1 board. I have tested it with an S-Mate and its working fine so far.
ESPHome code snippet:
esp32:
board: esp32dev
framework:
type: esp-idf
binary_sensor:
- platform: template
name: smate button1
id: button_b1
- platform: template
name: smate button2
id: button_b2
- platform: template
name: smate button3
id: button_b3
switch:
- platform: template
name: store smate button1
id: store_b1
optimistic: True
- platform: template
name: store smate button2
id: store_b2
optimistic: True
- platform: template
name: store smate button3
id: store_b3
optimistic: True
globals:
- id: btdata_store
type: 'std::vector< esphome::esp32_ble::ESPBTUUID >'
- id: btdata_b1
type: 'std::vector< esphome::esp32_ble::ESPBTUUID >'
- id: btdata_b1_serial
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_b1_uuid4
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_b2
type: 'std::vector< esphome::esp32_ble::ESPBTUUID >'
- id: btdata_b2_serial
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_b2_uuid4
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_b3
type: 'std::vector< esphome::esp32_ble::ESPBTUUID >'
- id: btdata_b3_serial
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_b3_uuid4
type: 'uint32_t'
initial_value: '0'
restore_value: yes
esp32_ble_tracker:
scan_parameters:
interval: 150ms
window: 150ms
active: false
on_ble_advertise:
- mac_address:
- "66:55:44:33:22:11"
then:
- lambda: |-
auto btdata = x.get_service_uuids();
if (btdata[1].contains(0x78, 0xF6) && (btdata.size()==6)) {
if (!(id(btdata_store) == btdata)) {
id(btdata_store) = btdata;
uint32_t btserial = (btdata[2].get_uuid().uuid.uuid32 & 0xff000000) | (btdata[3].get_uuid().uuid.uuid32 & 0x00ffffff);
uint32_t uuid4u32 = btdata[4].get_uuid().uuid.uuid32;
if (id(store_b1).state) {
id(btdata_b1) = btdata;
id(btdata_b1_serial) = btserial;
id(btdata_b1_uuid4) = uuid4u32;
id(store_b1).turn_off();
} else if (id(store_b2).state) {
id(btdata_b2) = btdata;
id(btdata_b2_serial) = btserial;
id(btdata_b2_uuid4) = uuid4u32;
id(store_b2).turn_off();
} else if (id(store_b3).state) {
id(btdata_b3) = btdata;
id(btdata_b3_serial) = btserial;
id(btdata_b3_uuid4) = uuid4u32;
id(store_b3).turn_off();
} else {
if ((btserial==id(btdata_b1_serial)) && (((uuid4u32 >> 24) ^ (id(btdata_b1_uuid4) >> 24)) == ((uuid4u32 >> 16 & 0xff) ^ (id(btdata_b1_uuid4) >> 16 & 0xff))) &&
(((uuid4u32 >> 8 & 0xff) ^ (id(btdata_b1_uuid4) >> 8 & 0xff)) == ((uuid4u32 & 0xff) ^ (id(btdata_b1_uuid4) & 0xff)))) {
id(button_b1).publish_state(!(id(button_b1).state));
} else {
if ((btserial==id(btdata_b2_serial)) && (((uuid4u32 >> 24) ^ (id(btdata_b2_uuid4) >> 24)) == ((uuid4u32 >> 16 & 0xff) ^ (id(btdata_b2_uuid4) >> 16 & 0xff))) &&
(((uuid4u32 >> 8 & 0xff) ^ (id(btdata_b2_uuid4) >> 8 & 0xff)) == ((uuid4u32 & 0xff) ^ (id(btdata_b2_uuid4) & 0xff)))) {
id(button_b2).publish_state(!(id(button_b2).state));
} else {
if ((btserial==id(btdata_b3_serial)) && (((uuid4u32 >> 24) ^ (id(btdata_b3_uuid4) >> 24)) == ((uuid4u32 >> 16 & 0xff) ^ (id(btdata_b3_uuid4) >> 16 & 0xff))) &&
(((uuid4u32 >> 8 & 0xff) ^ (id(btdata_b3_uuid4) >> 8 & 0xff)) == ((uuid4u32 & 0xff) ^ (id(btdata_b3_uuid4) & 0xff)))) {
id(button_b3).publish_state(!(id(button_b3).state));
}
}
}
}
}
}
At first You need to teech each S-mate buttons with the help of the “store” switches one by one. As soon as it receives an S-mate sending, the sotre switch goes off, and the corresponding binary template sensor will receive the further S-mate commands.
Would this work with s- mate 2?
If it uses the same protocol it would work. I’ll order an s-mate 2 soon and report the result here.
Hi, just new to HA and my first ever post. changed all my devices over from WiFi minir4 to zigbee minir2, but unfortunately have to keep the sonoff minir4 through sonoff lan integration as using the smart 2 with a dumb rocker switch. Has there been any update on how an automation/scene can work in HA? Currently my smate2 through sonoff lan shows no entities for trigger to create an automation. Was wondering if there has been any head way in that department? Many Thanks in advance
S-mate 2 works fine also. I just modified the code in my 1st post to check the serial num also and made some refactoring.
Hi. Thanks @norbim1 your lambda code for the s-mate really help here with a R5.
I use the same logic but extended it operate with 3 different R5 and implemented the different 18 possible buttons. The behaviour is similar to what Sonoff add-on does in HA.
Still needs some polishing with the clear of the text_sensor, but for a PoC it worked perfectly.
text_sensor:
- platform: template
name: "Device 1 Button Status"
id: button_text_sensor_0
icon: "mdi:gesture-tap-button"
- platform: template
name: "Device 2 Button Status"
id: button_text_sensor_1
icon: "mdi:gesture-tap-button"
- platform: template
name: "Device 3 Button Status"
id: button_text_sensor_2
icon: "mdi:gesture-tap-button"
switch:
- platform: template
name: Store R5 id1
id: store_id1
optimistic: True
- platform: template
name: Store R5 id2
id: store_id2
optimistic: True
- platform: template
name: Store R5 id3
id: store_id3
optimistic: True
binary_sensor:
- platform: template
name: R5 id1
id: button_b1
- platform: template
name: R5 id2
id: button_b2
- platform: template
name: R5 id3
id: button_b3
globals:
- id: btdata_store
type: 'std::vector< esphome::esp32_ble::ESPBTUUID >'
- id: btdata_r5_id
type: std::vector<unsigned int>
initial_value: "std::vector<unsigned int>{}"
- id: btdata_r5_id1
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_r5_id2
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_r5_id3
type: 'uint32_t'
initial_value: '0'
restore_value: yes
- id: btdata_uuid4_str
type: std::string
restore_value: yes
#button press UUID by order. The order may vary according to device version
initial_value: '"0xFA1B99AA, 0x9575F6C5, 0x9F7CFCCF, 0x29C84B79, 0xBF5FDDEF, 0xA043C2F0, 0xBF5EDAEF, 0xA141C4F1, 0x9D7EF8CD, 0x8A6BECDA, 0xD535B385, 0x1FFC794F, 0xE60781B6, 0x2FCF487F, 0xD734B087, 0x02E36652, 0xEB0B8FBB, 0x32D15662"'
esp32_ble_tracker:
scan_parameters:
interval: 150ms
window: 150ms
active: false
on_ble_advertise:
- mac_address: "66:55:44:33:22:11"
then:
- lambda: |-
auto btdata = x.get_service_uuids();
if (id(btdata_r5_id).size() < 3) {
id(btdata_r5_id).resize(3);
}
if (btdata.size() >= 6 && btdata[1].contains(0x78, 0xF6)) {
if (!(id(btdata_store) == btdata)) {
id(btdata_store) = btdata;
uint32_t btdevice = (btdata[2].get_uuid().uuid.uuid32 & 0xff000000) |
(btdata[3].get_uuid().uuid.uuid32 & 0x00ffffff);
uint32_t uuid4u32 = btdata[4].get_uuid().uuid.uuid32;
if (id(store_id1).state) {
id(btdata_r5_id)[0] = btdevice;
id(store_id1).turn_off();
} else if (id(store_id2).state) {
id(btdata_r5_id)[1] = btdevice;
id(store_id2).turn_off();
} else if (id(store_id3).state) {
id(btdata_r5_id)[2] = btdevice;
id(store_id3).turn_off();
} else {
std::vector<uint32_t> parsed_values;
std::string btdata_str = id(btdata_uuid4_str);
size_t pos = 0;
while ((pos = btdata_str.find(",")) != std::string::npos) {
std::string token = btdata_str.substr(0, pos);
parsed_values.push_back(strtoul(token.c_str(), nullptr, 16));
btdata_str.erase(0, pos + 1);
}
parsed_values.push_back(strtoul(btdata_str.c_str(), nullptr, 16)); // Last value
for (size_t j = 0; j < id(btdata_r5_id).size(); j++) {
if (btdevice == id(btdata_r5_id)[j]) {
for (size_t i = 0; i < parsed_values.size(); i++) {
uint32_t stored_value = parsed_values[i];
if ((((uuid4u32 >> 24) ^ (stored_value >> 24)) ==
((uuid4u32 >> 16 & 0xff) ^ (stored_value >> 16 & 0xff))) &&
(((uuid4u32 >> 8 & 0xff) ^ (stored_value >> 8 & 0xff)) ==
((uuid4u32 & 0xff) ^ (stored_value & 0xff)))) {
int button_number = i / 3 + 1; // each button is grouped in multiples of
std::string state = "Button_" + std::to_string(button_number) + ":";
//determinie the type of press
if (i % 3 == 0) {
state += "Single_Press";
} else if (i % 3 == 1) {
state += "Double_Press";
} else if (i % 3 == 2) {
state += "Long_Press";
}
if (j == 0) {
id(button_text_sensor_0).publish_state(state);
} else if (j == 1) {
id(button_text_sensor_1).publish_state(state);
} else if (j == 2) {
id(button_text_sensor_2).publish_state(state);
}
//this part is really ugly. next setps is to place it outside de lambda
delay(500);
if (j == 0) {
id(button_text_sensor_0).publish_state("");
} else if (j == 1) {
id(button_text_sensor_1).publish_state("");
} else if (j == 2) {
id(button_text_sensor_2).publish_state("");
}
return;
}
}
}
}
}
}
}
I would like to test this with my S-Mate 2. I already have an ESP board as a bluetooth proxy. Can I add this code without losing previous functionality?
Here little bit shorter code for Sonoff R5/S-Mate/S-Mate2.
ESPHome will fireHA event with data about device id, button pressed and kind of action.
Events can be seen in:
- Developer Tools → Events → Event to subscribe to: esphome.sonoff_ble → Start Listening.
globals:
- id: btdata_uuid4
type: std::vector<uint32_t>
restore_value: no
#button press UUID by order. The order may vary according to device version
initial_value: '{0x8A6BECDA, 0xD535B385, 0x1FFC794F, 0xE60781B6, 0x2FCF487F, 0xD734B087, 0x02E36652, 0xEB0B8FBB, 0x32D15662,
0xFA1B99AA, 0x9575F6C5, 0x9F7CFCCF, 0x29C84B79, 0xBF5FDDEF, 0xA043C2F0, 0xBF5EDAEF, 0xA141C4F1, 0x9D7EF8CD}'
- id: button_actions
type: 'std::vector<std::string>'
restore_value: no
initial_value: '{"short", "double", "long"}'
esp32_ble_tracker:
scan_parameters:
interval: 150ms
window: 150ms
active: false
on_ble_advertise:
- mac_address: "66:55:44:33:22:11" # Sonoff BLE
then:
- lambda: |-
static std::vector< esphome::esp32_ble::ESPBTUUID > btdata_store;
auto btdata = x.get_service_uuids();
if (btdata.size() >= 6 && btdata[1].contains(0x78, 0xF6)) {
if (!(btdata_store == btdata)) {
btdata_store = btdata;
uint32_t btdevice = (btdata[2].get_uuid().uuid.uuid32 & 0xff000000) |
(btdata[3].get_uuid().uuid.uuid32 & 0x00ffffff);
uint32_t uuid4u32 = (btdata[4].get_uuid().uuid.uuid32);
for (size_t i = 0; i < id(btdata_uuid4).size(); i++) {
uint32_t stored_value = id(btdata_uuid4)[i];
if ((((uuid4u32 >> 24) ^ (stored_value >> 24)) == ((uuid4u32 >> 16 & 0xff) ^ (stored_value >> 16 & 0xff))) &&
(((uuid4u32 >> 8 & 0xff) ^ (stored_value >> 8 & 0xff)) == ((uuid4u32 & 0xff) ^ (stored_value & 0xff)))) {
int button_number = i / 3 + 1; // each button is grouped in multiples of
int button_action = i % 3;
esphome::api::CustomAPIDevice capi;
capi.fire_homeassistant_event("esphome.sonoff_ble",
{{"device", format_hex(btdevice)},
{"button", to_string(button_number)},
{"action", id(button_actions)[button_action].c_str()}});
ESP_LOGI("Sonoff", "0x%x : %i : %s", btdevice, button_number,id(button_actions)[button_action].c_str());
return;
}
}
}
}
binary_sensor:
- platform: template
id: ble_scan_torun
internal: true
entity_category: diagnostic
device_class: connectivity
lambda: !lambda |-
return global_api_server->is_connected();
on_press:
- esp32_ble_tracker.start_scan:
continuous: true
on_release:
- esp32_ble_tracker.stop_scan:
Additionally possible to collect list of BLE MAC’s data received from:
globals:
- id: btdata_id_str
type: std::vector<std::string>
restore_value: no
initial_value: "std::vector<std::string>{}"
switch:
- platform: template
name: "BLE List Collect"
id: ble_list_collect
restore_mode: RESTORE_DEFAULT_ON
optimistic: true
button:
- platform: template
name: "BLE List Publish"
on_press:
- script.execute: ble_list_publish
script:
- id: ble_list_publish
then:
- lambda: |-
if (id(ble_list_collect).state) {
ESP_LOGI("BLE", "Address List (%02i)", id(btdata_id_str).size());
for (auto s: id(btdata_id_str)) {
ESP_LOGI("BLE", "%s", s.c_str());
}
} else {
ESP_LOGI("BLE", "Address List disabled");
}
esp32_ble_tracker:
on_ble_advertise:
- then:
- lambda: |-
if (id(ble_list_collect).state && (x.get_address_type() != BLE_ADDR_TYPE_RANDOM)) {
std::string address_str = x.address_str();
for (auto s: id(btdata_id_str)) {
if (address_str == s) return;
}
id(btdata_id_str).push_back(address_str);
id(ble_list_publish_script).execute();
}
If want to see all MAC’s in the air - remove (x.get_address_type() != BLE_ADDR_TYPE_RANDOM)
condition.
Here is an example of automation:
automation:
- id: sonoff_ble_events
alias: "Sonoff BLE events"
description: ''
mode: restart
trigger:
- platform: event
event_type: esphome.sonoff_ble
condition: []
variables:
data: "{{ trigger.event.data }}"
action:
- choose:
- conditions: "{{ data.device == '5a2bcfd9' }}"
sequence:
- choose:
- conditions: "{{ data.button == '1' and data.action == 'short' }}"
sequence:
- action: switch.toggle
target:
entity_id:
- switch.xxx
will this also work with a S-Mate?
Not right now. Will check soon for S-Mate & S-Mate2.
An finally by reordering codes in array that way:
initial_value: '{0x8A6BECDA, 0xD535B385, 0x1FFC794F, 0xE60781B6, 0x2FCF487F, 0xD734B087, 0x02E36652, 0xEB0B8FBB, 0x32D15662,
0xFA1B99AA, 0x9575F6C5, 0x9F7CFCCF, 0x29C84B79, 0xBF5FDDEF, 0xA043C2F0, 0xBF5EDAEF, 0xA141C4F1, 0x9D7EF8CD}'
we geting nice numbering.
For S-Mate & S-Mate now button numbers are 1-2-3, for R5:
1 2 3
6 5 4
S-Mate2:
When DIP switch in position “Momentary switch” - detects “short/double/long” events.
In position “Rocker” switch - generates same event “short” on both transitions open-close & close-open.
S-Mate:
In DIP Switch position “Rocker” switch - generates same event “short” on both transitions open-close & close-open.
In position “Pulse” - only generates “short” on transition open-close contact, nothing more.
Looking for a little help with this. I have 3 Smate-2s and have the esp32 showing the button presses and the list of macs e.g. [D][Sonoff_R5:148]: 5a2bcfd9:4:short which is button 1 on the first smate. However nothing seems to show up in Home assistant.
In particular, I presume I need to fill the initial value array with device specific numbers but it is not clear how I find these numbers? Any help appreciated.
Please check post 14 with updated ESPHome yaml, how to see fired events & automation example.
Thanks very much - got all three of the smate2s working.
One further question - if I had a couple of esp32s running this code and both of them picked up the ble broadcast, would this result in multiple events?