⬆️ ⬇️

Web interface for Ajenti coffee maker via HTCPCP

Tired of running around for coffee and pressing the buttons on the coffee machine? Make it a web interface!











All code from the article is available on Github .

')

Training


Download Ajenti with PyPI and unpack it.

Install dependencies:

sudo pip install -Ur requirements.txt 




In addition, you will need an HTCPCP- supported coffee maker, and if you don’t have one, you can use a simple emulator that supports the addition of milk and chocolate!



HTCPCP client


Create the htcpcp folder in ajenti / plugins , and in it the file structure for the plugin:



 ajenti/plugins/htcpcp β”œβ”€β”€ __init__.py β”œβ”€β”€ client.py └── layout  └── config.xml 




The __init__.py contains information about the plugin:



 from ajenti.api import * from ajenti.plugins import * info = PluginInfo( title='HTCPCP Example', icon='coffee', dependencies=[ PluginDependency('main') ], ) def init(): import client import main 




In client.py, create a class for working with the HTCPCP protocol:



 import requests from ajenti.api import * from ajenti.plugins.configurator.api import ClassConfigEditor @plugin class HTCPCPClientConfigEditor (ClassConfigEditor): """ A plugin that handles editing of HTCPCPClient's classconfig """ title = 'HTCPCP Client' icon = 'coffee' def init(self): self.append(self.ui.inflate('htcpcp:config')) # htcpcp/layout/config.xml class CoffeeAddition (object): def __init__(self, name): self.name = name self.selected = False @plugin class HTCPCPClient (BasePlugin): classconfig_editor = HTCPCPClientConfigEditor # connect this plugin with a ConfigEditor default_classconfig = {'url': 'htcpcp://127.0.0.1:5000'} def init(self): self.additions = [] def check_connectivity(self): resp = requests.request('PROPFIND', self.get_url()) if resp.status_code == 418: raise Exception('This coffee pot is a teapot') def refresh(self): resp = requests.request('PROPFIND', self.get_url()) self.additions = [CoffeeAddition(x) for x in resp.headers['Additions-List'].split(';')] def get_url(self): return self.classconfig['url'].replace('htcpcp', 'http') def brew(self): """ Brew coffee with selected additions """ return requests.request('BREW', self.get_url(), headers={ 'Accept-Additions': ';'.join(x.name for x in self.additions if x.selected) }) def retrieve(self): return requests.request('GET', self.get_url()) 




Please note that here we use the ConfigEditor API to enable the user to reconfigure the HTCPCPClient class to use a different URL for the coffee maker.



Create an empty section in the panel ( main.py ):

 from ajenti.api import * from ajenti.plugins.main.api import SectionPlugin from client import HTCPCPClient @plugin class CoffeePlugin (SectionPlugin): """ A HTCPCP capable coffeepot control plugin """ def init(self): self.title = 'Coffeepot' self.icon = 'coffee' self.category = _('System') # IoC:   HTCPCPClient self.pot = HTCPCPClient.get() 




Add a little UI to configure the class and launch the panel:



 <bind:dict id="bind"> <!--        .classconfig  --> <formline text="Coffeepot URL"> <textbox bind="url" /> </formline> </bind:dict> 




 make run 




Now, going to the section Configure> Plugins, we see our plugin and can customize the URL.







Interface


Let's create some beautiful buttons!



htcpcp / layout / main.xml :

  <hc> <button id="brew" icon="arrow-right" text="Brew" /> <button id="retrieve" icon="coffee" text="Retrieve" /> <button id="refresh" icon="refresh" text="Refresh" /> </hc> 




htcpcp / main.py :

 from ajenti.api import * from ajenti.plugins.main.api import SectionPlugin from ajenti.ui import on from ajenti.ui.binder import Binder from client import HTCPCPClient @plugin class CoffeePlugin (SectionPlugin): """ A HTCPCP capable coffeepot control plugin """ def init(self): self.title = 'Coffeepot' self.icon = 'coffee' self.category = _('System') self.append(self.ui.inflate('htcpcp:main')) # htcpcp/layout/main.xml self.pot = HTCPCPClient.get() def on_page_load(self): try: self.pot.check_connectivity() except Exception, e: self.context.notify('error', 'Could not access the coffee pot: %s!' % str(e)) self.context.launch('configure-plugin', plugin=self.pot) #   Configure    @on('brew', 'click') def on_brew(self): resp = self.pot.brew() if resp.status_code == 200: self.context.notify('info', 'Brewing') else: self.context.notify('error', resp.text) @on('refresh', 'click') def on_refresh(self): #     self.pot.refresh() @on('retrieve', 'click') def on_retrieve(self): resp = self.pot.retrieve() if resp.status_code == 200: self.context.notify('info', resp.text) else: self.context.notify('error', resp.text) 




Now you can press the buttons and make coffee :)







Displaying data




It remains only to make a display of supported coffee additives and their selection. For this, it is most convenient to use data binding with the UI directly, using the Binder class.



Add elements to the main.xml to display a list of checkboxes for additions:

 <vc> <body> <pad id="pot-root"> <!-- id    --> <bind:collection bind="additions"> <!--    CoffeeAdditions  HTCPCPClient.additions --> <vc bind="__items"> <!--       <vc> --> <label style="bold" text="Available additions:" /> </vc> <bind:template> <!--   --> <checkbox bind:value="selected" bind:text="name" /> <!--  value   addition.selected,  text - c addition.name --> </bind:template> </bind:collection> </pad> </body> <hc> <button id="brew" icon="arrow-right" text="Brew" /> <button id="retrieve" icon="coffee" text="Retrieve" /> <button id="refresh" icon="refresh" text="Refresh" /> </hc> </vc> 




And in main.py we use Binder to fill the interface with data and then update the state of the additives (selected or not, based on the state of the checkboxes):



 from ajenti.api import * from ajenti.plugins.main.api import SectionPlugin from ajenti.ui import on from ajenti.ui.binder import Binder from client import HTCPCPClient @plugin class CoffeePlugin (SectionPlugin): """ A HTCPCP capable coffeepot control plugin """ def init(self): self.title = 'Coffeepot' self.icon = 'coffee' self.category = _('System') self.append(self.ui.inflate('htcpcp:main')) self.pot = HTCPCPClient.get() #  binder    ( HTCPCPClient self.pot    id=pot-root) self.binder = Binder(self.pot, self.find('pot-root')) def on_page_load(self): try: self.pot.check_connectivity() except Exception, e: self.context.notify('error', 'Could not access the coffee pot: %s!' % str(e)) self.context.launch('configure-plugin', plugin=self.pot) return if not self.pot.additions: #     ,   self.pot.refresh() #    UI self.binder.populate() @on('brew', 'click') def on_brew(self): #    UI self.binder.update() resp = self.pot.brew() if resp.status_code == 200: self.context.notify('info', 'Brewing') else: self.context.notify('error', resp.text) @on('refresh', 'click') def on_refresh(self): self.pot.refresh() #    UI self.binder.populate() @on('retrieve', 'click') def on_retrieve(self): resp = self.pot.retrieve() if resp.status_code == 200: self.context.notify('info', resp.text) else: self.context.notify('error', resp.text) 




Done :)





I remind you that the code from the article is available on Github , and also do not forget to read the documentation and look at the examples .

Source: https://habr.com/ru/post/215083/



All Articles