Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Definition of Picnic sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime
8 from typing import Any, Literal, cast
9 
11  SensorDeviceClass,
12  SensorEntity,
13  SensorEntityDescription,
14 )
15 from homeassistant.config_entries import ConfigEntry
16 from homeassistant.const import CURRENCY_EURO
17 from homeassistant.core import HomeAssistant
18 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.typing import StateType
21 from homeassistant.helpers.update_coordinator import CoordinatorEntity
22 from homeassistant.util import dt as dt_util
23 
24 from .const import (
25  ATTRIBUTION,
26  CONF_COORDINATOR,
27  DOMAIN,
28  SENSOR_CART_ITEMS_COUNT,
29  SENSOR_CART_TOTAL_PRICE,
30  SENSOR_LAST_ORDER_DELIVERY_TIME,
31  SENSOR_LAST_ORDER_MAX_ORDER_TIME,
32  SENSOR_LAST_ORDER_SLOT_END,
33  SENSOR_LAST_ORDER_SLOT_START,
34  SENSOR_LAST_ORDER_STATUS,
35  SENSOR_LAST_ORDER_TOTAL_PRICE,
36  SENSOR_NEXT_DELIVERY_ETA_END,
37  SENSOR_NEXT_DELIVERY_ETA_START,
38  SENSOR_NEXT_DELIVERY_SLOT_END,
39  SENSOR_NEXT_DELIVERY_SLOT_START,
40  SENSOR_SELECTED_SLOT_END,
41  SENSOR_SELECTED_SLOT_MAX_ORDER_TIME,
42  SENSOR_SELECTED_SLOT_MIN_ORDER_VALUE,
43  SENSOR_SELECTED_SLOT_START,
44 )
45 from .coordinator import PicnicUpdateCoordinator
46 
47 
48 @dataclass(frozen=True, kw_only=True)
50  """Describes Picnic sensor entity."""
51 
52  data_type: Literal[
53  "cart_data", "slot_data", "next_delivery_data", "last_order_data"
54  ]
55  value_fn: Callable[[Any], StateType | datetime]
56 
57  entity_registry_enabled_default: bool = False
58 
59 
60 SENSOR_TYPES: tuple[PicnicSensorEntityDescription, ...] = (
62  key=SENSOR_CART_ITEMS_COUNT,
63  translation_key=SENSOR_CART_ITEMS_COUNT,
64  data_type="cart_data",
65  value_fn=lambda cart: cart.get("total_count", 0),
66  ),
68  key=SENSOR_CART_TOTAL_PRICE,
69  translation_key=SENSOR_CART_TOTAL_PRICE,
70  native_unit_of_measurement=CURRENCY_EURO,
71  entity_registry_enabled_default=True,
72  data_type="cart_data",
73  value_fn=lambda cart: cart.get("total_price", 0) / 100,
74  ),
76  key=SENSOR_SELECTED_SLOT_START,
77  translation_key=SENSOR_SELECTED_SLOT_START,
78  device_class=SensorDeviceClass.TIMESTAMP,
79  entity_registry_enabled_default=True,
80  data_type="slot_data",
81  value_fn=lambda slot: dt_util.parse_datetime(str(slot.get("window_start"))),
82  ),
84  key=SENSOR_SELECTED_SLOT_END,
85  translation_key=SENSOR_SELECTED_SLOT_END,
86  device_class=SensorDeviceClass.TIMESTAMP,
87  entity_registry_enabled_default=True,
88  data_type="slot_data",
89  value_fn=lambda slot: dt_util.parse_datetime(str(slot.get("window_end"))),
90  ),
92  key=SENSOR_SELECTED_SLOT_MAX_ORDER_TIME,
93  translation_key=SENSOR_SELECTED_SLOT_MAX_ORDER_TIME,
94  device_class=SensorDeviceClass.TIMESTAMP,
95  entity_registry_enabled_default=True,
96  data_type="slot_data",
97  value_fn=lambda slot: dt_util.parse_datetime(str(slot.get("cut_off_time"))),
98  ),
100  key=SENSOR_SELECTED_SLOT_MIN_ORDER_VALUE,
101  translation_key=SENSOR_SELECTED_SLOT_MIN_ORDER_VALUE,
102  native_unit_of_measurement=CURRENCY_EURO,
103  entity_registry_enabled_default=True,
104  data_type="slot_data",
105  value_fn=lambda slot: (
106  slot["minimum_order_value"] / 100
107  if slot.get("minimum_order_value")
108  else None
109  ),
110  ),
112  key=SENSOR_LAST_ORDER_SLOT_START,
113  translation_key=SENSOR_LAST_ORDER_SLOT_START,
114  device_class=SensorDeviceClass.TIMESTAMP,
115  data_type="last_order_data",
116  value_fn=lambda last_order: dt_util.parse_datetime(
117  str(last_order.get("slot", {}).get("window_start"))
118  ),
119  ),
121  key=SENSOR_LAST_ORDER_SLOT_END,
122  translation_key=SENSOR_LAST_ORDER_SLOT_END,
123  device_class=SensorDeviceClass.TIMESTAMP,
124  data_type="last_order_data",
125  value_fn=lambda last_order: dt_util.parse_datetime(
126  str(last_order.get("slot", {}).get("window_end"))
127  ),
128  ),
130  key=SENSOR_LAST_ORDER_STATUS,
131  translation_key=SENSOR_LAST_ORDER_STATUS,
132  data_type="last_order_data",
133  value_fn=lambda last_order: last_order.get("status"),
134  ),
136  key=SENSOR_LAST_ORDER_MAX_ORDER_TIME,
137  translation_key=SENSOR_LAST_ORDER_MAX_ORDER_TIME,
138  device_class=SensorDeviceClass.TIMESTAMP,
139  entity_registry_enabled_default=True,
140  data_type="last_order_data",
141  value_fn=lambda last_order: dt_util.parse_datetime(
142  str(last_order.get("slot", {}).get("cut_off_time"))
143  ),
144  ),
146  key=SENSOR_LAST_ORDER_DELIVERY_TIME,
147  translation_key=SENSOR_LAST_ORDER_DELIVERY_TIME,
148  device_class=SensorDeviceClass.TIMESTAMP,
149  entity_registry_enabled_default=True,
150  data_type="last_order_data",
151  value_fn=lambda last_order: dt_util.parse_datetime(
152  str(last_order.get("delivery_time", {}).get("start"))
153  ),
154  ),
156  key=SENSOR_LAST_ORDER_TOTAL_PRICE,
157  translation_key=SENSOR_LAST_ORDER_TOTAL_PRICE,
158  native_unit_of_measurement=CURRENCY_EURO,
159  data_type="last_order_data",
160  value_fn=lambda last_order: last_order.get("total_price", 0) / 100,
161  ),
163  key=SENSOR_NEXT_DELIVERY_ETA_START,
164  translation_key=SENSOR_NEXT_DELIVERY_ETA_START,
165  device_class=SensorDeviceClass.TIMESTAMP,
166  entity_registry_enabled_default=True,
167  data_type="next_delivery_data",
168  value_fn=lambda next_delivery: dt_util.parse_datetime(
169  str(next_delivery.get("eta", {}).get("start"))
170  ),
171  ),
173  key=SENSOR_NEXT_DELIVERY_ETA_END,
174  translation_key=SENSOR_NEXT_DELIVERY_ETA_END,
175  device_class=SensorDeviceClass.TIMESTAMP,
176  entity_registry_enabled_default=True,
177  data_type="next_delivery_data",
178  value_fn=lambda next_delivery: dt_util.parse_datetime(
179  str(next_delivery.get("eta", {}).get("end"))
180  ),
181  ),
183  key=SENSOR_NEXT_DELIVERY_SLOT_START,
184  translation_key=SENSOR_NEXT_DELIVERY_SLOT_START,
185  device_class=SensorDeviceClass.TIMESTAMP,
186  data_type="next_delivery_data",
187  value_fn=lambda next_delivery: dt_util.parse_datetime(
188  str(next_delivery.get("slot", {}).get("window_start"))
189  ),
190  ),
192  key=SENSOR_NEXT_DELIVERY_SLOT_END,
193  translation_key=SENSOR_NEXT_DELIVERY_SLOT_END,
194  device_class=SensorDeviceClass.TIMESTAMP,
195  data_type="next_delivery_data",
196  value_fn=lambda next_delivery: dt_util.parse_datetime(
197  str(next_delivery.get("slot", {}).get("window_end"))
198  ),
199  ),
200 )
201 
202 
204  hass: HomeAssistant,
205  config_entry: ConfigEntry,
206  async_add_entities: AddEntitiesCallback,
207 ) -> None:
208  """Set up Picnic sensor entries."""
209  picnic_coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
210 
211  # Add an entity for each sensor type
213  PicnicSensor(picnic_coordinator, config_entry, description)
214  for description in SENSOR_TYPES
215  )
216 
217 
218 class PicnicSensor(SensorEntity, CoordinatorEntity[PicnicUpdateCoordinator]):
219  """The CoordinatorEntity subclass representing Picnic sensors."""
220 
221  _attr_has_entity_name = True
222  _attr_attribution = ATTRIBUTION
223  entity_description: PicnicSensorEntityDescription
224 
225  def __init__(
226  self,
227  coordinator: PicnicUpdateCoordinator,
228  config_entry: ConfigEntry,
229  description: PicnicSensorEntityDescription,
230  ) -> None:
231  """Init a Picnic sensor."""
232  super().__init__(coordinator)
233  self.entity_descriptionentity_description = description
234 
235  self._attr_unique_id_attr_unique_id = f"{config_entry.unique_id}.{description.key}"
236  self._attr_device_info_attr_device_info = DeviceInfo(
237  entry_type=DeviceEntryType.SERVICE,
238  identifiers={(DOMAIN, cast(str, config_entry.unique_id))},
239  manufacturer="Picnic",
240  model=config_entry.unique_id,
241  )
242 
243  @property
244  def native_value(self) -> StateType | datetime:
245  """Return the value reported by the sensor."""
246  data_set = (
247  self.coordinator.data.get(self.entity_descriptionentity_description.data_type, {})
248  if self.coordinator.data is not None
249  else {}
250  )
251  return self.entity_descriptionentity_description.value_fn(data_set)
None __init__(self, PicnicUpdateCoordinator coordinator, ConfigEntry config_entry, PicnicSensorEntityDescription description)
Definition: sensor.py:230
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:207