Here’s what it looks like on my frontend:

I tried using room-assistant and it worked pretty well for the last two years until one of my pi 3’s wifi module broke.
I am using room presence to decide whether to turn off the devices in the room if there are no motion for x amount of time and room presence is equal to 0.
Here’s what I am using:
• Amazfit’s GTR 2e / GTS2 / BIP S (Mi band will also work)
• Tasker or HA companion app on Android
• Notify for amazfit (need the intents for heartbeat/notWearing) - this is to determine if the watch is on hand
• ESP 32s for each room
Steps:
- Configure amazfit on notify app.
a. Enable amazfit discoverable
b. Enable tasker integration for the intents
c. Configure heart monitor:
i. interval - 5 mins
ii. Heart monitor mode - use notify and amazfit device
- Configure tasker or companion app to catch the following intents:
(com.mc.miband.tasker.notWearing & com.mc.miband.heartRateGot)
a. On HA companion app (recommended so it will work with public/private URL)
Go to settings > companion app> manage sensors> Last update trigger
Add the two intents
b. On Tasker (you will need MQTT plugin + external&internal MQTT server):
i. Create intent received profile for the two intents above.
ii. Point the profile to execute tasks that passes the intent's value e.g.
1) Heartrate
a) Topic: user1/watch/hr
b) Payload: %value
2) NotWearing
a) Topic: user1/watch/onhand
b) Payload: false
c. Configure mqtt sensor:
- name: "user1_watch_heartrate" unique_id: user1_watch_heartrate state_topic: "user1/watch/hr" value_template: "{{ value|int(0)}}" unit_of_measurement: "bps" state_class: measurement
d. Configure mqtt binary sensor:
- name: "user1_watch_onhand" unique_id: user1_watch_onhand state_topic: "user1/watch/onhand" payload_on: "true" payload_off: "false"
e. Automations:
i. watch_onhand_true:
alias: watch_onhand_true id: watch_onhand_true mode: parallel trigger: - platform: state entity_id: 'sensor.user1_watch_heartrate' id: user1 condition: condition: or conditions: - condition: and conditions: - condition: state entity_id: binary_sensor.user1_watch_onhand state: 'off' - condition: trigger id: user1 action: - choose: - conditions: - condition: trigger id: user1 sequence: - service: mqtt.publish data: topic: "user1/watch/onhand" payload: "yes" retain: true
ii. watch_onhand_false:
alias: 'watch_onhand_false' id: watch_onhand_false mode: parallel max: 5 trigger: - platform: event event_type: android.intent_received event_data: intent: com.mc.watch.tasker.notWearing device_id: <16 character of companion app's device id that you can get from events>listen to events> android.intent_received id: user1 - platform: numeric_state entity_id: sensor.user1_watch_heartrate below: 1 for: 00:10:00 id: user1_hr action: - choose: - conditions: - condition: or conditions: - condition: template value_template: "{{trigger.id in ('user1','user1_hr')}}" sequence: - service: mqtt.publish data: topic: "user1/watch/onhand" payload: "false" retain: true
iii. watch_heartrate:
alias: 'watch_heartrate' id: watch_heartrate mode: parallel max: 5 trigger: - platform: event event_type: android.intent_received event_data: intent: com.mc.watch.heartRateGot device_id: c44efa41bb151d0e id: user1 action: - choose: - conditions: - condition: trigger id: user1 sequence: - service: mqtt.publish data: topic: "user1/watch/hr" payload: >- {{trigger.event.data.value}} retain: true
- ESP32s
a. Per each room / esp32, Configure the input_number for RSSI strength at 1meter:
esphome_measuredpower_rp_livingroom:
1) Min: -99
2) Max: -1
3) Unit of measurement: dB
b. Templates:
notes: name of ble rssi entity is rp__user1watch
i. persons_livingroom:
persons_livingroom: unit_of_measurement: person/s value_template: > {% set room = 'livingroom' %} {% set ns = namespace(total=0,notonhand=0) %} {% for state in states['sensor'] %} {% if state.entity_id.startswith('sensor.rp_') and state.state==room %} {% set ns.total = ns.total + 1 %} {% endif %} {% endfor %} {% if states.binary_sensor.user1_watch_onhand.state=='off' and states.sensor.rp_user1.state==room %} {% set ns.notonhand = ns.notonhand + 1 %} {% endif %} {% if states.binary_sensor.user2_watch_onhand.state=='off' and states.sensor.rp_user2.state==room %} {% set ns.notonhand = ns.notonhand + 1 %} {% endif %} {% if states.binary_sensor.user3_watch_onhand.state=='off' and states.sensor.rp_user3.state==room %} {% set ns.notonhand = ns.notonhand + 1 %} {% endif %} {{ ns.total - ns.notonhand}}
ii. rp_user1 (note: you can set the farthest distance of the room between your device and esp32 on the ns namespace as input_number too)
rp_user1: value_template: > {% set device = 'user1watch' %} {% set ns = namespace(kitchen=5,livingroom=8,entertainmentroom=6.5,laundry=5,masterbedroom=6,home=0,rp=[], min=[]) %} {% for state in states['sensor'] %} {% if state.entity_id.startswith('sensor.rp_') and state.entity_id.endswith(device+'_distance') %} {% set room = state.entity_id|replace('sensor.rp_','')|replace('_'+device+'_distance','') %} {% if room=='kitchen' and state.state|float(20) <= ns.kitchen %} {% set ns.rp=ns.rp + [state.entity_id] %} {% set ns.min=ns.min + [state.state|float(20)] %} {% elif room=='kitchen' and state.state|float(20) > ns.kitchen and state.state!='unknown' and state.state!='unavailable' %} {% set ns.home=ns.home|int(0)+1 %} {% elif room=='livingroom' and state.state|float(20) <= ns.livingroom %} {% set ns.rp=ns.rp + [state.entity_id] %} {% set ns.min=ns.min + [state.state|float(20)] %} {% elif room=='livingroom' and state.state|float(20) > ns.livingroom and state.state!='unknown' and state.state!='unavailable'%} {% set ns.home=ns.home|int(0)+1 %} {% elif room=='entertainmentroom' and state.state|float(20) <= ns.entertainmentroom %} {% set ns.rp=ns.rp + [state.entity_id] %} {% set ns.min=ns.min + [state.state|float(20)] %} {% elif room=='entertainmentroom' and state.state|float(20) > ns.entertainmentroom and state.state!='unknown' and state.state!='unavailable' %} {% set ns.home=ns.home|int(0)+1 %} {% elif room=='laundry' and state.state|float(20) <= ns.laundry %} {% set ns.rp=ns.rp + [state.entity_id] %} {% set ns.min=ns.min + [state.state|float(20)] %} {% elif room=='laundry' and state.state|float(20) > ns.laundry and state.state!='unknown' and state.state!='unavailable' %} {% set ns.home=ns.home|int(0)+1 %} {% elif room=='masterbedroom' and state.state|float(20) <= ns.masterbedroom %} {% set ns.rp=ns.rp + [state.entity_id] %} {% set ns.min=ns.min + [state.state|float(20)] %} {% elif room=='masterbedroom' and state.state|float(20) > ns.masterbedroom and state.state!='unknown' and state.state!='unavailable' %} {% set ns.home=ns.home|int(0)+1 %} {% endif %} {% endif %} {% endfor %} {% if ns.min==[] and ns.home!=0 %} home {% elif ns.min==[] and ns.home==0 %} not_home {% else %} {% set min_d = min(ns.min) %} {% set min_di = ns.min.index(min_d) %} {{ ns.rp[min_di]|replace('sensor.rp_','')|replace('_'+device+'_distance','')}} {% endif %}
c. ESP32 config:
notes:
1) I’ve splitted this so I just need to configure common_sensor_bletracker.yaml if I wanted to change filters on all esp32 devices.
2) You also need to create components/just_Esp_bt.h and copy secrets.yaml to common/
3) Regarding filters, I tested sliding_moving_average and mean but the update is very slow. Throttling average gives me somewhat accurate and it shows unknown if the device is not within esp32 bluetooth range
i. rp_livingroom.yaml:
substitutions: board: featheresp32 device_name: rp_livingroom friendly_name: rp_livingroom esphome: name: ${device_name} platform: ESP32 board: ${board} includes: - components/just_esp_bt.h # contains: #include <esp_bt.h> packages: wifi: !include common/wifi.yaml esphome: !include common/esphome_esp32bletracker.yaml bletracker: !include common/sensor_bletracker.yaml
ii. common/esphome_esp32bletracker.yaml:
web_server: port: 80 auth: username: user1 password: !secret web_pw # Enable logging logger: baud_rate: 0 # level: VERY_VERBOSE # Enable Home Assistant API api: password: !secret esphome encryption: key: !secret encrypt ota: password: !secret esphome on_begin: then: - logger.log: "OTA: disabling bluetooth ..." - lambda: '(void)esp_bt_controller_disable();' - lambda: '(void)esp_bt_controller_deinit();' on_error: then: - logger.log: "OTA error: Waiting 180s before forced reboot" - delay: 180 sec - logger.log: "OTA error: Rebooting now" - lambda: 'App.safe_reboot();' esp32_ble_tracker: scan_parameters: interval: 320ms window: 160ms active: true dashboard_import: package_import_url: github://esphome/bluetooth-proxies/esp32-generic.yaml@main bluetooth_proxy: button: - platform: safe_mode name: ${device_name}_safemode_boot entity_category: diagnostic switch: - platform: restart name: '${friendly_name} REBOOT' entity_category: diagnostic
iii. common/sensor_bletracker.yaml
sensor: - platform: homeassistant entity_id: input_number.esphome_measuredpower_${device_name} id: esphome_measuredpower_${device_name} - platform: ble_rssi mac_address: xx:xx:xx:xx:xx:xx name: ${device_name}_user1watch id: ${device_name}_user1watch filters: - throttle_average: 120s on_value: then: - component.update: ${device_name}_user1watch_distance - platform: template name: ${device_name}_user1watch_distance id: ${device_name}_user1watch_distance lambda: |- return pow(10, ((id(esphome_measuredpower_${device_name}).state - (id(${device_name}_user1watch).state)) / (10*2.4))); update_interval: 60s unit_of_measurement: 'm' - platform: uptime name: ${friendly_name} uptime filters: - lambda: return x / 60; unit_of_measurement: "min"