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
Fix Citybikes naming #12661
Fix Citybikes naming #12661
Conversation
The problem is that the entity defines a friendly name attribute in the device state attributes. That's not the correct way. The entity should only define the friendly name in the |
Basically the same as #12346; however the reason why this is happening is different from what you explained in the description of your PR if I understand correctly. Since the introduction of the entity registry Edit: Ah, @MartinHjelmare beat me to it :) |
Yes, but you added the better context. |
I see. No one commented on the use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're still setting attributes[ATTR_FRIENDLY_NAME]
here, I think you should remove that to remove potential confusion.
@@ -250,13 +251,19 @@ def state(self): | |||
"""Return the state of the sensor.""" | |||
return self._station_data.get(ATTR_FREE_BIKES, STATE_UNKNOWN) | |||
|
|||
@property | |||
def entity_id(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we're technically allowed to override the entity attribute, I don't think we should necessarily do that. I don't know the specifics about this platform, but maybe it could for some reason happen that two entities with the same entity_id are generated, thus breaking the second entity.
I think you should instead dynamically generate the entity_id
through generate_entity_id
. Then you'd also not need to slugify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the input! My initial idea was to use unique_id
(which seems to be the perfect field to store this kind of information). However, since the changes in the way entity IDs are applied, the code in Home Assistant prefers to use the name
property for entity ID generation, as opposed to unique_id
(i.e., if the name
property isn't None
, it will use it to generate entity_id
, even if unique_id
exists). See here: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/entity_platform.py#L197
I feel that for this platform (it shows the number of bikes available at different bike-sharing stations), an entity ID built from the bike sharing network ID/name and the network-specific station ID is more appropriate than the name of the station (which is usually an address). Maybe I'm wrong and not up to date on the philosophy behind entity IDs. However, the address is definitely more appropriate as a friendly name.
As for your concern about duplicate entity IDs, this shouldn't be a problem, as the entity_id
property is rewritten by HASS anyway, as you can see here: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/entity_platform.py#L222
I'm open to changing the platform to use the station names as entity IDs. The original idea was to generate the entity ID based on the bike sharing network name and the station ID while using the station name (usually, its address) as the friendly name. If people think that it's appropriate to use the name/address as the basis for the entity ID, I'm fine with that. I do insist on using the name/address as the friendly name that appears in the UI, as it is much more usable. |
@@ -13,7 +13,8 @@ | |||
import voluptuous as vol | |||
|
|||
import homeassistant.helpers.config_validation as cv | |||
from homeassistant.components.sensor import PLATFORM_SCHEMA | |||
from homeassistant.components.sensor import ( | |||
PLATFORM_SCHEMA, DOMAIN as PARENT_DOMAIN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of importing as PARENT_DOMAIN
, change the name of the existing DOMAIN
constant in this module. You're not allowed to overwrite the component domain in the platform, in this case the sensor domain. Eg:
CITYBIKES_DATA = 'citybikes'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed that.
@property | ||
def entity_id(self): | ||
"""Return the entity ID.""" | ||
return "{}.{}_{}".format(PARENT_DOMAIN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the property and define an instance attribute self.entity_id
in init instead. This isn't doing any I/O, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it is recommended to use generate_entity_id
as @OttoWinter says.
Only entities that defines a unique ID get registered in the entity registry. So id conflict can still occur for all other entities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason, calling generate_entity_id
gets stuck. I think it has something to do with the fact that the platform setup is async, but the call to generate_entity_id
is performed in a constructor, which is not async. From looking at the code of other platforms and components, it seems that only platforms that aren't async use generate_entity_id
, while others that need the functionality, simply create the ID on their own - which is what I've done here (after all, the logic for creating the ID is pretty straightforward, and shouldn't result in any duplicates).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you can just call async_generate_entity_id
;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that, that doesn't work either... :-/...
I'm supposed to yield from
it, am I not? This is what I tried in the constructor:
self.entity_id = yield from async_generate_entity_id(ENTITY_ID_FORMAT, uid, hass=hass)
It results in an exception:
Traceback (most recent call last):
File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/helpers/entity_platform.py", line 84, in async_setup
SLOW_SETUP_MAX_WAIT, loop=hass.loop)
File "/usr/local/lib/python3.6/asyncio/tasks.py", line 358, in wait_for
return fut.result()
File "/usr/local/lib/python3.6/asyncio/futures.py", line 245, in result
raise self._exception
File "/usr/local/lib/python3.6/asyncio/tasks.py", line 180, in _step
result = coro.send(None)
File "/srv/homeassistant/lib/python3.6/site-packages/homeassistant/components/sensor/citybikes.py", line 172, in async_setup_platform
devices.append(CityBikesStation(hass, network, station_id, name))
TypeError: __init__() should return None, not 'generator'
And when using the normal version, as I mentioned before, it just hangs inside the execution of generate_entity_id
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, after more experimentations, tried calling async_generate_entity_id
directly (without yield from
), and it worked. I'm confused - I thought I'm supposed to await
or to yield from
any coroutines...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a coroutine, it's a callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the naming is a bit confusing, in that case... Oh well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A function that is named async_*
in home assistant means that the function needs to run in the event loop. Coroutines are marked with the @asyncio.coroutine
annotation or defined with the python 3.5 keyword async def
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Thanks!
Entity ID generation now includes the base name (again), resulting in preservation of the entity ID format (the change is non-breaking)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Can be merged when build passes.
@MartinHjelmare sorry for the confusion. As I mentioned in one of the comments, the change ended up not being breaking after all, so no need to mark it as breaking in release notes. |
Great! |
Description:
The recent changes in Home Assistant core broke the naming scheme for CityBikes sensors. This PR fixes the naming.
Basically, up until now, Citybikes assigned each bike-sharing station a name based on the network and station IDs, to be used by HASS for generation of the entity ID of the station. The friendly name attribute was used to provide a nice, readable alternative to this ID.
However, following some recent changes (I believe PR #12237), the custom friendly name seems to be discarded by HASS, if the name attribute exists.
In over to overcome this, Citybikes now provides an
entity_id
property based on the previous template of network ID and station ID, while thename
property is used to provide a friendly name, when available.This change may be breaking, depending on user configuration, as the user-configurable name of the station cluster is not incorporated in the entity ID anymore.
Related issue (if applicable):
No issue was created, a direct fix is proposed here instead.
Checklist:
tox
run successfully. Your PR cannot be merged unless tests pass