From 1a7522a594aa9fb1de397248e75e86c9a7cde626 Mon Sep 17 00:00:00 2001 From: "Craig J. Ward" Date: Thu, 23 Nov 2017 19:21:24 -0600 Subject: [PATCH] Add Dominos Pizza platform (#10379) * add dominos service * change require * dump to log * component fixes * clean-up use updated library * remove unnecessary import * fix hound errors * more lint fixes * Coverage rc * update requirements * cleanup as per notes * missing message * linting... * schema validation and reducing requests * fixlint * spacing * unused variable * fix docstrings * update req * notes updates, pypi package, front-end panel * stale import * fix constant name * docstrings * fix library import * lint fixes * pylint bug * remove built-in panel * Make synchronous * unused import and use throttle * Handle exceptions properly and update client * Import exceptions properly * unused import * remove bloat from start-up, readability fixes from notes, retrieve menu on request, not on startup * whitespace on blank line --- .coveragerc | 3 +- homeassistant/components/dominos.py | 240 ++++++++++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/dominos.py diff --git a/.coveragerc b/.coveragerc index 1a8d8efc4ec..af4d5b32a78 100644 --- a/.coveragerc +++ b/.coveragerc @@ -53,6 +53,8 @@ omit = homeassistant/components/digital_ocean.py homeassistant/components/*/digital_ocean.py + homeassistant/components/dominos.py + homeassistant/components/doorbird.py homeassistant/components/*/doorbird.py @@ -639,7 +641,6 @@ omit = homeassistant/components/zwave/util.py homeassistant/components/vacuum/mqtt.py - [report] # Regexes for lines to exclude from consideration exclude_lines = diff --git a/homeassistant/components/dominos.py b/homeassistant/components/dominos.py new file mode 100644 index 00000000000..867bdfafc6b --- /dev/null +++ b/homeassistant/components/dominos.py @@ -0,0 +1,240 @@ +""" +Support for Dominos Pizza ordering. + +The Dominos Pizza component ceates a service which can be invoked to order +from their menu + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/dominos/. +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components import http +from homeassistant.core import callback +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import EntityComponent +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) + +REQUIREMENTS = ['pizzapi==0.0.3'] + +DEPENDENCIES = ['http'] + +_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): vol.All(cv.ensure_list, [_ORDERS_SCHEMA]), + }), +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """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) + + 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) + + 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] + from pizzapi import Address, Customer, Store + 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) + self.closest_store = Store() + + def handle_order(self, call): + """Handle ordering pizza.""" + entity_ids = call.data.get(ATTR_ORDER_ENTITY, None) + + 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).""" + from pizzapi.address import StoreException + try: + self.closest_store = self.address.closest_store() + except StoreException: + self.closest_store = False + + def get_menu(self): + """Return the products from the closest stores menu.""" + if self.closest_store is False: + _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 False: + return 'closed' + else: + return 'orderable' if self._orderable else 'unorderable' + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Update the order state and refreshes the store.""" + from pizzapi.address import StoreException + 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.""" + from pizzapi import Order + 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.""" + from pizzapi.address import StoreException + try: + order = self.order() + order.place() + except StoreException: + self._orderable = False + _LOGGER.warning( + 'Attempted to order Dominos - Order invalid or store closed') diff --git a/requirements_all.txt b/requirements_all.txt index 7ec77dffcf7..25ddb2ec2d1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -536,6 +536,9 @@ piglow==1.2.4 # homeassistant.components.pilight pilight==0.1.1 +# homeassistant.components.dominos +pizzapi==0.0.3 + # homeassistant.components.media_player.plex # homeassistant.components.sensor.plex plexapi==3.0.3