Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Opower sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 
8 from opower import Forecast, MeterType, UnitOfMeasure
9 
11  SensorDeviceClass,
12  SensorEntity,
13  SensorEntityDescription,
14  SensorStateClass,
15 )
16 from homeassistant.config_entries import ConfigEntry
17 from homeassistant.const import EntityCategory, UnitOfEnergy, UnitOfVolume
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 from homeassistant.helpers.typing import StateType
22 from homeassistant.helpers.update_coordinator import CoordinatorEntity
23 
24 from .const import DOMAIN
25 from .coordinator import OpowerCoordinator
26 
27 
28 @dataclass(frozen=True, kw_only=True)
30  """Class describing Opower sensors entities."""
31 
32  value_fn: Callable[[Forecast], str | float]
33 
34 
35 # suggested_display_precision=0 for all sensors since
36 # Opower provides 0 decimal points for all these.
37 # (for the statistics in the energy dashboard Opower does provide decimal points)
38 ELEC_SENSORS: tuple[OpowerEntityDescription, ...] = (
40  key="elec_usage_to_date",
41  name="Current bill electric usage to date",
42  device_class=SensorDeviceClass.ENERGY,
43  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
44  # Not TOTAL_INCREASING because it can decrease for accounts with solar
45  state_class=SensorStateClass.TOTAL,
46  suggested_display_precision=0,
47  value_fn=lambda data: data.usage_to_date,
48  ),
50  key="elec_forecasted_usage",
51  name="Current bill electric forecasted usage",
52  device_class=SensorDeviceClass.ENERGY,
53  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
54  state_class=SensorStateClass.TOTAL,
55  suggested_display_precision=0,
56  value_fn=lambda data: data.forecasted_usage,
57  ),
59  key="elec_typical_usage",
60  name="Typical monthly electric usage",
61  device_class=SensorDeviceClass.ENERGY,
62  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
63  state_class=SensorStateClass.TOTAL,
64  suggested_display_precision=0,
65  value_fn=lambda data: data.typical_usage,
66  ),
68  key="elec_cost_to_date",
69  name="Current bill electric cost to date",
70  device_class=SensorDeviceClass.MONETARY,
71  native_unit_of_measurement="USD",
72  state_class=SensorStateClass.TOTAL,
73  suggested_display_precision=0,
74  value_fn=lambda data: data.cost_to_date,
75  ),
77  key="elec_forecasted_cost",
78  name="Current bill electric forecasted cost",
79  device_class=SensorDeviceClass.MONETARY,
80  native_unit_of_measurement="USD",
81  state_class=SensorStateClass.TOTAL,
82  suggested_display_precision=0,
83  value_fn=lambda data: data.forecasted_cost,
84  ),
86  key="elec_typical_cost",
87  name="Typical monthly electric cost",
88  device_class=SensorDeviceClass.MONETARY,
89  native_unit_of_measurement="USD",
90  state_class=SensorStateClass.TOTAL,
91  suggested_display_precision=0,
92  value_fn=lambda data: data.typical_cost,
93  ),
95  key="elec_start_date",
96  name="Current bill electric start date",
97  device_class=SensorDeviceClass.DATE,
98  entity_category=EntityCategory.DIAGNOSTIC,
99  entity_registry_enabled_default=False,
100  value_fn=lambda data: data.start_date,
101  ),
103  key="elec_end_date",
104  name="Current bill electric end date",
105  device_class=SensorDeviceClass.DATE,
106  entity_category=EntityCategory.DIAGNOSTIC,
107  entity_registry_enabled_default=False,
108  value_fn=lambda data: data.end_date,
109  ),
110 )
111 GAS_SENSORS: tuple[OpowerEntityDescription, ...] = (
113  key="gas_usage_to_date",
114  name="Current bill gas usage to date",
115  device_class=SensorDeviceClass.GAS,
116  native_unit_of_measurement=UnitOfVolume.CENTUM_CUBIC_FEET,
117  state_class=SensorStateClass.TOTAL,
118  suggested_display_precision=0,
119  value_fn=lambda data: data.usage_to_date,
120  ),
122  key="gas_forecasted_usage",
123  name="Current bill gas forecasted usage",
124  device_class=SensorDeviceClass.GAS,
125  native_unit_of_measurement=UnitOfVolume.CENTUM_CUBIC_FEET,
126  state_class=SensorStateClass.TOTAL,
127  suggested_display_precision=0,
128  value_fn=lambda data: data.forecasted_usage,
129  ),
131  key="gas_typical_usage",
132  name="Typical monthly gas usage",
133  device_class=SensorDeviceClass.GAS,
134  native_unit_of_measurement=UnitOfVolume.CENTUM_CUBIC_FEET,
135  state_class=SensorStateClass.TOTAL,
136  suggested_display_precision=0,
137  value_fn=lambda data: data.typical_usage,
138  ),
140  key="gas_cost_to_date",
141  name="Current bill gas cost to date",
142  device_class=SensorDeviceClass.MONETARY,
143  native_unit_of_measurement="USD",
144  state_class=SensorStateClass.TOTAL,
145  suggested_display_precision=0,
146  value_fn=lambda data: data.cost_to_date,
147  ),
149  key="gas_forecasted_cost",
150  name="Current bill gas forecasted cost",
151  device_class=SensorDeviceClass.MONETARY,
152  native_unit_of_measurement="USD",
153  state_class=SensorStateClass.TOTAL,
154  suggested_display_precision=0,
155  value_fn=lambda data: data.forecasted_cost,
156  ),
158  key="gas_typical_cost",
159  name="Typical monthly gas cost",
160  device_class=SensorDeviceClass.MONETARY,
161  native_unit_of_measurement="USD",
162  state_class=SensorStateClass.TOTAL,
163  suggested_display_precision=0,
164  value_fn=lambda data: data.typical_cost,
165  ),
167  key="gas_start_date",
168  name="Current bill gas start date",
169  device_class=SensorDeviceClass.DATE,
170  entity_category=EntityCategory.DIAGNOSTIC,
171  entity_registry_enabled_default=False,
172  value_fn=lambda data: data.start_date,
173  ),
175  key="gas_end_date",
176  name="Current bill gas end date",
177  device_class=SensorDeviceClass.DATE,
178  entity_category=EntityCategory.DIAGNOSTIC,
179  entity_registry_enabled_default=False,
180  value_fn=lambda data: data.end_date,
181  ),
182 )
183 
184 
186  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
187 ) -> None:
188  """Set up the Opower sensor."""
189 
190  coordinator: OpowerCoordinator = hass.data[DOMAIN][entry.entry_id]
191  entities: list[OpowerSensor] = []
192  forecasts = coordinator.data.values()
193  for forecast in forecasts:
194  device_id = f"{coordinator.api.utility.subdomain()}_{forecast.account.utility_account_id}"
195  device = DeviceInfo(
196  identifiers={(DOMAIN, device_id)},
197  name=f"{forecast.account.meter_type.name} account {forecast.account.utility_account_id}",
198  manufacturer="Opower",
199  model=coordinator.api.utility.name(),
200  entry_type=DeviceEntryType.SERVICE,
201  )
202  sensors: tuple[OpowerEntityDescription, ...] = ()
203  if (
204  forecast.account.meter_type == MeterType.ELEC
205  and forecast.unit_of_measure == UnitOfMeasure.KWH
206  ):
207  sensors = ELEC_SENSORS
208  elif (
209  forecast.account.meter_type == MeterType.GAS
210  and forecast.unit_of_measure in [UnitOfMeasure.THERM, UnitOfMeasure.CCF]
211  ):
212  sensors = GAS_SENSORS
213  entities.extend(
214  OpowerSensor(
215  coordinator,
216  sensor,
217  forecast.account.utility_account_id,
218  device,
219  device_id,
220  )
221  for sensor in sensors
222  )
223 
224  async_add_entities(entities)
225 
226 
227 class OpowerSensor(CoordinatorEntity[OpowerCoordinator], SensorEntity):
228  """Representation of an Opower sensor."""
229 
230  entity_description: OpowerEntityDescription
231 
232  def __init__(
233  self,
234  coordinator: OpowerCoordinator,
235  description: OpowerEntityDescription,
236  utility_account_id: str,
237  device: DeviceInfo,
238  device_id: str,
239  ) -> None:
240  """Initialize the sensor."""
241  super().__init__(coordinator)
242  self.entity_descriptionentity_description = description
243  self._attr_unique_id_attr_unique_id = f"{device_id}_{description.key}"
244  self._attr_device_info_attr_device_info = device
245  self.utility_account_idutility_account_id = utility_account_id
246 
247  @property
248  def native_value(self) -> StateType:
249  """Return the state."""
250  if self.coordinator.data is not None:
251  return self.entity_descriptionentity_description.value_fn(
252  self.coordinator.data[self.utility_account_idutility_account_id]
253  )
254  return None
None __init__(self, OpowerCoordinator coordinator, OpowerEntityDescription description, str utility_account_id, DeviceInfo device, str device_id)
Definition: sensor.py:239
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:187