1128 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			1128 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
#!/usr/bin/env python
 | 
						|
# coding=utf-8
 | 
						|
 | 
						|
from __future__ import print_function
 | 
						|
from __future__ import with_statement
 | 
						|
 | 
						|
AP_DESCRIPTION="""
 | 
						|
Publish Home Assistant MQTT auto discovery topics for rtl_433 devices.
 | 
						|
 | 
						|
rtl_433_mqtt_hass.py connects to MQTT and subscribes to the rtl_433
 | 
						|
event stream that is published to MQTT by rtl_433. The script publishes
 | 
						|
additional MQTT topics that can be used by Home Assistant to automatically
 | 
						|
discover and minimally configure new devices.
 | 
						|
 | 
						|
The configuration topics published by this script tell Home Assistant
 | 
						|
what MQTT topics to subscribe to in order to receive the data published
 | 
						|
as device topics by MQTT.
 | 
						|
"""
 | 
						|
 | 
						|
AP_EPILOG="""
 | 
						|
It is strongly recommended to run rtl_433 with "-C si".
 | 
						|
This script requires rtl_433 to publish both event messages and device
 | 
						|
messages. If you've changed the device topic in rtl_433, use the same device
 | 
						|
topic with the "-T" parameter.
 | 
						|
 | 
						|
MQTT Username and Password can be set via the cmdline or passed in the
 | 
						|
environment: MQTT_USERNAME and MQTT_PASSWORD.
 | 
						|
 | 
						|
Prerequisites:
 | 
						|
 | 
						|
1. rtl_433 running separately publishing events and devices messages to MQTT.
 | 
						|
 | 
						|
2. Python installation
 | 
						|
* Python 3.x preferred.
 | 
						|
* Needs Paho-MQTT https://pypi.python.org/pypi/paho-mqtt
 | 
						|
 | 
						|
  Debian/raspbian:  apt install python3-paho-mqtt
 | 
						|
  Or
 | 
						|
  pip install paho-mqtt
 | 
						|
* Optional for running as a daemon see PEP 3143 - Standard daemon process library
 | 
						|
  (use Python 3.x or pip install python-daemon)
 | 
						|
 | 
						|
 | 
						|
Running:
 | 
						|
 | 
						|
This script can run continually as a daemon, where it will publish
 | 
						|
a configuration topic for the device events sent to MQTT by rtl_433
 | 
						|
every 10 minutes.
 | 
						|
 | 
						|
Alternatively if the rtl_433 devices in your environment change infrequently
 | 
						|
this script can use the MQTT retain flag to make the configuration topics
 | 
						|
persistent. The script will only need to be run when things change or if
 | 
						|
the MQTT server loses its retained messages.
 | 
						|
 | 
						|
Getting rtl_433 devices back after Home Assistant restarts will happen
 | 
						|
more quickly if MQTT retain is enabled. Note however that definitions
 | 
						|
for any transitient devices/false positives will retained indefinitely.
 | 
						|
 | 
						|
If your sensor values change infrequently and you prefer to write the most
 | 
						|
recent value even if not changed set -f to append "force_update = true" to
 | 
						|
all configs. This is useful if you're graphing the sensor data or want to
 | 
						|
alert on missing data.
 | 
						|
 | 
						|
If you have changed the topic structure from the default topics in the rtl433
 | 
						|
configuration use the -T parameter to set the same topic structure here.
 | 
						|
 | 
						|
Suggestions:
 | 
						|
 | 
						|
Running this script will cause a number of Home Assistant entities (sensors
 | 
						|
and binary sensors) to be created. These entities can linger for a while unless
 | 
						|
the topic is republished with an empty config string.  To avoid having to
 | 
						|
do a lot of clean up When running this initially or debugging, set this
 | 
						|
script to publish to a topic other than the one Home Assistant users (homeassistant).
 | 
						|
 | 
						|
MQTT Explorer (http://mqtt-explorer.com/) is a very nice GUI for
 | 
						|
working with MQTT. It is free, cross platform, and OSS. The structured
 | 
						|
hierarchical view makes it easier to understand what rtl_433 is publishing
 | 
						|
and how this script works with Home Assistant.
 | 
						|
 | 
						|
MQTT Explorer also makes it easy to publish an empty config topic to delete an
 | 
						|
entity from Home Assistant.
 | 
						|
 | 
						|
 | 
						|
As of 2020-10, Home Assistant MQTT auto discovery doesn't currently support
 | 
						|
supplying "friendly name", and "area" key, so some configuration must be
 | 
						|
done in Home Assistant.
 | 
						|
 | 
						|
There is a single global set of field mappings to Home Assistant meta data.
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
 | 
						|
# import daemon
 | 
						|
 | 
						|
 | 
						|
import os
 | 
						|
import argparse
 | 
						|
import logging
 | 
						|
import time
 | 
						|
import json
 | 
						|
import paho.mqtt.client as mqtt
 | 
						|
import re
 | 
						|
 | 
						|
 | 
						|
discovery_timeouts = {}
 | 
						|
 | 
						|
# Fields that get ignored when publishing to Home Assistant
 | 
						|
# (reduces noise to help spot missing field mappings)
 | 
						|
SKIP_KEYS = [ "type", "model", "subtype", "channel", "id", "mic", "mod",
 | 
						|
                "freq", "sequence_num", "message_type", "exception", "raw_msg" ]
 | 
						|
 | 
						|
 | 
						|
# Global mapping of rtl_433 field names to Home Assistant metadata.
 | 
						|
# @todo - should probably externalize to a config file
 | 
						|
# @todo - Model specific definitions might be needed
 | 
						|
 | 
						|
mappings = {
 | 
						|
    "temperature_C": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "T",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature",
 | 
						|
            "unit_of_measurement": "°C",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "temperature_1_C": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "T1",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature 1",
 | 
						|
            "unit_of_measurement": "°C",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "temperature_2_C": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "T2",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature 2",
 | 
						|
            "unit_of_measurement": "°C",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "temperature_3_C": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "T3",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature 3",
 | 
						|
            "unit_of_measurement": "°C",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "temperature_4_C": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "T4",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature 4",
 | 
						|
            "unit_of_measurement": "°C",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "temperature_F": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "F",
 | 
						|
        "config": {
 | 
						|
            "device_class": "temperature",
 | 
						|
            "name": "Temperature",
 | 
						|
            "unit_of_measurement": "°F",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    # This diagnostic sensor is useful to see when a device last sent a value,
 | 
						|
    # even if the value didn't change.
 | 
						|
    # https://community.home-assistant.io/t/send-metrics-to-influxdb-at-regular-intervals/9096
 | 
						|
    # https://github.com/home-assistant/frontend/discussions/13687
 | 
						|
    "time": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "UTC",
 | 
						|
        "config": {
 | 
						|
            "device_class": "timestamp",
 | 
						|
            "name": "Timestamp",
 | 
						|
            "entity_category": "diagnostic",
 | 
						|
            "enabled_by_default": False,
 | 
						|
            "icon": "mdi:clock-in"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "battery_ok": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "B",
 | 
						|
        "config": {
 | 
						|
            "device_class": "battery",
 | 
						|
            "name": "Battery",
 | 
						|
            "unit_of_measurement": "%",
 | 
						|
            "value_template": "{{ ((float(value) * 99)|round(0)) + 1 }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "battery_mV": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "mV",
 | 
						|
        "config": {
 | 
						|
            "device_class": "voltage",
 | 
						|
            "name": "Battery mV",
 | 
						|
            "unit_of_measurement": "mV",
 | 
						|
            "value_template": "{{ float(value) }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "supercap_V": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "V",
 | 
						|
        "config": {
 | 
						|
            "device_class": "voltage",
 | 
						|
            "name": "Supercap V",
 | 
						|
            "unit_of_measurement": "V",
 | 
						|
            "value_template": "{{ float(value) }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "humidity": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "H",
 | 
						|
        "config": {
 | 
						|
            "device_class": "humidity",
 | 
						|
            "name": "Humidity",
 | 
						|
            "unit_of_measurement": "%",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "humidity_1": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "H1",
 | 
						|
        "config": {
 | 
						|
            "device_class": "humidity",
 | 
						|
            "name": "Humidity 1",
 | 
						|
            "unit_of_measurement": "%",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "humidity_2": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "H2",
 | 
						|
        "config": {
 | 
						|
            "device_class": "humidity",
 | 
						|
            "name": "Humidity 2",
 | 
						|
            "unit_of_measurement": "%",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "moisture": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "M",
 | 
						|
        "config": {
 | 
						|
            "device_class": "moisture",
 | 
						|
            "name": "Moisture",
 | 
						|
            "unit_of_measurement": "%",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "detect_wet": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "moisture",
 | 
						|
        "config": {
 | 
						|
            "name": "Water Sensor",
 | 
						|
            "device_class": "moisture",
 | 
						|
            "force_update": "true",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "pressure_hPa": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "P",
 | 
						|
        "config": {
 | 
						|
            "device_class": "pressure",
 | 
						|
            "name": "Pressure",
 | 
						|
            "unit_of_measurement": "hPa",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "pressure_kPa": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "P",
 | 
						|
        "config": {
 | 
						|
            "device_class": "pressure",
 | 
						|
            "name": "Pressure",
 | 
						|
            "unit_of_measurement": "kPa",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_speed_km_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind Speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_avg_km_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind Speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_avg_mi_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind Speed",
 | 
						|
            "unit_of_measurement": "mi/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_avg_m_s": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind Average",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ (float(value|float) * 3.6) | round(2) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_speed_m_s": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind Speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ float(value|float) * 3.6 }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "gust_speed_km_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "GS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Gust Speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_max_km_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "GS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind max speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_max_m_s": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "GS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Wind max",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ (float(value|float) * 3.6) | round(2) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "gust_speed_m_s": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "GS",
 | 
						|
        "config": {
 | 
						|
            "device_class": "wind_speed",
 | 
						|
            "name": "Gust Speed",
 | 
						|
            "unit_of_measurement": "km/h",
 | 
						|
            "value_template": "{{ float(value|float) * 3.6 }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "wind_dir_deg": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "WD",
 | 
						|
        "config": {
 | 
						|
            "name": "Wind Direction",
 | 
						|
            "unit_of_measurement": "°",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "rain_mm": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "RT",
 | 
						|
        "config": {
 | 
						|
            "device_class": "precipitation",
 | 
						|
            "name": "Rain Total",
 | 
						|
            "unit_of_measurement": "mm",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "total_increasing"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "rain_rate_mm_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "RR",
 | 
						|
        "config": {
 | 
						|
            "device_class": "precipitation_intensity",
 | 
						|
            "name": "Rain Rate",
 | 
						|
            "unit_of_measurement": "mm/h",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "rain_in": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "RT",
 | 
						|
        "config": {
 | 
						|
            "device_class": "precipitation",
 | 
						|
            "name": "Rain Total",
 | 
						|
            "unit_of_measurement": "in",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "total_increasing"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "rain_rate_in_h": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "RR",
 | 
						|
        "config": {
 | 
						|
            "device_class": "precipitation_intensity",
 | 
						|
            "name": "Rain Rate",
 | 
						|
            "unit_of_measurement": "in/h",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "reed_open": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "reed_open",
 | 
						|
        "config": {
 | 
						|
            "device_class": "safety",
 | 
						|
            "force_update": "true",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "contact_open": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "contact_open",
 | 
						|
        "config": {
 | 
						|
            "device_class": "safety",
 | 
						|
            "force_update": "true",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "tamper": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "tamper",
 | 
						|
        "config": {
 | 
						|
            "device_class": "safety",
 | 
						|
            "force_update": "true",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "alarm": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "alarm",
 | 
						|
        "config": {
 | 
						|
            "device_class": "safety",
 | 
						|
            "force_update": "true",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "rssi": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "rssi",
 | 
						|
        "config": {
 | 
						|
            "device_class": "signal_strength",
 | 
						|
            "unit_of_measurement": "dB",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "snr": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "snr",
 | 
						|
        "config": {
 | 
						|
            "device_class": "signal_strength",
 | 
						|
            "unit_of_measurement": "dB",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "noise": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "noise",
 | 
						|
        "config": {
 | 
						|
            "device_class": "signal_strength",
 | 
						|
            "unit_of_measurement": "dB",
 | 
						|
            "value_template": "{{ value|float|round(2) }}",
 | 
						|
            "state_class": "measurement",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "depth_cm": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "D",
 | 
						|
        "config": {
 | 
						|
            "name": "Depth",
 | 
						|
            "unit_of_measurement": "cm",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "power_W": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "watts",
 | 
						|
        "config": {
 | 
						|
            "device_class": "power",
 | 
						|
            "name": "Power",
 | 
						|
            "unit_of_measurement": "W",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "energy_kWh": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "kwh",
 | 
						|
        "config": {
 | 
						|
            "device_class": "energy",
 | 
						|
            "name": "Energy",
 | 
						|
            "unit_of_measurement": "kWh",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "current_A": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "A",
 | 
						|
        "config": {
 | 
						|
            "device_class": "current",
 | 
						|
            "name": "Current",
 | 
						|
            "unit_of_measurement": "A",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "voltage_V": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "V",
 | 
						|
        "config": {
 | 
						|
            "device_class": "voltage",
 | 
						|
            "name": "Voltage",
 | 
						|
            "unit_of_measurement": "V",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "light_lux": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "lux",
 | 
						|
        "config": {
 | 
						|
            "device_class": "illuminance",
 | 
						|
            "name": "Outside Luminance",
 | 
						|
            "unit_of_measurement": "lx",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "lux": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "lux",
 | 
						|
        "config": {
 | 
						|
            "device_class": "illuminance",
 | 
						|
            "name": "Outside Luminance",
 | 
						|
            "unit_of_measurement": "lx",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "uv": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "uv",
 | 
						|
        "config": {
 | 
						|
            "name": "UV Index",
 | 
						|
            "unit_of_measurement": "UV Index",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
    "uvi": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "uvi",
 | 
						|
        "config": {
 | 
						|
            "name": "UV Index",
 | 
						|
            "unit_of_measurement": "UV Index",
 | 
						|
            "value_template": "{{ value|float|round(1) }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "storm_dist_km": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "stdist",
 | 
						|
        "config": {
 | 
						|
            "name": "Lightning Distance",
 | 
						|
            "unit_of_measurement": "km",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "storm_dist": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "stdist",
 | 
						|
        "config": {
 | 
						|
            "name": "Lightning Distance",
 | 
						|
            "unit_of_measurement": "mi",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "strike_distance": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "stdist",
 | 
						|
        "config": {
 | 
						|
            "name": "Lightning Distance",
 | 
						|
            "unit_of_measurement": "mi",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "strike_count": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "strcnt",
 | 
						|
        "config": {
 | 
						|
            "name": "Lightning Strike Count",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "total_increasing"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "consumption_data": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "consumption",
 | 
						|
        "config": {
 | 
						|
            "name": "SCM Consumption Value",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "total_increasing",
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "consumption": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "consumption",
 | 
						|
        "config": {
 | 
						|
            "name": "SCMplus Consumption Value",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "total_increasing",
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "channel": {
 | 
						|
        "device_type": "device_automation",
 | 
						|
        "object_suffix": "CH",
 | 
						|
        "config": {
 | 
						|
           "automation_type": "trigger",
 | 
						|
           "type": "button_short_release",
 | 
						|
           "subtype": "button_1",
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "button": {
 | 
						|
        "device_type": "device_automation",
 | 
						|
        "object_suffix": "BTN",
 | 
						|
        "config": {
 | 
						|
           "automation_type": "trigger",
 | 
						|
           "type": "button_short_release",
 | 
						|
           "subtype": "button_2",
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    # WH45, WH290
 | 
						|
    "pm2_5_ug_m3": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "PM25",
 | 
						|
        "config": {
 | 
						|
            "device_class": "pm25",
 | 
						|
            "name": "PM 2.5 Concentration",
 | 
						|
            "unit_of_measurement": "µg/m³",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    # WH45
 | 
						|
    "pm10_ug_m3": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "PM10",
 | 
						|
        "config": {
 | 
						|
            "device_class": "pm10",
 | 
						|
            "name": "PM 10 Concentration",
 | 
						|
            "unit_of_measurement": "µg/m³",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    # WH290
 | 
						|
    "estimated_pm10_0_ug_m3": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "PM10",
 | 
						|
        "config": {
 | 
						|
            "device_class": "pm10",
 | 
						|
            "name": "Estimated PM 10 Concentration",
 | 
						|
            "unit_of_measurement": "µg/m³",
 | 
						|
            "value_template": "{{ value|float }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    # WH45
 | 
						|
    "co2_ppm": {
 | 
						|
        "device_type": "sensor",
 | 
						|
        "object_suffix": "CO2",
 | 
						|
        "config": {
 | 
						|
            "device_class": "carbon_dioxide",
 | 
						|
            "name": "CO2 Concentration",
 | 
						|
            "unit_of_measurement": "ppm",
 | 
						|
            "value_template": "{{ value|int }}",
 | 
						|
            "state_class": "measurement"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    "ext_power": {
 | 
						|
        "device_type": "binary_sensor",
 | 
						|
        "object_suffix": "extpwr",
 | 
						|
        "config": {
 | 
						|
            "device_class": "power",
 | 
						|
            "name": "External Power",
 | 
						|
            "payload_on": "1",
 | 
						|
            "payload_off": "0",
 | 
						|
            "entity_category": "diagnostic"
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
# Use secret_knock to trigger device automations for Honeywell ActivLink
 | 
						|
# doorbells. We have this outside of mappings as we need to configure two
 | 
						|
# different configuration topics.
 | 
						|
secret_knock_mappings = [
 | 
						|
 | 
						|
    {
 | 
						|
        "device_type": "device_automation",
 | 
						|
        "object_suffix": "Knock",
 | 
						|
        "config": {
 | 
						|
            "automation_type": "trigger",
 | 
						|
            "type": "button_short_release",
 | 
						|
            "subtype": "button_1",
 | 
						|
            "payload": 0,
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    {
 | 
						|
        "device_type": "device_automation",
 | 
						|
        "object_suffix": "Secret-Knock",
 | 
						|
        "config": {
 | 
						|
            "automation_type": "trigger",
 | 
						|
            "type": "button_triple_press",
 | 
						|
            "subtype": "button_1",
 | 
						|
            "payload": 1,
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
]
 | 
						|
 | 
						|
TOPIC_PARSE_RE = re.compile(r'\[(?P<slash>/?)(?P<token>[^\]:]+):?(?P<default>[^\]:]*)\]')
 | 
						|
 | 
						|
def mqtt_connect(client, userdata, flags, rc):
 | 
						|
    """Callback for MQTT connects."""
 | 
						|
 | 
						|
    logging.info("MQTT connected: " + mqtt.connack_string(rc))
 | 
						|
    if rc != 0:
 | 
						|
        logging.error("Could not connect. Error: " + str(rc))
 | 
						|
    else:
 | 
						|
        logging.info("Subscribing to: " + args.rtl_topic)
 | 
						|
        client.subscribe(args.rtl_topic)
 | 
						|
 | 
						|
 | 
						|
def mqtt_disconnect(client, userdata, rc):
 | 
						|
    """Callback for MQTT disconnects."""
 | 
						|
    logging.info("MQTT disconnected: " + mqtt.connack_string(rc))
 | 
						|
 | 
						|
 | 
						|
def mqtt_message(client, userdata, msg):
 | 
						|
    """Callback for MQTT message PUBLISH."""
 | 
						|
    logging.debug("MQTT message: " + json.dumps(msg.payload.decode()))
 | 
						|
 | 
						|
    try:
 | 
						|
        # Decode JSON payload
 | 
						|
        data = json.loads(msg.payload.decode())
 | 
						|
 | 
						|
    except json.decoder.JSONDecodeError:
 | 
						|
        logging.error("JSON decode error: " + msg.payload.decode())
 | 
						|
        return
 | 
						|
 | 
						|
    topicprefix = "/".join(msg.topic.split("/", 2)[:2])
 | 
						|
    bridge_event_to_hass(client, topicprefix, data)
 | 
						|
 | 
						|
 | 
						|
def sanitize(text):
 | 
						|
    """Sanitize a name for Graphite/MQTT use."""
 | 
						|
    return (text
 | 
						|
            .replace(" ", "_")
 | 
						|
            .replace("/", "_")
 | 
						|
            .replace(".", "_")
 | 
						|
            .replace("&", ""))
 | 
						|
 | 
						|
def rtl_433_device_info(data, topic_prefix):
 | 
						|
    """Return rtl_433 device topic to subscribe to for a data element, based on the
 | 
						|
    rtl_433 device topic argument, as well as the device identifier"""
 | 
						|
 | 
						|
    path_elements = []
 | 
						|
    id_elements = []
 | 
						|
    last_match_end = 0
 | 
						|
    # The default for args.device_topic_suffix is the same topic structure
 | 
						|
    # as set by default in rtl433 config
 | 
						|
    for match in re.finditer(TOPIC_PARSE_RE, args.device_topic_suffix):
 | 
						|
        path_elements.append(args.device_topic_suffix[last_match_end:match.start()])
 | 
						|
        key = match.group(2)
 | 
						|
        if key in data:
 | 
						|
            # If we have this key, prepend a slash if needed
 | 
						|
            if match.group(1):
 | 
						|
                path_elements.append('/')
 | 
						|
            element = sanitize(str(data[key]))
 | 
						|
            path_elements.append(element)
 | 
						|
            id_elements.append(element)
 | 
						|
        elif match.group(3):
 | 
						|
            path_elements.append(match.group(3))
 | 
						|
        last_match_end = match.end()
 | 
						|
 | 
						|
    path = ''.join(list(filter(lambda item: item, path_elements)))
 | 
						|
    id = '-'.join(id_elements)
 | 
						|
    return (f"{topic_prefix}/{path}", id)
 | 
						|
 | 
						|
 | 
						|
def publish_config(mqttc, topic, model, object_id, mapping, key=None):
 | 
						|
    """Publish Home Assistant auto discovery data."""
 | 
						|
    global discovery_timeouts
 | 
						|
 | 
						|
    device_type = mapping["device_type"]
 | 
						|
    object_suffix = mapping["object_suffix"]
 | 
						|
    object_name = "-".join([object_id, object_suffix])
 | 
						|
 | 
						|
    path = "/".join([args.discovery_prefix, device_type, object_id, object_name, "config"])
 | 
						|
 | 
						|
    # check timeout
 | 
						|
    now = time.time()
 | 
						|
    if path in discovery_timeouts:
 | 
						|
        if discovery_timeouts[path] > now:
 | 
						|
            logging.debug("Discovery timeout in the future for: " + path)
 | 
						|
            return False
 | 
						|
 | 
						|
    discovery_timeouts[path] = now + args.discovery_interval
 | 
						|
 | 
						|
    config = mapping["config"].copy()
 | 
						|
 | 
						|
    # Device Automation configuration is in a different structure compared to
 | 
						|
    # all other mqtt discovery types.
 | 
						|
    # https://www.home-assistant.io/integrations/device_trigger.mqtt/
 | 
						|
    if device_type == 'device_automation':
 | 
						|
        config["topic"] = topic
 | 
						|
        config["platform"] = 'mqtt'
 | 
						|
    else:
 | 
						|
        readable_name = mapping["config"]["name"] if "name" in mapping["config"] else key
 | 
						|
        config["state_topic"] = topic
 | 
						|
        config["unique_id"] = object_name
 | 
						|
        config["name"] = readable_name
 | 
						|
    config["device"] = { "identifiers": [object_id], "name": object_id, "model": model, "manufacturer": "rtl_433" }
 | 
						|
 | 
						|
    if args.force_update:
 | 
						|
        config["force_update"] = "true"
 | 
						|
 | 
						|
    if args.expire_after:
 | 
						|
        config["expire_after"] = args.expire_after
 | 
						|
 | 
						|
    logging.debug(path + ":" + json.dumps(config))
 | 
						|
 | 
						|
    mqttc.publish(path, json.dumps(config), retain=args.retain)
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
def bridge_event_to_hass(mqttc, topic_prefix, data):
 | 
						|
    """Translate some rtl_433 sensor data to Home Assistant auto discovery."""
 | 
						|
 | 
						|
    if "model" not in data:
 | 
						|
        # not a device event
 | 
						|
        logging.debug("Model is not defined. Not sending event to Home Assistant.")
 | 
						|
        return
 | 
						|
 | 
						|
    model = sanitize(data["model"])
 | 
						|
 | 
						|
    skipped_keys = []
 | 
						|
    published_keys = []
 | 
						|
 | 
						|
    base_topic, device_id = rtl_433_device_info(data, topic_prefix)
 | 
						|
    if not device_id:
 | 
						|
        # no unique device identifier
 | 
						|
        logging.warning("No suitable identifier found for model: %s", model)
 | 
						|
        return
 | 
						|
 | 
						|
    if args.ids and "id" in data and data.get("id") not in args.ids:
 | 
						|
        # not in the safe list
 | 
						|
        logging.debug("Device (%s) is not in the desired list of device ids: [%s]" % (data["id"], ids))
 | 
						|
        return
 | 
						|
 | 
						|
    # detect known attributes
 | 
						|
    for key in data.keys():
 | 
						|
        if key in mappings:
 | 
						|
            # topic = "/".join([topicprefix,"devices",model,instance,key])
 | 
						|
            topic = "/".join([base_topic, key])
 | 
						|
            if publish_config(mqttc, topic, model, device_id, mappings[key], key):
 | 
						|
                published_keys.append(key)
 | 
						|
        else:
 | 
						|
            if key not in SKIP_KEYS:
 | 
						|
                skipped_keys.append(key)
 | 
						|
 | 
						|
    if "secret_knock" in data.keys():
 | 
						|
        for m in secret_knock_mappings:
 | 
						|
            topic = "/".join([base_topic, "secret_knock"])
 | 
						|
            if publish_config(mqttc, topic, model, device_id, m, "secret_knock"):
 | 
						|
                published_keys.append("secret_knock")
 | 
						|
 | 
						|
    if published_keys:
 | 
						|
        logging.info("Published %s: %s" % (device_id, ", ".join(published_keys)))
 | 
						|
 | 
						|
        if skipped_keys:
 | 
						|
            logging.info("Skipped %s: %s" % (device_id, ", ".join(skipped_keys)))
 | 
						|
 | 
						|
 | 
						|
def rtl_433_bridge():
 | 
						|
    """Run a MQTT Home Assistant auto discovery bridge for rtl_433."""
 | 
						|
 | 
						|
    if hasattr(mqtt, 'CallbackAPIVersion'):  # paho >= 2.0.0
 | 
						|
        mqttc = mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION1)
 | 
						|
    else:
 | 
						|
        mqttc = mqtt.Client()
 | 
						|
 | 
						|
    if args.debug:
 | 
						|
        mqttc.enable_logger()
 | 
						|
 | 
						|
    if args.user is not None:
 | 
						|
        mqttc.username_pw_set(args.user, args.password)
 | 
						|
 | 
						|
    if args.ca_cert is not None:
 | 
						|
        mqttc.tls_set(ca_certs=args.ca_cert)
 | 
						|
 | 
						|
    mqttc.on_connect = mqtt_connect
 | 
						|
    mqttc.on_disconnect = mqtt_disconnect
 | 
						|
    mqttc.on_message = mqtt_message
 | 
						|
    mqttc.connect_async(args.host, args.port, 60)
 | 
						|
    logging.debug("MQTT Client: Starting Loop")
 | 
						|
    mqttc.loop_start()
 | 
						|
 | 
						|
    while True:
 | 
						|
        time.sleep(1)
 | 
						|
 | 
						|
 | 
						|
def run():
 | 
						|
    """Run main or daemon."""
 | 
						|
    # with daemon.DaemonContext(files_preserve=[sock]):
 | 
						|
    #  detach_process=True
 | 
						|
    #  uid
 | 
						|
    #  gid
 | 
						|
    #  working_directory
 | 
						|
    rtl_433_bridge()
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    logging.basicConfig(format='[%(asctime)s] %(levelname)s:%(name)s:%(message)s',datefmt='%Y-%m-%dT%H:%M:%S%z')
 | 
						|
    logging.getLogger().setLevel(logging.INFO)
 | 
						|
 | 
						|
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
 | 
						|
                                     description=AP_DESCRIPTION,
 | 
						|
                                     epilog=AP_EPILOG)
 | 
						|
 | 
						|
    parser.add_argument("-d", "--debug", action="store_true")
 | 
						|
    parser.add_argument("-q", "--quiet", action="store_true")
 | 
						|
    parser.add_argument("-u", "--user", type=str, help="MQTT username")
 | 
						|
    parser.add_argument("-P", "--password", type=str, help="MQTT password")
 | 
						|
    parser.add_argument("-H", "--host", type=str, default="127.0.0.1",
 | 
						|
                        help="MQTT hostname to connect to (default: %(default)s)")
 | 
						|
    parser.add_argument("-p", "--port", type=int, default=1883,
 | 
						|
                        help="MQTT port (default: %(default)s)")
 | 
						|
    parser.add_argument("-c", "--ca_cert", type=str, help="MQTT TLS CA certificate path")
 | 
						|
    parser.add_argument("-r", "--retain", action="store_true")
 | 
						|
    parser.add_argument("-f", "--force_update", action="store_true",
 | 
						|
                        help="Append 'force_update = true' to all configs.")
 | 
						|
    parser.add_argument("-R", "--rtl-topic", type=str,
 | 
						|
                        default="rtl_433/+/events",
 | 
						|
                        dest="rtl_topic",
 | 
						|
                        help="rtl_433 MQTT event topic to subscribe to (default: %(default)s)")
 | 
						|
    parser.add_argument("-D", "--discovery-prefix", type=str,
 | 
						|
                        dest="discovery_prefix",
 | 
						|
                        default="homeassistant",
 | 
						|
                        help="Home Assistant MQTT topic prefix (default: %(default)s)")
 | 
						|
    # This defaults to the rtl433 config default, so we assemble the same topic structure
 | 
						|
    parser.add_argument("-T", "--device-topic_suffix", type=str,
 | 
						|
                        dest="device_topic_suffix",
 | 
						|
                        default="devices[/type][/model][/subtype][/channel][/id]",
 | 
						|
                        help="rtl_433 device topic suffix (default: %(default)s)")
 | 
						|
    parser.add_argument("-i", "--interval", type=int,
 | 
						|
                        dest="discovery_interval",
 | 
						|
                        default=600,
 | 
						|
                        help="Interval to republish config topics in seconds (default: %(default)d)")
 | 
						|
    parser.add_argument("-x", "--expire-after", type=int,
 | 
						|
                        dest="expire_after",
 | 
						|
                        help="Number of seconds with no updates after which the sensor becomes unavailable")
 | 
						|
    parser.add_argument("-I", "--ids", type=int, nargs="+",
 | 
						|
                        help="ID's of devices that will be discovered (omit for all)")
 | 
						|
    args = parser.parse_args()
 | 
						|
 | 
						|
    if args.debug and args.quiet:
 | 
						|
        logging.critical("Debug and quiet can not be specified at the same time")
 | 
						|
        exit(1)
 | 
						|
 | 
						|
    if args.debug:
 | 
						|
        logging.info("Enabling debug logging")
 | 
						|
        logging.getLogger().setLevel(logging.DEBUG)
 | 
						|
    if args.quiet:
 | 
						|
        logging.getLogger().setLevel(logging.ERROR)
 | 
						|
 | 
						|
    # allow setting MQTT username and password via environment variables
 | 
						|
    if not args.user and 'MQTT_USERNAME' in os.environ:
 | 
						|
        args.user = os.environ['MQTT_USERNAME']
 | 
						|
 | 
						|
    if not args.password and 'MQTT_PASSWORD' in os.environ:
 | 
						|
        args.password = os.environ['MQTT_PASSWORD']
 | 
						|
 | 
						|
    if not args.user or not args.password:
 | 
						|
        logging.warning("User or password is not set. Check credentials if subscriptions do not return messages.")
 | 
						|
 | 
						|
    if args.ids:
 | 
						|
        ids = ', '.join(str(id) for id in args.ids)
 | 
						|
        logging.info("Only discovering devices with ids: [%s]" % ids)
 | 
						|
    else:
 | 
						|
        logging.info("Discovering all devices")
 | 
						|
 | 
						|
    run()
 |