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

Add support for Zillow Zestimate sensor #12597

Merged
merged 8 commits into from
Mar 3, 2018

Conversation

jcconnell
Copy link
Contributor

@jcconnell jcconnell commented Feb 22, 2018

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):

sensor:
  - platform: zestimate
    name: Zillow Zestimate
    api_key: <zillow API key>
    zpid:
      - <zpid 1>
      - <zpid 2>

Checklist:

  • The code change is tested and works locally.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass
  • New dependencies have been added to the REQUIREMENTS variable ([example][ex-requir]).
  • New dependencies are only imported inside functions that use them ([example][ex-import]).
  • New dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

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'])

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)

Copy link
Contributor

@tinloaf tinloaf left a 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
Copy link
Contributor

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'
Copy link
Contributor

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)
Copy link
Contributor

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()
Copy link
Contributor

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
Copy link
Contributor

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:
Copy link
Contributor

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?

@jcconnell
Copy link
Contributor Author

@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.

@jcconnell
Copy link
Contributor Author

@tinloaf Any comment on the changes I made?

from datetime import timedelta

import voluptuous as vol
import requests
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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]
Copy link
Member

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.

Copy link
Member

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:
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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."""
Copy link
Member

Choose a reason for hiding this comment

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

"Set up the..."

@MartinHjelmare MartinHjelmare changed the title Adds support for Zillow Zestimate sensor Add support for Zillow Zestimate sensor Mar 3, 2018
@jcconnell
Copy link
Contributor Author

Thank you @MartinHjelmare! I think I've addressed everything you requested.

Copy link
Member

@MartinHjelmare MartinHjelmare left a 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
Copy link
Member

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.

Copy link
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

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

Looks great!

@MartinHjelmare MartinHjelmare merged commit a9d242a into home-assistant:dev Mar 3, 2018
@jcconnell
Copy link
Contributor Author

Thank you!

@balloob balloob mentioned this pull request Mar 9, 2018
@home-assistant home-assistant locked and limited conversation to collaborators Jul 26, 2018
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.

None yet

5 participants