Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Dominos Pizza ordering."""
2 
3 from datetime import timedelta
4 import logging
5 
6 from pizzapi import Address, Customer, Order
7 import voluptuous as vol
8 
9 from homeassistant.components import http
10 from homeassistant.core import HomeAssistant, ServiceCall, callback
11 from homeassistant.exceptions import HomeAssistantError
13 from homeassistant.helpers.entity import Entity
14 from homeassistant.helpers.entity_component import EntityComponent
15 from homeassistant.helpers.typing import ConfigType
16 from homeassistant.util import Throttle
17 
18 _LOGGER = logging.getLogger(__name__)
19 
20 # The domain of your component. Should be equal to the name of your component.
21 DOMAIN = "dominos"
22 ENTITY_ID_FORMAT = DOMAIN + ".{}"
23 
24 ATTR_COUNTRY = "country_code"
25 ATTR_FIRST_NAME = "first_name"
26 ATTR_LAST_NAME = "last_name"
27 ATTR_EMAIL = "email"
28 ATTR_PHONE = "phone"
29 ATTR_ADDRESS = "address"
30 ATTR_ORDERS = "orders"
31 ATTR_SHOW_MENU = "show_menu"
32 ATTR_ORDER_ENTITY = "order_entity_id"
33 ATTR_ORDER_NAME = "name"
34 ATTR_ORDER_CODES = "codes"
35 
36 MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
37 MIN_TIME_BETWEEN_STORE_UPDATES = timedelta(minutes=3330)
38 
39 _ORDERS_SCHEMA = vol.Schema(
40  {
41  vol.Required(ATTR_ORDER_NAME): cv.string,
42  vol.Required(ATTR_ORDER_CODES): vol.All(cv.ensure_list, [cv.string]),
43  }
44 )
45 
46 CONFIG_SCHEMA = vol.Schema(
47  {
48  DOMAIN: vol.Schema(
49  {
50  vol.Required(ATTR_COUNTRY): cv.string,
51  vol.Required(ATTR_FIRST_NAME): cv.string,
52  vol.Required(ATTR_LAST_NAME): cv.string,
53  vol.Required(ATTR_EMAIL): cv.string,
54  vol.Required(ATTR_PHONE): cv.string,
55  vol.Required(ATTR_ADDRESS): cv.string,
56  vol.Optional(ATTR_SHOW_MENU): cv.boolean,
57  vol.Optional(ATTR_ORDERS, default=[]): vol.All(
58  cv.ensure_list, [_ORDERS_SCHEMA]
59  ),
60  }
61  )
62  },
63  extra=vol.ALLOW_EXTRA,
64 )
65 
66 
67 def setup(hass: HomeAssistant, config: ConfigType) -> bool:
68  """Set up is called when Home Assistant is loading our component."""
69  dominos = Dominos(hass, config)
70 
71  component = EntityComponent[DominosOrder](_LOGGER, DOMAIN, hass)
72  hass.data[DOMAIN] = {}
73  entities: list[DominosOrder] = []
74  conf = config[DOMAIN]
75 
76  hass.services.register(
77  DOMAIN,
78  "order",
79  dominos.handle_order,
80  vol.Schema(
81  {
82  vol.Required(ATTR_ORDER_ENTITY): cv.entity_ids,
83  }
84  ),
85  )
86 
87  if conf.get(ATTR_SHOW_MENU):
88  hass.http.register_view(DominosProductListView(dominos))
89 
90  for order_info in conf.get(ATTR_ORDERS):
91  order = DominosOrder(order_info, dominos)
92  entities.append(order)
93 
94  component.add_entities(entities)
95 
96  # Return boolean to indicate that initialization was successfully.
97  return True
98 
99 
100 class Dominos:
101  """Main Dominos service."""
102 
103  def __init__(self, hass, config):
104  """Set up main service."""
105  conf = config[DOMAIN]
106 
107  self.hasshass = hass
108  self.customercustomer = Customer(
109  conf.get(ATTR_FIRST_NAME),
110  conf.get(ATTR_LAST_NAME),
111  conf.get(ATTR_EMAIL),
112  conf.get(ATTR_PHONE),
113  conf.get(ATTR_ADDRESS),
114  )
115  self.addressaddress = Address(
116  *self.customercustomer.address.split(","), country=conf.get(ATTR_COUNTRY)
117  )
118  self.countrycountry = conf.get(ATTR_COUNTRY)
119  try:
120  self.closest_storeclosest_store = self.addressaddress.closest_store()
121  except Exception: # noqa: BLE001
122  self.closest_storeclosest_store = None
123 
124  def handle_order(self, call: ServiceCall) -> None:
125  """Handle ordering pizza."""
126  entity_ids = call.data[ATTR_ORDER_ENTITY]
127 
128  target_orders = [
129  order
130  for order in self.hasshass.data[DOMAIN]["entities"]
131  if order.entity_id in entity_ids
132  ]
133 
134  for order in target_orders:
135  order.place()
136 
137  @Throttle(MIN_TIME_BETWEEN_STORE_UPDATES)
139  """Update the shared closest store (if open)."""
140  try:
141  self.closest_storeclosest_store = self.addressaddress.closest_store()
142  except Exception: # noqa: BLE001
143  self.closest_storeclosest_store = None
144  return False
145  return True
146 
147  def get_menu(self):
148  """Return the products from the closest stores menu."""
149  self.update_closest_storeupdate_closest_store()
150  if self.closest_storeclosest_store is None:
151  _LOGGER.warning("Cannot get menu. Store may be closed")
152  return []
153  menu = self.closest_storeclosest_store.get_menu()
154  product_entries = []
155 
156  for product in menu.products:
157  item = {}
158  if isinstance(product.menu_data["Variants"], list):
159  variants = ", ".join(product.menu_data["Variants"])
160  else:
161  variants = product.menu_data["Variants"]
162  item["name"] = product.name
163  item["variants"] = variants
164  product_entries.append(item)
165 
166  return product_entries
167 
168 
169 class DominosProductListView(http.HomeAssistantView):
170  """View to retrieve product list content."""
171 
172  url = "/api/dominos"
173  name = "api:dominos"
174 
175  def __init__(self, dominos):
176  """Initialize suite view."""
177  self.dominosdominos = dominos
178 
179  @callback
180  def get(self, request):
181  """Retrieve if API is running."""
182  return self.json(self.dominosdominos.get_menu())
183 
184 
186  """Represents a Dominos order entity."""
187 
188  def __init__(self, order_info, dominos):
189  """Set up the entity."""
190  self._name_name = order_info["name"]
191  self._product_codes_product_codes = order_info["codes"]
192  self._orderable_orderable = False
193  self.dominosdominos = dominos
194 
195  @property
196  def name(self):
197  """Return the orders name."""
198  return self._name_name
199 
200  @property
201  def product_codes(self):
202  """Return the orders product codes."""
203  return self._product_codes_product_codes
204 
205  @property
206  def orderable(self):
207  """Return the true if orderable."""
208  return self._orderable_orderable
209 
210  @property
211  def state(self):
212  """Return the state either closed, orderable or unorderable."""
213  if self.dominosdominos.closest_store is None:
214  return "closed"
215  return "orderable" if self._orderable_orderable else "unorderable"
216 
217  @Throttle(MIN_TIME_BETWEEN_UPDATES)
218  def update(self):
219  """Update the order state and refreshes the store."""
220  try:
221  self.dominosdominos.update_closest_store()
222  except Exception: # noqa: BLE001
223  self._orderable_orderable = False
224  return
225 
226  try:
227  order = self.orderorder()
228  order.pay_with()
229  self._orderable_orderable = True
230  except Exception: # noqa: BLE001
231  self._orderable_orderable = False
232 
233  def order(self):
234  """Create the order object."""
235  if self.dominosdominos.closest_store is None:
236  raise HomeAssistantError("No store available")
237 
238  order = Order(
239  self.dominosdominos.closest_store,
240  self.dominosdominos.customer,
241  self.dominosdominos.address,
242  self.dominosdominos.country,
243  )
244 
245  for code in self._product_codes_product_codes:
246  order.add_item(code)
247 
248  return order
249 
250  def place(self):
251  """Place the order."""
252  try:
253  order = self.orderorder()
254  order.place()
255  except Exception: # noqa: BLE001
256  self._orderable_orderable = False
257  _LOGGER.warning(
258  "Attempted to order Dominos - Order invalid or store closed"
259  )
def __init__(self, order_info, dominos)
Definition: __init__.py:188
def __init__(self, hass, config)
Definition: __init__.py:103
None handle_order(self, ServiceCall call)
Definition: __init__.py:124
bool setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:67