|
| 1 | +from enum import StrEnum |
| 2 | +from chip import ChipDeviceCtrl |
| 3 | +import chip.clusters as Clusters |
| 4 | + |
| 5 | + |
| 6 | +class Bottle(StrEnum): |
| 7 | + kBourbon = "bourbon" |
| 8 | + kGin = "gin" |
| 9 | + kCampari = "campari" |
| 10 | + kVermouth = "vermouth" |
| 11 | + kSourMix = "sour mix" |
| 12 | + kSimpleSyrup = "simple syrup", |
| 13 | + |
| 14 | +# One once == approx 12s. |
| 15 | + |
| 16 | + |
| 17 | +class Oz: |
| 18 | + def __init__(self, oz: float): |
| 19 | + self.oz = oz |
| 20 | + |
| 21 | + def time(self): |
| 22 | + return self.oz * 12 |
| 23 | + |
| 24 | + |
| 25 | +class DrinkMachine: |
| 26 | + def __init__(self, devCtrl: ChipDeviceCtrl, node_id: int): |
| 27 | + self.dev_ctrl = devCtrl |
| 28 | + self.node_id = node_id |
| 29 | + # TODO: Should this actually be modelled as an aggregator with bridged nodes so I can have a nodelabel? |
| 30 | + # Right now I'm going to leave this as something on the app side because it's a demo, but it's weird that we don't have writeable labels unless you model it strangely. Spec issue? |
| 31 | + self.bottles: dict[Bottle, int] = {Bottle.kBourbon: 1, Bottle.kGin: 2, |
| 32 | + Bottle.kCampari: 3, Bottle.kVermouth: 4, Bottle.kSourMix: 5, Bottle.kSimpleSyrup: 6} |
| 33 | + self.recipes: dict[str, dict[Bottle, Oz]] = {} |
| 34 | + self.add_recipe("negroni", {Bottle.kGin: Oz(1.5), Bottle.kCampari: Oz(1.5), Bottle.kVermouth: Oz(1.5)}) |
| 35 | + self.add_recipe("boulevardier", {Bottle.kBourbon: Oz(1.5), Bottle.kCampari: Oz(1.5), Bottle.kVermouth: Oz(1.5)}) |
| 36 | + self.add_recipe("bourbon sour", {Bottle.kBourbon: Oz(2), Bottle.kSourMix: Oz(1), Bottle.kSimpleSyrup: Oz(1.5)}) |
| 37 | + self.add_recipe("martini", {Bottle.kGin: Oz(2), Bottle.kVermouth: Oz(0.25)}) |
| 38 | + self.add_recipe("gimlet", {Bottle.kGin: Oz(2.5), Bottle.kSourMix: Oz(0.5), Bottle.kSimpleSyrup: Oz(0.5)}) |
| 39 | + self.add_recipe("old fashioned", {Bottle.kBourbon: Oz(2), Bottle.kSimpleSyrup: Oz(0.125)}) |
| 40 | + |
| 41 | + def set_bottle_names(self, bottles: dict[Bottle, int]) -> bool: |
| 42 | + ''' Bottle is a dict of bottle name to endpoint and should contain all 6 endpoints at once''' |
| 43 | + if len(bottles) != 6: |
| 44 | + return False |
| 45 | + self.bottles = bottles |
| 46 | + |
| 47 | + def get_bottle_names(self): |
| 48 | + return self.Bottle |
| 49 | + |
| 50 | + def add_recipe(self, name: str, ingredients: dict[Bottle, Oz]): |
| 51 | + # TODO: should store somewhere permanent - simplest is to write out to file. In the meanwhile, we have a few pre-populated |
| 52 | + self.recipes[name] = ingredients |
| 53 | + |
| 54 | + async def dispense(self, recipe: str): |
| 55 | + # TODO: be a bit nicer on the comparison here. Strings as keys aren't great, but I want the flexibility to add non-standard recipes |
| 56 | + if recipe not in self.recipes.keys(): |
| 57 | + print(f"Unable to find the specified recipe. Available Recipes: {self.recipes.keys()}") |
| 58 | + return |
| 59 | + required_bottles = set(self.recipes[recipe].keys()) |
| 60 | + if not required_bottles.issubset(set(self.bottles)): |
| 61 | + print('Recipe requires an ingredient that is not loaded into the drink machine') |
| 62 | + print(f'Recipe requires: {required_bottles}') |
| 63 | + print(f'Availble: {self.bottles}') |
| 64 | + return |
| 65 | + |
| 66 | + ingredients = self.recipes[recipe] |
| 67 | + for bottle, amount in ingredients.items(): |
| 68 | + ep = self.bottles[bottle] |
| 69 | + time = amount.time() |
| 70 | + await self.dev_ctrl.SendCommand(nodeid=self.node_id, endpoint=ep, |
| 71 | + payload=Clusters.ValveConfigurationAndControl.Commands.Open(openDuration=time)) |
0 commit comments