-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
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
Add support for Zillow Zestimate sensor #12597
Conversation
details[ATTR_CURRENCY] = data['amount']['@currency'] | ||
details[ATTR_LAST_UPDATED] = data['last-updated'] | ||
details[ATTR_CHANGE] = int(data['valueChange']['#text']) | ||
details[ATTR_VAL_HIGH] = int(data['valuationRange']['high']['#text']) |
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.
line too long (81 > 79 characters)
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.
Some comments. On a larger scale, I'm not sure why you split the whole thing into two classes. I think the complexity of the code is not that high, and splitting it over two classes makes things rather more complicated than easier - but that's just my taste I guess. :)
self.address = response['address']['street'] | ||
self.measurings = details | ||
except AttributeError: | ||
self.measurings = None |
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'd probably want to log an error (or at least a warning) here?
|
||
ICON = 'mdi:home-variant' | ||
|
||
ATTR_LOCATION = 'location' |
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.
ATTR_LOCATION
is already defined in const.py
.
for zpid in properties: | ||
params['zpid'] = zpid | ||
try: | ||
response = requests.get(_RESOURCE, params=params, timeout=5) |
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.
Why do you do this? This looks like you do the first update before you create the ZestimateData
object. But you don't use any of the results to construct the entity?
Please don't do this. Setting up the platform should be fast, i.e., should do no I/O if not absolutely necessary. The first update should happen only when your update
method is called for the first time.
"""Initialize the sensor.""" | ||
self.data = data | ||
self._name = name | ||
self.update() |
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.
Please don't call update here. Since you called add_devices
with True
as second parameter above, your update
method will be called right after adding the entity anyways.
|
||
attributes[ATTR_LOCATION] = self.data.address | ||
attributes[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION | ||
return attributes |
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.
Event though None
is the default return value if your method doesn't return anything, I think it's nicer to still explicitly return None
if there are no measurings. It's easier to read that way.
def update(self): | ||
"""Get the latest data and update the states.""" | ||
self.data.update() | ||
if self.data.measurings is not None: |
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.
Shouldn't the state also be set to unknown if there are no measurings?
@tinloaf, thanks for the comments. I'm still learning and they are sincerely appreciated. I built it this way because the Swiss Hydrological Data sensor seemed like a component that was already solving a similar problem. I'll work to incorporate your changes and then push once complete. |
@tinloaf Any comment on the changes I made? |
from datetime import timedelta | ||
|
||
import voluptuous as vol | ||
import requests |
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.
Please sort 🔤.
try: | ||
return round(float(self._state), 1) | ||
except ValueError: | ||
return STATE_UNKNOWN |
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.
Return None
. The base entity class will handle unknown state.
if self.data is not None: | ||
self._state = self.data[ATTR_AMOUNT] | ||
else: | ||
self._state = STATE_UNKNOWN |
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.
Return None
.
"""Return the state attributes.""" | ||
attributes = {} | ||
if self.data is not None: | ||
attributes['Amount'] = self.data[ATTR_AMOUNT] |
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.
Attributes should be lowercase snakecase.
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 self.data
is a subset of attributes, so just update attributes with self.data
and then set address and attribution.
attributes['Valuation Range Low'] = self.data[ATTR_VAL_LOW] | ||
attributes['Address'] = self.address | ||
attributes[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION | ||
if attributes is not None: |
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.
The other case will never happen.
if error_code != 0: | ||
_LOGGER.error('The API returned: %s', | ||
data_dict['message']['text']) | ||
return False |
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.
Nothing is checking this return value. Just return
.
return False | ||
except requests.exceptions.ConnectionError: | ||
_LOGGER.error('Unable to retrieve data from %s', _RESOURCE) | ||
return False |
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.
Same as above.
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Setup the Zestimate sensor.""" |
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.
"Set up the..."
Thank you @MartinHjelmare! I think I've addressed everything you requested. |
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.
One fix left to do, otherwise looks good!
""" | ||
from datetime import timedelta | ||
import logging | ||
import requests |
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.
Add a blank line between standard library and 3rd party imports.
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.
Looks great!
Thank you! |
Description:
Adds support for the Zillow Zestimate as a sensor.
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#4812
Example entry for
configuration.yaml
(if applicable):Checklist:
If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
tox
run successfully. Your PR cannot be merged unless tests passREQUIREMENTS
variable ([example][ex-requir]).requirements_all.txt
by runningscript/gen_requirements_all.py
..coveragerc
.