1
mirror of https://github.com/home-assistant/core synced 2024-10-04 07:58:43 +02:00
ha-core/homeassistant/components/dominos/__init__.py
epenet f14e162610
Add missing schema to dominos order service (#63016)
Co-authored-by: epenet <epenet@users.noreply.github.com>
2022-01-02 20:49:41 +01:00

260 lines
7.3 KiB
Python

"""Support for Dominos Pizza ordering."""
from datetime import timedelta
import logging
from pizzapi import Address, Customer, Order
from pizzapi.address import StoreException
import voluptuous as vol
from homeassistant.components import http
from homeassistant.core import HomeAssistant, ServiceCall, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "dominos"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
ATTR_COUNTRY = "country_code"
ATTR_FIRST_NAME = "first_name"
ATTR_LAST_NAME = "last_name"
ATTR_EMAIL = "email"
ATTR_PHONE = "phone"
ATTR_ADDRESS = "address"
ATTR_ORDERS = "orders"
ATTR_SHOW_MENU = "show_menu"
ATTR_ORDER_ENTITY = "order_entity_id"
ATTR_ORDER_NAME = "name"
ATTR_ORDER_CODES = "codes"
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
MIN_TIME_BETWEEN_STORE_UPDATES = timedelta(minutes=3330)
_ORDERS_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ORDER_NAME): cv.string,
vol.Required(ATTR_ORDER_CODES): vol.All(cv.ensure_list, [cv.string]),
}
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(ATTR_COUNTRY): cv.string,
vol.Required(ATTR_FIRST_NAME): cv.string,
vol.Required(ATTR_LAST_NAME): cv.string,
vol.Required(ATTR_EMAIL): cv.string,
vol.Required(ATTR_PHONE): cv.string,
vol.Required(ATTR_ADDRESS): cv.string,
vol.Optional(ATTR_SHOW_MENU): cv.boolean,
vol.Optional(ATTR_ORDERS, default=[]): vol.All(
cv.ensure_list, [_ORDERS_SCHEMA]
),
}
)
},
extra=vol.ALLOW_EXTRA,
)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up is called when Home Assistant is loading our component."""
dominos = Dominos(hass, config)
component = EntityComponent(_LOGGER, DOMAIN, hass)
hass.data[DOMAIN] = {}
entities = []
conf = config[DOMAIN]
hass.services.register(
DOMAIN,
"order",
dominos.handle_order,
vol.Schema(
{
vol.Required(ATTR_ORDER_ENTITY): cv.entity_ids,
}
),
)
if conf.get(ATTR_SHOW_MENU):
hass.http.register_view(DominosProductListView(dominos))
for order_info in conf.get(ATTR_ORDERS):
order = DominosOrder(order_info, dominos)
entities.append(order)
if entities:
component.add_entities(entities)
# Return boolean to indicate that initialization was successfully.
return True
class Dominos:
"""Main Dominos service."""
def __init__(self, hass, config):
"""Set up main service."""
conf = config[DOMAIN]
self.hass = hass
self.customer = Customer(
conf.get(ATTR_FIRST_NAME),
conf.get(ATTR_LAST_NAME),
conf.get(ATTR_EMAIL),
conf.get(ATTR_PHONE),
conf.get(ATTR_ADDRESS),
)
self.address = Address(
*self.customer.address.split(","), country=conf.get(ATTR_COUNTRY)
)
self.country = conf.get(ATTR_COUNTRY)
try:
self.closest_store = self.address.closest_store()
except StoreException:
self.closest_store = None
def handle_order(self, call: ServiceCall) -> None:
"""Handle ordering pizza."""
entity_ids = call.data[ATTR_ORDER_ENTITY]
target_orders = [
order
for order in self.hass.data[DOMAIN]["entities"]
if order.entity_id in entity_ids
]
for order in target_orders:
order.place()
@Throttle(MIN_TIME_BETWEEN_STORE_UPDATES)
def update_closest_store(self):
"""Update the shared closest store (if open)."""
try:
self.closest_store = self.address.closest_store()
return True
except StoreException:
self.closest_store = None
return False
def get_menu(self):
"""Return the products from the closest stores menu."""
self.update_closest_store()
if self.closest_store is None:
_LOGGER.warning("Cannot get menu. Store may be closed")
return []
menu = self.closest_store.get_menu()
product_entries = []
for product in menu.products:
item = {}
if isinstance(product.menu_data["Variants"], list):
variants = ", ".join(product.menu_data["Variants"])
else:
variants = product.menu_data["Variants"]
item["name"] = product.name
item["variants"] = variants
product_entries.append(item)
return product_entries
class DominosProductListView(http.HomeAssistantView):
"""View to retrieve product list content."""
url = "/api/dominos"
name = "api:dominos"
def __init__(self, dominos):
"""Initialize suite view."""
self.dominos = dominos
@callback
def get(self, request):
"""Retrieve if API is running."""
return self.json(self.dominos.get_menu())
class DominosOrder(Entity):
"""Represents a Dominos order entity."""
def __init__(self, order_info, dominos):
"""Set up the entity."""
self._name = order_info["name"]
self._product_codes = order_info["codes"]
self._orderable = False
self.dominos = dominos
@property
def name(self):
"""Return the orders name."""
return self._name
@property
def product_codes(self):
"""Return the orders product codes."""
return self._product_codes
@property
def orderable(self):
"""Return the true if orderable."""
return self._orderable
@property
def state(self):
"""Return the state either closed, orderable or unorderable."""
if self.dominos.closest_store is None:
return "closed"
return "orderable" if self._orderable else "unorderable"
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Update the order state and refreshes the store."""
try:
self.dominos.update_closest_store()
except StoreException:
self._orderable = False
return
try:
order = self.order()
order.pay_with()
self._orderable = True
except StoreException:
self._orderable = False
def order(self):
"""Create the order object."""
if self.dominos.closest_store is None:
raise StoreException
order = Order(
self.dominos.closest_store,
self.dominos.customer,
self.dominos.address,
self.dominos.country,
)
for code in self._product_codes:
order.add_item(code)
return order
def place(self):
"""Place the order."""
try:
order = self.order()
order.place()
except StoreException:
self._orderable = False
_LOGGER.warning(
"Attempted to order Dominos - Order invalid or store closed"
)