Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for zestimate data from zillow.com."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 
8 import requests
9 import voluptuous as vol
10 import xmltodict
11 
13  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
14  SensorEntity,
15 )
16 from homeassistant.const import CONF_API_KEY, CONF_NAME
17 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
21 
22 _LOGGER = logging.getLogger(__name__)
23 _RESOURCE = "http://www.zillow.com/webservice/GetZestimate.htm"
24 
25 CONF_ZPID = "zpid"
26 
27 DEFAULT_NAME = "Zestimate"
28 NAME = "zestimate"
29 ZESTIMATE = f"{DEFAULT_NAME}:{NAME}"
30 
31 
32 ATTR_AMOUNT = "amount"
33 ATTR_CHANGE = "amount_change_30_days"
34 ATTR_CURRENCY = "amount_currency"
35 ATTR_LAST_UPDATED = "amount_last_updated"
36 ATTR_VAL_HI = "valuation_range_high"
37 ATTR_VAL_LOW = "valuation_range_low"
38 
39 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
40  {
41  vol.Required(CONF_API_KEY): cv.string,
42  vol.Required(CONF_ZPID): vol.All(cv.ensure_list, [cv.string]),
43  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
44  }
45 )
46 
47 
48 SCAN_INTERVAL = timedelta(minutes=30)
49 
50 
52  hass: HomeAssistant,
53  config: ConfigType,
54  add_entities: AddEntitiesCallback,
55  discovery_info: DiscoveryInfoType | None = None,
56 ) -> None:
57  """Set up the Zestimate sensor."""
58  name = config.get(CONF_NAME)
59  properties = config[CONF_ZPID]
60 
61  sensors = []
62  for zpid in properties:
63  params = {"zws-id": config[CONF_API_KEY]}
64  params["zpid"] = zpid
65  sensors.append(ZestimateDataSensor(name, params))
66  add_entities(sensors, True)
67 
68 
70  """Implementation of a Zestimate sensor."""
71 
72  _attr_attribution = "Data provided by Zillow.com"
73  _attr_icon = "mdi:home-variant"
74 
75  def __init__(self, name, params):
76  """Initialize the sensor."""
77  self._name_name = name
78  self.paramsparams = params
79  self.datadata = None
80  self.addressaddress = None
81  self._state_state = None
82 
83  @property
84  def unique_id(self):
85  """Return the ZPID."""
86  return self.paramsparams["zpid"]
87 
88  @property
89  def name(self):
90  """Return the name of the sensor."""
91  return f"{self._name} {self.address}"
92 
93  @property
94  def native_value(self):
95  """Return the state of the sensor."""
96  try:
97  return round(float(self._state_state), 1)
98  except ValueError:
99  return None
100 
101  @property
103  """Return the state attributes."""
104  attributes = {}
105  if self.datadata is not None:
106  attributes = self.datadata
107  attributes["address"] = self.addressaddress
108  return attributes
109 
110  def update(self):
111  """Get the latest data and update the states."""
112 
113  try:
114  response = requests.get(_RESOURCE, params=self.paramsparams, timeout=5)
115  data = response.content.decode("utf-8")
116  data_dict = xmltodict.parse(data).get(ZESTIMATE)
117  error_code = int(data_dict["message"]["code"])
118  if error_code != 0:
119  _LOGGER.error("The API returned: %s", data_dict["message"]["text"])
120  return
121  except requests.exceptions.ConnectionError:
122  _LOGGER.error("Unable to retrieve data from %s", _RESOURCE)
123  return
124  data = data_dict["response"][NAME]
125  details = {}
126  if "amount" in data and data["amount"] is not None:
127  details[ATTR_AMOUNT] = data["amount"]["#text"]
128  details[ATTR_CURRENCY] = data["amount"]["@currency"]
129  if "last-updated" in data and data["last-updated"] is not None:
130  details[ATTR_LAST_UPDATED] = data["last-updated"]
131  if "valueChange" in data and data["valueChange"] is not None:
132  details[ATTR_CHANGE] = int(data["valueChange"]["#text"])
133  if "valuationRange" in data and data["valuationRange"] is not None:
134  details[ATTR_VAL_HI] = int(data["valuationRange"]["high"]["#text"])
135  details[ATTR_VAL_LOW] = int(data["valuationRange"]["low"]["#text"])
136  self.addressaddress = data_dict["response"]["address"]["street"]
137  self.datadata = details
138  if self.datadata is not None:
139  self._state_state = self.datadata[ATTR_AMOUNT]
140  else:
141  self._state_state = None
142  _LOGGER.error("Unable to parase Zestimate data from response")
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:56