MQTT parsing help

Hi Community,

Been a while since posting as things have been going well for me. I recently switched to using MQTT with Owntracks and that was relatively easy to sort out. I’ve decided to expand my MQTT to include the @Bit-River script I use for Tile detection via BLE:

import paho.mqtt.publish as publish
import paho.mqtt.client as mqtt

from bluepy.btle import Scanner, DefaultDelegate

mqtt_host = 'x.x.x.x'
mqtt_port = 1883
mqtt_clid = 'blescan'
mqtt_auth = {
  'username':"username",
  'password':"password"
}

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        return()

scanner = Scanner().withDelegate(ScanDelegate())

while True:
  devices = scanner.scan()
  for dev in devices:
    publish.single(mqtt_clid + '/' + dev.addr,
      payload="true",
      hostname=mqtt_host,
      client_id=mqtt_clid,
      auth=mqtt_auth,
      port=mqtt_port,
      protocol=mqtt.MQTTv311)

This works perfectly fine, using MQTT.fx I can see the data:

image

I have a sensor setup as such:

  - platform: mqtt
    name: Chris Tile
    state_topic: "blescan/e9:16:98:77:eb:1a"
    expire_after: 60

Which works perfectly fine when the tile is detected, sensor reports as TRUE. Using my poor-man’s Faraday Cage (tinfoil) wrapping up the Tile to make it disappear it reports as UNKNOWN, as expected.

I’ve been messing with various value_template’s to get FALSE as a state if TRUE isn’t returned and haven’t succeeded. Here’s what I’m looking for (weird thing is, my attempts work fine in the HA Template Parser (realtime) but not in the sensor, ie:

  - platform: mqtt
    name: Chris Tile
    state_topic: "blescan/e9:16:98:77:eb:1a"
    expire_after: 60
    value_template: >-
      {% if value_jason %}
        true
      {% else %}
        false
      {% endif %}

Also tried "if is_state(‘sensor.chris_tile’,‘true’) ", “if not is_state…” etc. I just can’t get a FALSE value to report when the MQTT topic for the tile (or data or whatever the right terminology is) is no longer valid/expired.

I’m sure it’s simple as all heck but I can’t see the tree through the forest on this one.

1 Like

Well… it’s not the way I wanted to do it but I ended up creating a new sensor chris_tile_state which does the is_state comparison for true/unknown and works. Was hoping to keep this check all within the MQTT sensor itself :frowning:

It may be because you only give it a true value. That value should persist in mqtt indefinitely. Also, I’m not sure if mqtt converts strings into booleans. using (% if value_jason %} on a string will always return true if the string is not equal to ‘’. So, if the result truly is a string, the only way to verify it would be {% if value_jason == ‘true’ %}. You could also simplify it down to {{ value_jason == ‘true’ }} which will return a true if the statement passes and false if the statement fails. This means you don’t need to explicitly make an if statement returning true/false.

1 Like

@petro much thanks - this got me going in the right direction. Ended up using:

value_template: '{{ value = "true" }}'

Which gave me a True value when present in MQTT but Unknown when not (ie; Tile was not detected.) At least this taught me how to match on a value without a json attribute (is that the right term?) prefixing the actual wanted value.

In the end, my _state sensor ended up doing the trick for me. I could have altered the source to add something like: “detected”:“true” but there’s no way to add a false value because it only writes out what it sees thus the need for the secondary _state sensor.

Nevertheless, I very much appreciate the help, it got me to a point where I know I’m at the right way to do this.

1 Like

I know this is an old thread that I started but I’m updating it so it’s all in one place. I ended up adding to this script after playing around with @andrewjfreyer monitor script here:

I learned a lot playing around with this tool but unfortunately it wasn’t reliable enough for my use-case whereas my original script which I adapted from @Bit-River original script which was used to detect my tiles was. I’ve since updated my script to include my phone and my wife’s phone via Bluetooth.

His original script is here:

I had to pair the phones to my BT dongle on my Linux server (after hunting down and installing the Linux Broadcom firmware for it but it’s easy to find as it’s a BCM20702A)

[1478751.487994] usb 2-1.5: Product: BCM20702A0
[1478751.487999] usb 2-1.5: Manufacturer: Broadcom Corp
[1478751.488003] usb 2-1.5: SerialNumber: 5CF370891ECD
[1478751.598430] Bluetooth: hci0: BCM: chip id 63
[1478751.599345] Bluetooth: hci0: BCM: features 0x07
[1478751.615276] Bluetooth: hci0: BCM20702A
[1478751.616433] Bluetooth: hci0: BCM20702A1 (001.002.014) build 0000
[1478752.169121] bluetooth hci0: firmware: direct-loading firmware brcm/BCM20702A1-0b05-17cb.hcd
[1478753.038470] Bluetooth: hci0: BCM20702A1 (001.002.014) build 1347
[1478753.055441] Bluetooth: hci0: Broadcom Bluetooth Device

https://www.amazon.ca/ASUS-USB-Adapter-Bluetooth-USB-BT400/dp/B00DJ83070

In order to pair, I executed the following to pair the dongle to each phone:

hcitool cc 00:00:00:00:00:00 
hcitool auth 00:00:00:00:00:00 
hcitool dc 00:00:00:00:00:00 

Just replace 00:00:00:00:00:00 with the BT MAC address of your phone. On my wife’s iPhone I was at the home screen/desktop whereas on my Android (Oreo) I was in Bluetooth settings. In both cases, the devices asked me to pair to my Linux server with the dongle (device shows up as host name of the server.)

Here’s the updated script:

import paho.mqtt.publish as publish
import paho.mqtt.client as mqtt
import bluetooth
from bluepy.btle import Scanner, DefaultDelegate
from random import randint
from time import sleep

mqtt_host = 'mqtt_ip_addr'
mqtt_port = 1883
mqtt_clid = 'blescan'
mqtt_auth = {
  'username':"mqtt_username",
  'password':"mqtt_password"
}

bt_macs = [ 'xx:xx:xx:xx:xx:xx', 'yy:yy:yy:yy:yy:yy' ]

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        return()

scanner = Scanner().withDelegate(ScanDelegate())

while True:
  devices = scanner.scan()
  for dev in devices:
    publish.single(mqtt_clid + '/' + dev.addr,
      payload=1,
      hostname=mqtt_host,
      client_id=mqtt_clid,
      auth=mqtt_auth,
      port=mqtt_port,
      protocol=mqtt.MQTTv311)
  for bdaddr in bt_macs:
    btdev = bluetooth.lookup_name(bdaddr)
    if btdev is not None:
      publish.single(mqtt_clid + '/' + bdaddr,
        payload=1,
        hostname=mqtt_host,
        client_id=mqtt_clid,
        auth=mqtt_auth,
        port=mqtt_port,
        protocol=mqtt.MQTTv311)
  sleep(randint(30,60))

Just replace the MAC addresses in bt_macs with the MAC addresses of your devices to be scanned (not BTLE devices; these are picked up on the first part of the scan and injected into MQTT automatically) I’ve since adapted to a binary sensor to track these from MQTT and no longer doing any transform as I mentioned above originally. Script will write 1/on to the topic and the sensor will auto-set to 0/off after 90 seconds. There is a randomized 30-60 sleep period between each scan attempt.

binary_sensor:
  - platform: mqtt
    name: Chris Tile
    state_topic: "blescan/zz:zz:zz:zz:zz:zz"
    payload_on: 1
    off_delay: 90
    qos: 0
  - platform: mqtt
    name: Chris BT
    state_topic: "blescan/yy:yy:yy:yy:yy:yy"
    payload_on: 1
    off_delay: 90
    qos: 0


image

image

I run this from /etc/rc.local:

hciconfig hci0 down
hciconfig hci0 up

/usr/bin/python3.5 /home/hass/homeassistant/ble_scan.py &

Which I control via systemctl as a service (/etc/systemd/system/rc-local.service):

[Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

[Service]
 Type=forking
 ExecStart=/etc/rc.local start
 RemainAfterExit=no
 Restart=on-failure
 RestartSec=5s

[Install]
 WantedBy=multi-user.target
2 Likes