core/homeassistant/components/zestimate/sensor.py

136 lines
4.3 KiB
Python
Raw Normal View History

"""Support for zestimate data from zillow.com."""
from datetime import timedelta
import logging
import requests
import voluptuous as vol
import xmltodict
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
_RESOURCE = "http://www.zillow.com/webservice/GetZestimate.htm"
ATTRIBUTION = "Data provided by Zillow.com"
2019-07-31 19:25:30 +00:00
CONF_ZPID = "zpid"
2019-07-31 19:25:30 +00:00
DEFAULT_NAME = "Zestimate"
NAME = "zestimate"
ZESTIMATE = f"{DEFAULT_NAME}:{NAME}"
2019-07-31 19:25:30 +00:00
ICON = "mdi:home-variant"
2019-07-31 19:25:30 +00:00
ATTR_AMOUNT = "amount"
ATTR_CHANGE = "amount_change_30_days"
ATTR_CURRENCY = "amount_currency"
ATTR_LAST_UPDATED = "amount_last_updated"
ATTR_VAL_HI = "valuation_range_high"
ATTR_VAL_LOW = "valuation_range_low"
2019-07-31 19:25:30 +00:00
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_ZPID): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
SCAN_INTERVAL = timedelta(minutes=30)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Zestimate sensor."""
name = config.get(CONF_NAME)
properties = config[CONF_ZPID]
sensors = []
for zpid in properties:
2019-07-31 19:25:30 +00:00
params = {"zws-id": config[CONF_API_KEY]}
params["zpid"] = zpid
sensors.append(ZestimateDataSensor(name, params))
add_entities(sensors, True)
class ZestimateDataSensor(Entity):
"""Implementation of a Zestimate sensor."""
def __init__(self, name, params):
"""Initialize the sensor."""
self._name = name
self.params = params
self.data = None
self.address = None
self._state = None
2019-05-10 05:01:37 +00:00
@property
def unique_id(self):
"""Return the ZPID."""
2019-07-31 19:25:30 +00:00
return self.params["zpid"]
@property
def name(self):
"""Return the name of the sensor."""
return f"{self._name} {self.address}"
@property
def state(self):
"""Return the state of the sensor."""
try:
return round(float(self._state), 1)
except ValueError:
return None
@property
def device_state_attributes(self):
"""Return the state attributes."""
attributes = {}
if self.data is not None:
attributes = self.data
2019-07-31 19:25:30 +00:00
attributes["address"] = self.address
attributes[ATTR_ATTRIBUTION] = ATTRIBUTION
return attributes
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return ICON
def update(self):
"""Get the latest data and update the states."""
2019-07-31 19:25:30 +00:00
try:
response = requests.get(_RESOURCE, params=self.params, timeout=5)
2019-07-31 19:25:30 +00:00
data = response.content.decode("utf-8")
data_dict = xmltodict.parse(data).get(ZESTIMATE)
2019-07-31 19:25:30 +00:00
error_code = int(data_dict["message"]["code"])
if error_code != 0:
2019-07-31 19:25:30 +00:00
_LOGGER.error("The API returned: %s", data_dict["message"]["text"])
return
except requests.exceptions.ConnectionError:
2019-07-31 19:25:30 +00:00
_LOGGER.error("Unable to retrieve data from %s", _RESOURCE)
return
2019-07-31 19:25:30 +00:00
data = data_dict["response"][NAME]
details = {}
2019-07-31 19:25:30 +00:00
if "amount" in data and data["amount"] is not None:
details[ATTR_AMOUNT] = data["amount"]["#text"]
details[ATTR_CURRENCY] = data["amount"]["@currency"]
if "last-updated" in data and data["last-updated"] is not None:
details[ATTR_LAST_UPDATED] = data["last-updated"]
if "valueChange" in data and data["valueChange"] is not None:
details[ATTR_CHANGE] = int(data["valueChange"]["#text"])
if "valuationRange" in data and data["valuationRange"] is not None:
details[ATTR_VAL_HI] = int(data["valuationRange"]["high"]["#text"])
details[ATTR_VAL_LOW] = int(data["valuationRange"]["low"]["#text"])
self.address = data_dict["response"]["address"]["street"]
self.data = details
if self.data is not None:
self._state = self.data[ATTR_AMOUNT]
else:
self._state = None
2019-07-31 19:25:30 +00:00
_LOGGER.error("Unable to parase Zestimate data from response")