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

Support abbreviations in discovery topic #16635

Merged
merged 13 commits into from
Oct 12, 2018

Conversation

emontnemery
Copy link
Contributor

@emontnemery emontnemery commented Sep 15, 2018

Description:

Support abbreviations in MQTT discovery messages. Purpose is to avoid out-of-memory situations on memory constrained devices when sending discovery topic.
Note: This is item 4 from home-assistant/architecture#70

Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#6294

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox.

Copy link
Member

@fabaff fabaff left a comment

Choose a reason for hiding this comment

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

A change like this would also require a test.

homeassistant/components/mqtt/discovery.py Outdated Show resolved Hide resolved
homeassistant/components/mqtt/discovery.py Show resolved Hide resolved
@emontnemery
Copy link
Contributor Author

emontnemery commented Sep 16, 2018

@fabaff Thanks for review!
OK, Test case added to test the expansion
Also, the list of abbreviations should now cover all words in the configuration variables for supported devices.


def expand(matchobj):
"""Helper to expand a possibly abbreviated word."""
abbreviation = matchobj.group(0)

Choose a reason for hiding this comment

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

unexpected indentation

pattern = r'(?:(?<=^)|(?<=_))[^\W_]+(?=_|$)'

def expand(matchobj):
"""Helper to expand a possibly abbreviated word."""

Choose a reason for hiding this comment

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

expected an indented block
IndentationError: expected an indented block

@emontnemery
Copy link
Contributor Author

Btw, I didn't add words relevant for Alarm control panel, HVAC and Lock (home-assistant/home-assistant.io#6295). I'll add those and and push a new commit.

@balloob
Copy link
Member

balloob commented Sep 17, 2018

We should wait with this PR until we finalized our discussion in the architecture repo?

@emontnemery emontnemery changed the title Support abbreviations in discovery topic WIP - Support abbreviations in discovery topic Sep 17, 2018
@emontnemery
Copy link
Contributor Author

@balloob Definitely, I added WIP tag


from tests.common import async_fire_mqtt_message, mock_coro

from tests.common import (

Choose a reason for hiding this comment

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

'tests.common.mock_mqtt_component' imported but unused
'tests.common.fire_mqtt_message' imported but unused
'tests.common.get_test_home_assistant' imported but unused

@@ -1,12 +1,15 @@
"""The tests for the MQTT discovery."""
import asyncio
import unittest

Choose a reason for hiding this comment

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

'unittest' imported but unused

@emontnemery
Copy link
Contributor Author

@fabaff Please have a look at the updated version:

  • Test case added
  • Support for topic prefix added as discussed in MQTT Discovery improvement proposals architecture#70
  • Abbreviations of all words used in configuration variables added
    (My idea initially was to only support abbreviations of the longest words as well as of the most frequently used words)

if TOPIC_PREFIX in payload:
prefix = payload[TOPIC_PREFIX]
for key in payload.keys():
payload[key] = payload[key].replace(TOPIC_PREFIX, prefix)
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't do the replace like this, as it would also replace something like hello/~paulus. Instead, do this:

for key, value in payload.items():
    if key[0] == TOPIC_PREFIX:
        payload[key] = "{}{}".format(TOPIC_PREFIX, value[1:])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, and that limits the topic prefix to the very beginning, maybe that's a reasonable limitation?
Perhaps it also makes sense to only do the replacement if the configuration variable ends with _topic, so something like:

for key, value in payload.items():
    if value[0] == TOPIC_PREFIX and key.endswith('_topic'):
        payload[key] = "{}{}".format(TOPIC_PREFIX, value[1:])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in latest commit.

@@ -62,6 +142,31 @@
return

payload = dict(payload)

for key in list(payload.keys()):
Copy link
Member

Choose a reason for hiding this comment

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

Wait, how does this work, I was under the impression that we had a fixed list of abbreviation -> full key, which would not require a regex. If we can, let's avoid regex.

Copy link
Contributor Author

@emontnemery emontnemery Sep 24, 2018

Choose a reason for hiding this comment

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

It's a (long) fixed list of abbreviated words, the purpose of the regex was to split the configuration variable name in words and attempt to expand word by word.
Regex replaced with string.split()

if TOPIC_PREFIX in payload:
prefix = payload[TOPIC_PREFIX]
for key, value in payload.items():
if value.startswith(TOPIC_PREFIX) and key.endswith('_topic'):
Copy link
Member

Choose a reason for hiding this comment

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

value[0] will be faster.

Copy link
Contributor Author

@emontnemery emontnemery Sep 24, 2018

Choose a reason for hiding this comment

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

Fixed.

for key, value in payload.items():
if value.startswith(TOPIC_PREFIX) and key.endswith('_topic'):
payload[key] = "{}{}".format(prefix,
value[len(TOPIC_PREFIX):])
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we'll ever change the prefix, I think just using 1: would be more readable.

Copy link
Contributor Author

@emontnemery emontnemery Sep 24, 2018

Choose a reason for hiding this comment

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

Fixed.

@emontnemery emontnemery force-pushed the mqtt_discovery_shortened branch from ded3945 to 8fc9c96 Compare September 24, 2018 17:06
@emontnemery
Copy link
Contributor Author

@balloob Yes, exactly. I'm not sure what the rationale for the "backwards" default topic scheme is, maybe to make it easier to subscribe to all state updates as you suggest.
But the point is that it is too restrictive if the "prefix" introduced by this PR must be at the start of the configuration value. Do you consider my updated suggestion acceptable?

@balloob
Copy link
Member

balloob commented Oct 1, 2018

Why can't we have nice things 😢

Looks like in Tasmota the topics can be configured to have the tasmota "prefix" be last: https://github.com/arendst/Sonoff-Tasmota/wiki/MQTT-Features#mqtt-topic-definition

@emontnemery
Copy link
Contributor Author

@balloob Yes, exactly, and "prefix" last is even the default setting.

Hence, can I update the PR by replacing:

# Replace TOPIC_PREFIX at the start of configuration value
if TOPIC_PREFIX in payload:
    prefix = payload[TOPIC_PREFIX]
    for key, value in payload.items():
        if value[0] == TOPIC_PREFIX and key.endswith('_topic'):
            payload[key] = "{}{}".format(prefix, value[1:])

with:

# Replace BASE_TOPIC in configuration value
if BASE_TOPIC in payload:
    base_topic = payload[BASE_TOPIC]
    for key in payload.keys():
        if key.endswith('_topic'):
            payload = payload.split('/')
            payload = [x if x != BASE_TOPIC else base_topic for x in payload]
            payload[key] = '/'.join(payload)

@emontnemery emontnemery force-pushed the mqtt_discovery_shortened branch from be57bcb to 7a2d892 Compare October 2, 2018 14:40
@emontnemery emontnemery changed the title WIP - Support abbreviations in discovery topic Support abbreviations in discovery topic Oct 2, 2018
@emontnemery
Copy link
Contributor Author

@fabaff, @balloob Are any more changes required?

@balloob
Copy link
Member

balloob commented Oct 8, 2018

But if default prefix is last, then why would we have to update to support the base topic in every slot? Couldn't we get the user to just configure it properly?

@emontnemery
Copy link
Contributor Author

@balloob OK, how about supporting base topic either first (this makes most sense) or last (since it's Tasmota defualt) then?

@balloob
Copy link
Member

balloob commented Oct 10, 2018

Let's do it. I don't like it though 😢

@emontnemery
Copy link
Contributor Author

@balloob Done!

@balloob balloob merged commit b2789d9 into home-assistant:dev Oct 12, 2018
@ghost ghost removed the in progress label Oct 12, 2018
@emontnemery emontnemery deleted the mqtt_discovery_shortened branch October 12, 2018 13:13
@balloob balloob mentioned this pull request Oct 26, 2018
@home-assistant home-assistant locked and limited conversation to collaborators Feb 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants