Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read rtl_433_mqtt_hass.py configuration from rtl_433.conf #2841

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions examples/rtl_433_mqtt_hass.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,26 @@

discovery_timeouts = {}

CONFIG_PATHS = [
"/etc/rtl_433",
"/usr/local/etc/rtl_433",
os.path.expanduser("~/.config/rtl_433"),
os.path.dirname(os.path.realpath(__file__)),
os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
]
CONFIG_PATHS = [p for p in CONFIG_PATHS if os.path.exists(p)]

CONFIG_FILENAME = "rtl_433.conf"

def find_file_in_path(filename, paths, success_log=None):
for path in paths:
fullpath = os.path.join(path, filename)
if os.path.exists(fullpath):
if success_log:
logging.info(success_log % path)
return fullpath
raise FileNotFoundError(f"File {filename} not found in paths: {paths}")

# 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",
Expand Down Expand Up @@ -863,6 +883,27 @@ def rtl_433_device_info(data, topic_prefix):
return (f"{topic_prefix}/{path}", id)


def device_topic_from_data(data):
device_topic_suffix = ''+args.device_topic_suffix
keys = re.findall(r'\[([^\]]+)\]', device_topic_suffix)
for key in keys:
default = None
k = key
if key.startswith('/'):
k = key[1:]
if ':' in key:
k, default = key.split(':')
value = data.get(k, default)
if key.startswith('/') and value is None:
device_topic_suffix = device_topic_suffix.replace(f'[{key}]', '')
continue
elif key.startswith('/') and value is not None:
value = '/' + str(value)

device_topic_suffix = device_topic_suffix.replace(f'[{key}]', str(value))
return device_topic_suffix


def publish_config(mqttc, topic, model, object_id, mapping, key=None):
"""Publish Home Assistant auto discovery data."""
global discovery_timeouts
Expand Down Expand Up @@ -923,6 +964,8 @@ def bridge_event_to_hass(mqttc, topic_prefix, data):
published_keys = []

base_topic, device_id = rtl_433_device_info(data, topic_prefix)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit, but it feels a bit unclean that you didn't remove the old assignment to base_topic.

I know this whole script needs work, but If you don't want to refactor rtl_433_device_info, then just use:

_, device_id = rtl_433_device_info(data, topic_prefix).

The underscore seems to be the prevailing convention for a value you don't care about when unpacking a tuple during assignment.

base_topic = device_topic_from_data(data)

if not device_id:
# no unique device identifier
logging.warning("No suitable identifier found for model: %s", model)
Expand Down Expand Up @@ -960,7 +1003,7 @@ def bridge_event_to_hass(mqttc, topic_prefix, data):
def rtl_433_bridge():
"""Run a MQTT Home Assistant auto discovery bridge for rtl_433."""

mqttc = mqtt.Client()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)

if args.debug:
mqttc.enable_logger()
Expand Down Expand Up @@ -1053,13 +1096,67 @@ def run():
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")

found_config = False
try:
with open(find_file_in_path(CONFIG_FILENAME, CONFIG_PATHS)) as f:
found = re.findall('^output (.*)$', f.read(), re.MULTILINE)
devices = None
events = None
retain = None
user = None
password = None
host = None
port = None
for find in found:
if "mqtt" not in find: continue
host = re.findall(r'mqtt://(.*?),', find)[0]
devices = re.findall(r'devices=(.*?)(?:,+|$)', find)
if len(devices) > 0: devices = devices[0]
events = re.findall(r'events=(.*?)(?:,+|$)', find)
if len(events) > 0: events = events[0]
retain = re.findall(r'retain=(.*?)(?:,+|$)', find)
if len(retain) > 0: retain = retain[0]

if '@' in host:
user, password = host.split('@')[0].split(':')
host = host.split('@')[1]
if ':' in host:
host, port = host.split(':')
found_config = True
break
except FileNotFoundError:
logging.warning("Config file not found")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to be less ambiguous about what config file you are talking about in this and the other log messages.

I would explicitly mention that it is rtl_433's config file and/or the path.

Some of the Home Assistant add-ons have their own config files for this script.

except Exception as e:
logging.exception("Error reading config file")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, be explicit about the file.


if found_config:
if host:
logging.info(f"Using host {host} from config")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as about, be more explicit about the source, "from config" is a bit ambiguous.

args.host = host
if port:
logging.info(f"Using port {port} from config")
args.port = int(port)
if user:
logging.info(f"Using user {user} from config")
args.user = user
if password:
args.password = password
if retain:
logging.info(f"Using retain {retain} from config")
args.retain = int(retain)
if devices:
logging.info(f"Using device_topic_suffix {devices} from config")
args.device_topic_suffix = devices
if events:
logging.info(f"Using rtl_topic {events} from config")
args.rtl_topic = events
if not args.user or not args.password:
logging.warning("User or password is not set. Check credentials if subscriptions do not return messages.")

run()
Loading