Merge branch 'master' of https://code.hackerspace.pl/q3k/bitvend into usb-interface
commit
f4f76f888e
|
@ -6,3 +6,4 @@
|
||||||
cython-tests/*
|
cython-tests/*
|
||||||
.ropeproject
|
.ropeproject
|
||||||
cygpio/*
|
cygpio/*
|
||||||
|
result
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
graft bitvend/static
|
||||||
|
graft bitvend/templates
|
||||||
|
global-exclude *.pyc
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO) # noqa
|
logging.basicConfig(level=logging.INFO) # noqa
|
|
@ -30,6 +30,7 @@ def bitvend_user_loader(username, profile=None):
|
||||||
def create_app():
|
def create_app():
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
app.config.from_object('bitvend.default_settings')
|
app.config.from_object('bitvend.default_settings')
|
||||||
|
print('Loading extra settings from {}...'.format(os.environ.get('BITVEND_SETTINGS', '')))
|
||||||
app.config.from_pyfile(os.environ.get('BITVEND_SETTINGS', ''), silent=True)
|
app.config.from_pyfile(os.environ.get('BITVEND_SETTINGS', ''), silent=True)
|
||||||
|
|
||||||
# Use proper proxy headers, this fixes invalid scheme in
|
# Use proper proxy headers, this fixes invalid scheme in
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from flask import Blueprint, render_template, redirect, request, flash, url_for
|
from flask import Blueprint, render_template, redirect, request, flash, url_for
|
||||||
from flask_login import current_user, fresh_login_required
|
from flask_login import current_user, fresh_login_required
|
||||||
|
|
||||||
|
from bitvend import dev
|
||||||
from bitvend.models import db, Transaction
|
from bitvend.models import db, Transaction
|
||||||
from bitvend.forms import ManualForm
|
from bitvend.forms import ManualForm
|
||||||
from spaceauth import cap_required
|
from spaceauth import cap_required
|
||||||
|
@ -31,3 +32,21 @@ def transactions(page):
|
||||||
return render_template('admin/transactions.html',
|
return render_template('admin/transactions.html',
|
||||||
transactions=Transaction.query.paginate(page)
|
transactions=Transaction.query.paginate(page)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/begin')
|
||||||
|
@fresh_login_required
|
||||||
|
@admin_required
|
||||||
|
def begin():
|
||||||
|
dev.begin_session(500)
|
||||||
|
flash('Operation successful.', 'success')
|
||||||
|
return redirect('/')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/cancel')
|
||||||
|
@fresh_login_required
|
||||||
|
@admin_required
|
||||||
|
def cancel():
|
||||||
|
dev.cancel_session()
|
||||||
|
flash('Operation successful.', 'success')
|
||||||
|
return redirect('/')
|
||||||
|
|
|
@ -42,4 +42,4 @@ ITEMS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
DEBT_LIMIT = 2500
|
DEBT_LIMIT = 1500
|
||||||
|
|
|
@ -45,5 +45,4 @@ class TransferForm(FlaskForm):
|
||||||
|
|
||||||
class ManualForm(FlaskForm):
|
class ManualForm(FlaskForm):
|
||||||
amount = DecimalUnityField("Amount", default=0, validators=[
|
amount = DecimalUnityField("Amount", default=0, validators=[
|
||||||
NumberRange(min=1),
|
|
||||||
])
|
])
|
||||||
|
|
|
@ -24,7 +24,7 @@ class PaymentProcessor(threading.Thread):
|
||||||
self.chain_id = chain_id
|
self.chain_id = chain_id
|
||||||
self.logger = logging.getLogger(type(self).__name__)
|
self.logger = logging.getLogger(type(self).__name__)
|
||||||
self.token = token
|
self.token = token
|
||||||
|
|
||||||
if app:
|
if app:
|
||||||
self.init_app(app)
|
self.init_app(app)
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ class PaymentProcessor(threading.Thread):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
ws = websocket.WebSocketApp(
|
ws = websocket.WebSocketApp(
|
||||||
"wss://socket.blockcypher.com/v1/%s?token=%s"\
|
"wss://socket.blockcypher.com/v1/%s?token=%s" \
|
||||||
% (self.chain_id, self.token),
|
% (self.chain_id, self.token),
|
||||||
on_message=self.on_message,
|
on_message=self.on_message,
|
||||||
on_error=self.on_error,
|
on_error=self.on_error,
|
||||||
on_close=self.on_close,
|
on_close=self.on_close,
|
||||||
on_open = self.on_open)
|
on_open=self.on_open)
|
||||||
|
|
||||||
ws.run_forever(ping_timeout=20, ping_interval=30)
|
ws.run_forever(ping_timeout=20, ping_interval=30)
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -77,10 +77,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p class="navbar-text navbar-right">
|
<p class="navbar-text navbar-right">
|
||||||
1zł = {{ format_btc(from_local_currency(100)) }}
|
1zł = {{ format_btc(from_local_currency(100, True)) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="navbar-text navbar-right">
|
<p class="navbar-text navbar-right">
|
||||||
<b>Rate:</b> {{ to_local_currency(100000000) / 100 }}zł
|
<b>Rate:</b> {{ to_local_currency(100000000, True) / 100 }}zł
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
<th>Name</th><th class="text-right">Balance</th>
|
<th>Name</th><th class="text-right">Balance</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
{% for user in hallofshame %}
|
{% for user, balance in hallofshame %}
|
||||||
<tr><td>{{ user }}</td><td class="text-right">{{ format_currency(user.balance) }}</td></tr>
|
<tr><td>{{ user }}</td><td class="text-right">{{ format_currency(balance) }}</td></tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><td colspan=2 class="placeholder">Wow! Nobody's due!</td></tr>
|
<tr><td colspan=2 class="placeholder">Wow! Nobody's due!</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -65,8 +65,8 @@
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
<th>Name</th><th class="text-right">Amount</th><th>Purchases</th>
|
<th>Name</th><th class="text-right">Amount</th><th>Purchases</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
{% for user in hallofaddicts %}
|
{% for user, purchase_amount, purchase_count in hallofaddicts %}
|
||||||
<tr><td>{{ user }}</td><td class="text-right">{{ format_currency(user.purchase_amount) }}</td><td>{{ user.purchase_count }}</td></tr>
|
<tr><td>{{ user }}</td><td class="text-right">{{ format_currency(purchase_amount) }}</td><td>{{ purchase_count }}</td></tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><td colspan=3 class="placeholder">Huh?</td></tr>
|
<tr><td colspan=3 class="placeholder">Huh?</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<span class="label label-info">{{ format_currency(item.value) }}</span>
|
<span class="label label-info">{{ format_currency(item.value) }}</span>
|
||||||
<span class="label label-primary">{{ format_btc(from_local_currency(item.value*1.03)) }}</span>
|
<span class="label label-primary">{{ format_btc(from_local_currency(item.value*1.03, True)) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3>{{ item.name }}</h3>
|
<h3>{{ item.name }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,7 +140,7 @@
|
||||||
<img src="{{ item.image }}" class="img-responsive center-block" />
|
<img src="{{ item.image }}" class="img-responsive center-block" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6 text-center">
|
<div class="col-xs-6 text-center">
|
||||||
{% with btc_uri = 'bitcoin:%s?amount=%s' % (config['INPUT_ADDRESS'], sat_to_btc(from_local_currency(item.value*1.03))) %}
|
{% with btc_uri = 'bitcoin:%s?amount=%s' % (config['INPUT_ADDRESS'], sat_to_btc(from_local_currency(item.value*1.03, True))) %}
|
||||||
<a href="{{ btc_uri }}">
|
<a href="{{ btc_uri }}">
|
||||||
<img src="{{ qrcode(btc_uri) }}" class="img-responsive center-block"/>
|
<img src="{{ qrcode(btc_uri) }}" class="img-responsive center-block"/>
|
||||||
<code><small>{{ config['INPUT_ADDRESS'] }}</small></code>
|
<code><small>{{ config['INPUT_ADDRESS'] }}</small></code>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import cachetools
|
import cachetools
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -6,14 +8,25 @@ def get_exchange_rate(currency='PLN'):
|
||||||
# Returns current exchange rate for selected currency
|
# Returns current exchange rate for selected currency
|
||||||
return requests.get('https://blockchain.info/pl/ticker').json()[currency]['last']
|
return requests.get('https://blockchain.info/pl/ticker').json()[currency]['last']
|
||||||
|
|
||||||
def to_local_currency(sat):
|
def to_local_currency(sat, safe=False):
|
||||||
# Returns satoshi in local lowest denomination currency (grosze)
|
# Returns satoshi in local lowest denomination currency (grosze)
|
||||||
rate = get_exchange_rate()
|
try:
|
||||||
|
rate = get_exchange_rate()
|
||||||
|
except:
|
||||||
|
if safe:
|
||||||
|
return 0
|
||||||
|
raise
|
||||||
return int(sat / 1000000.0 * rate)
|
return int(sat / 1000000.0 * rate)
|
||||||
|
|
||||||
def from_local_currency(val):
|
def from_local_currency(val, safe=False):
|
||||||
# Returns satoshi value from local currency
|
# Returns satoshi value from local currency
|
||||||
rate = get_exchange_rate()
|
try:
|
||||||
|
rate = get_exchange_rate()
|
||||||
|
except:
|
||||||
|
if safe:
|
||||||
|
return 0
|
||||||
|
raise
|
||||||
|
|
||||||
return int(val / rate * 1000000)
|
return int(val / rate * 1000000)
|
||||||
|
|
||||||
def sat_to_btc(amount):
|
def sat_to_btc(amount):
|
||||||
|
|
|
@ -19,12 +19,14 @@ bp = Blueprint('bitvend', __name__, template_folder='templates')
|
||||||
def index():
|
def index():
|
||||||
transactions = []
|
transactions = []
|
||||||
hallofshame = User.query \
|
hallofshame = User.query \
|
||||||
|
.with_entities(User, User.balance) \
|
||||||
.order_by(User.balance.asc()) \
|
.order_by(User.balance.asc()) \
|
||||||
.filter(User.balance < 0) \
|
.filter(User.balance < 0) \
|
||||||
.limit(5) \
|
.limit(5) \
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
hallofaddicts = User.query \
|
hallofaddicts = User.query \
|
||||||
|
.with_entities(User, User.purchase_amount, User.purchase_count) \
|
||||||
.order_by(User.purchase_amount.desc()) \
|
.order_by(User.purchase_amount.desc()) \
|
||||||
.filter(User.purchase_amount > 0) \
|
.filter(User.purchase_amount > 0) \
|
||||||
.limit(5) \
|
.limit(5) \
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
RX_PIN = 4
|
||||||
|
TX_PIN = 17
|
||||||
|
|
||||||
|
cdef extern from "pigpio.h":
|
||||||
|
int gpioInitialise()
|
||||||
|
int gpioCfgInterfaces(unsigned ifFlags)
|
||||||
|
|
||||||
|
int gpioSetMode(unsigned gpio, unsigned mode)
|
||||||
|
|
||||||
|
int gpioSerialReadOpen(unsigned user_gpio, unsigned baud, unsigned data_bits)
|
||||||
|
int gpioSerialRead(unsigned user_gpio, void *buf, size_t bufSize) nogil
|
||||||
|
int gpioSerialReadClose(unsigned user_gpio)
|
||||||
|
|
||||||
|
int gpioWaveCreate()
|
||||||
|
int gpioWaveDelete(unsigned wave_id)
|
||||||
|
int gpioWaveClear()
|
||||||
|
|
||||||
|
int gpioWaveTxSend(unsigned wave_id, unsigned wave_mode)
|
||||||
|
int gpioWaveTxBusy()
|
||||||
|
|
||||||
|
int gpioWaveAddSerial(unsigned user_gpio, unsigned baud, unsigned data_bits, unsigned stop_bits, unsigned offset, unsigned numBytes, char *str)
|
||||||
|
|
||||||
|
int gpioCfgMemAlloc(unsigned memAllocMode)
|
||||||
|
unsigned int gpioCfgGetInternals()
|
||||||
|
int gpioCfgSetInternals(unsigned int cfgVal)
|
||||||
|
|
||||||
|
cdef int INPUT "PI_INPUT"
|
||||||
|
cdef int OUTPUT "PI_OUTPUT"
|
||||||
|
cdef int PI_DISABLE_FIFO_IF
|
||||||
|
cdef int PI_DISABLE_SOCK_IF
|
||||||
|
|
||||||
|
cdef int PI_WAVE_MODE_ONE_SHOT
|
||||||
|
|
||||||
|
cdef unsigned PI_MEM_ALLOC_PAGEMAP
|
||||||
|
|
||||||
|
cdef extern from "unistd.h" nogil:
|
||||||
|
unsigned int sleep(unsigned int seconds)
|
||||||
|
unsigned int usleep(unsigned int usecs)
|
||||||
|
|
||||||
|
def test():
|
||||||
|
b = CythonRaspiBackend()
|
||||||
|
b.open()
|
||||||
|
while True:
|
||||||
|
print(repr(b.read()))
|
||||||
|
|
||||||
|
cdef class CythonRaspiBackend(object):
|
||||||
|
cdef int rx_pin
|
||||||
|
cdef int tx_pin
|
||||||
|
|
||||||
|
def __init__(self, rx_pin=RX_PIN, tx_pin=TX_PIN):
|
||||||
|
self.rx_pin = rx_pin
|
||||||
|
self.tx_pin = tx_pin
|
||||||
|
|
||||||
|
cpdef open(self):
|
||||||
|
# Enable startup debug
|
||||||
|
gpioCfgSetInternals(gpioCfgGetInternals() | 8);
|
||||||
|
|
||||||
|
# Force usage of non-mailbox DMA
|
||||||
|
gpioCfgMemAlloc(PI_MEM_ALLOC_PAGEMAP);
|
||||||
|
|
||||||
|
gpioCfgInterfaces(PI_DISABLE_FIFO_IF | PI_DISABLE_SOCK_IF);
|
||||||
|
gpioInitialise()
|
||||||
|
gpioWaveClear()
|
||||||
|
gpioSetMode(self.tx_pin, INPUT)
|
||||||
|
|
||||||
|
# gpioSerClose...
|
||||||
|
|
||||||
|
cdef int resp = gpioSerialReadOpen(self.rx_pin, 9600, 9)
|
||||||
|
if resp != 0:
|
||||||
|
raise Exception('Serial open failed: %d' % resp)
|
||||||
|
|
||||||
|
cpdef read(self):
|
||||||
|
cdef unsigned char buf[1024]
|
||||||
|
cdef int read_size
|
||||||
|
|
||||||
|
with nogil:
|
||||||
|
while 1:
|
||||||
|
read_size = gpioSerialRead(self.rx_pin, &buf, sizeof(buf))
|
||||||
|
if read_size > 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
usleep(100)
|
||||||
|
|
||||||
|
return bytes(buf[0:read_size])
|
||||||
|
|
||||||
|
cpdef write(self, data):
|
||||||
|
cdef char* c_data = data
|
||||||
|
gpioWaveAddSerial(self.tx_pin, 9600, 9, 6, 0, len(data), c_data)
|
||||||
|
wid = gpioWaveCreate()
|
||||||
|
|
||||||
|
gpioSetMode(self.tx_pin, OUTPUT)
|
||||||
|
gpioWaveTxSend(wid, PI_WAVE_MODE_ONE_SHOT)
|
||||||
|
|
||||||
|
while gpioWaveTxBusy():
|
||||||
|
usleep(100)
|
||||||
|
|
||||||
|
gpioWaveDelete(wid)
|
||||||
|
gpioSetMode(self.tx_pin, INPUT)
|
|
@ -0,0 +1,2 @@
|
||||||
|
import cygpio
|
||||||
|
cygpio.test()
|
|
@ -0,0 +1,7 @@
|
||||||
|
from distutils.core import setup
|
||||||
|
from distutils.extension import Extension
|
||||||
|
from Cython.Build import cythonize
|
||||||
|
|
||||||
|
setup(
|
||||||
|
ext_modules = cythonize([Extension("cygpio", ["cygpio.pyx"], libraries=["pigpio"])])
|
||||||
|
)
|
|
@ -0,0 +1,133 @@
|
||||||
|
with import <nixpkgs> {};
|
||||||
|
|
||||||
|
let
|
||||||
|
upstream = with pkgs.python3Packages; {
|
||||||
|
inherit buildPythonPackage;
|
||||||
|
inherit fetchPypi;
|
||||||
|
|
||||||
|
inherit blinker;
|
||||||
|
inherit cachetools;
|
||||||
|
inherit cryptography;
|
||||||
|
inherit cython;
|
||||||
|
inherit flask;
|
||||||
|
inherit flask_login;
|
||||||
|
inherit flask_sqlalchemy;
|
||||||
|
inherit flask_wtf;
|
||||||
|
inherit mock;
|
||||||
|
inherit prometheus_client;
|
||||||
|
inherit pyjwt;
|
||||||
|
inherit pytest;
|
||||||
|
inherit qrcode;
|
||||||
|
inherit raspberrypi-tools;
|
||||||
|
inherit requests;
|
||||||
|
inherit six;
|
||||||
|
};
|
||||||
|
|
||||||
|
in with upstream; let
|
||||||
|
websocket_client = buildPythonPackage rec {
|
||||||
|
version = "0.40.0";
|
||||||
|
pname = "websocket_client";
|
||||||
|
|
||||||
|
src = fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
sha256 = "1yz67wdjijrvwpx0a0f6wdfy8ajsvr9xbj5514ld452fqnh19b20";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = [
|
||||||
|
six
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
oauthlib = buildPythonPackage rec {
|
||||||
|
pname = "oauthlib";
|
||||||
|
version = "2.1.0";
|
||||||
|
src = fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
sha256 = "0qj183fipjzw6ipiv2k10896y97sxvargnkb6db5qs61c5d6cddc";
|
||||||
|
};
|
||||||
|
|
||||||
|
checkInputs = [ mock pytest ];
|
||||||
|
propagatedBuildInputs = [ cryptography blinker pyjwt ];
|
||||||
|
|
||||||
|
checkPhase = ''
|
||||||
|
py.test tests/
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
requests_oauthlib = buildPythonPackage rec {
|
||||||
|
pname = "requests-oauthlib";
|
||||||
|
version = "1.0.0";
|
||||||
|
src = fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
sha256 = "0gys581rqjdlv0whhqp5s2caxx66jzvb2hslxn8v7bypbbnbz1l8";
|
||||||
|
};
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
propagatedBuildInputs = [ oauthlib requests ];
|
||||||
|
};
|
||||||
|
|
||||||
|
flask_oauthlib = buildPythonPackage rec {
|
||||||
|
pname = "Flask-OAuthlib";
|
||||||
|
version = "0.9.5";
|
||||||
|
src = fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
sha256 = "01llysn53jfrr9n02hvjcynrb28lh4rjqn18k2hhk6an09cq7znb";
|
||||||
|
};
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
propagatedBuildInputs = [ flask flask_sqlalchemy requests_oauthlib oauthlib ];
|
||||||
|
};
|
||||||
|
|
||||||
|
spaceauth = buildPythonPackage rec {
|
||||||
|
pname = "Flask-SpaceAuth";
|
||||||
|
version = "0.2.0";
|
||||||
|
|
||||||
|
src = pkgs.fetchgit {
|
||||||
|
url = "https://code.hackerspace.pl/informatic/flask-spaceauth";
|
||||||
|
rev = "v${version}";
|
||||||
|
sha256 = "000vg41lw4pyd10bvcqrp15y673qlpkllgppfhm48w7vk02r6zi2";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = [ flask flask_login flask_oauthlib flask_wtf requests ];
|
||||||
|
};
|
||||||
|
|
||||||
|
pigpio = stdenv.mkDerivation rec {
|
||||||
|
pname = "pigpio";
|
||||||
|
version = "74-q3k";
|
||||||
|
buildFlags = [ "STRIPLIB=echo" "STRIP=echo" "CFLAGS=-g" ];
|
||||||
|
installFlags = [ "DESTDIR=$(out)" "prefix=" ];
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "q3k";
|
||||||
|
repo = "pigpio";
|
||||||
|
rev = "fa8c3ec41cb70da4d1868caec655d5f7d474573f";
|
||||||
|
sha256 = "0shd2p1w8k0iz7v5j81w8hw6hy67zxd6r4mvz2xflabiwblr5zi3";
|
||||||
|
};
|
||||||
|
|
||||||
|
dontStrip = true;
|
||||||
|
propagatedBuildInputs = [ raspberrypi-tools ];
|
||||||
|
};
|
||||||
|
|
||||||
|
cygpio = buildPythonPackage {
|
||||||
|
pname = "cygpio";
|
||||||
|
version = "1.0.0";
|
||||||
|
src = ./cygpio;
|
||||||
|
propagatedBuildInputs = [ pigpio cython ];
|
||||||
|
};
|
||||||
|
|
||||||
|
in buildPythonPackage rec {
|
||||||
|
name = "bitvend";
|
||||||
|
src = ./.;
|
||||||
|
doCheck = false;
|
||||||
|
propagatedBuildInputs = [
|
||||||
|
cygpio
|
||||||
|
flask
|
||||||
|
flask_sqlalchemy
|
||||||
|
websocket_client
|
||||||
|
cachetools
|
||||||
|
requests
|
||||||
|
prometheus_client
|
||||||
|
spaceauth
|
||||||
|
qrcode
|
||||||
|
];
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ Type=simple
|
||||||
User=bitvend
|
User=bitvend
|
||||||
Environment=BITVEND_SETTINGS=bitvend.cfg
|
Environment=BITVEND_SETTINGS=bitvend.cfg
|
||||||
WorkingDirectory=/var/bitvend
|
WorkingDirectory=/var/bitvend
|
||||||
ExecStart=/usr/bin/python3 -u /var/bitvend/bitvend.py
|
ExecStart=/usr/bin/python3 -u /var/bitvend/bitvend-run.py
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
- hosts: bitvend
|
- hosts: bitvend
|
||||||
tasks:
|
tasks:
|
||||||
|
- hostname: name={{ inventory_hostname }}
|
||||||
|
|
||||||
|
- apt: name=dphys-swapfile state=absent
|
||||||
|
- file: name=/var/swap state=absent
|
||||||
|
- mount: name=/var/log src=tmpfs fstype=tmpfs state=present opts="defaults,noatime,nosuid,mode=0755,size=50m"
|
||||||
|
|
||||||
- apt: name="{{ item }}" state=present
|
- apt: name="{{ item }}" state=present
|
||||||
with_items:
|
with_items:
|
||||||
- pigpio
|
- pigpio
|
||||||
|
|
|
@ -7,6 +7,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import Queue as queue
|
import Queue as queue
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cygpio
|
||||||
|
except ImportError:
|
||||||
|
raise
|
||||||
|
cygpio = None
|
||||||
|
|
||||||
from mdb.utils import compute_checksum, compute_chk, bcd_decode
|
from mdb.utils import compute_checksum, compute_chk, bcd_decode
|
||||||
from mdb.constants import *
|
from mdb.constants import *
|
||||||
from mdb.backend import RaspiBackend, DummyBackend, SerialBackend, pigpio
|
from mdb.backend import RaspiBackend, DummyBackend, SerialBackend, pigpio
|
||||||
|
@ -18,6 +24,10 @@ class MDBRequest(object):
|
||||||
processed = False
|
processed = False
|
||||||
|
|
||||||
def __init__(self, command):
|
def __init__(self, command):
|
||||||
|
self.reset(command)
|
||||||
|
|
||||||
|
def reset(self, command):
|
||||||
|
self.processed = False
|
||||||
self.timestamp = time.time()
|
self.timestamp = time.time()
|
||||||
self.command = command
|
self.command = command
|
||||||
self.data = bytearray()
|
self.data = bytearray()
|
||||||
|
@ -41,7 +51,7 @@ class MDBRequest(object):
|
||||||
return False
|
return False
|
||||||
@property
|
@property
|
||||||
def ack(self):
|
def ack(self):
|
||||||
return self.data[-1] == 0x00
|
return len(self.data) and self.data[-1] == 0x00
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<MDBRequest 0x%02x [%s] chk:%r>' % (
|
return '<MDBRequest 0x%02x [%s] chk:%r>' % (
|
||||||
|
@ -60,20 +70,26 @@ class MDBDevice(object):
|
||||||
def __init__(self, app=None):
|
def __init__(self, app=None):
|
||||||
self.logger = logging.getLogger(type(self).__name__)
|
self.logger = logging.getLogger(type(self).__name__)
|
||||||
self.poll_queue = queue.Queue()
|
self.poll_queue = queue.Queue()
|
||||||
if pigpio:
|
if cygpio:
|
||||||
|
self.backend = cygpio.CythonRaspiBackend()
|
||||||
|
self.logger.warning('Running with FAST CYTHON BACKEND')
|
||||||
|
elif pigpio:
|
||||||
self.backend = RaspiBackend()
|
self.backend = RaspiBackend()
|
||||||
else:
|
else:
|
||||||
self.logger.warning('Running with dummy backend device')
|
self.logger.warning('Running with dummy backend device')
|
||||||
self.backend = SerialBackend()
|
self.backend = SerialBackend()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.logger.info('Initializing...')
|
self.logger.info('Initializing... %r backend', self.backend)
|
||||||
self.backend.open()
|
self.backend.open()
|
||||||
# here should IO / connection initizliation go
|
# here should IO / connection initizliation go
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
|
||||||
|
self.current_request = MDBRequest(0)
|
||||||
|
self.current_request.processed = True
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
data = self.backend.read()
|
data = self.backend.read()
|
||||||
for b in range(0, len(data), 2):
|
for b in range(0, len(data), 2):
|
||||||
|
@ -85,7 +101,7 @@ class MDBDevice(object):
|
||||||
self.logger.info('Got response: %d',self.current_request.data[-1])
|
self.logger.info('Got response: %d',self.current_request.data[-1])
|
||||||
self.poll_msg = []
|
self.poll_msg = []
|
||||||
|
|
||||||
self.current_request = MDBRequest(data[b])
|
self.current_request.reset(data[b])
|
||||||
self.send_buffer = None
|
self.send_buffer = None
|
||||||
elif self.current_request:
|
elif self.current_request:
|
||||||
if self.current_request.processed and data[b] == 0xaa and self.send_buffer:
|
if self.current_request.processed and data[b] == 0xaa and self.send_buffer:
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf mkOption types;
|
||||||
|
|
||||||
|
cfg = config.services.bitvend;
|
||||||
|
|
||||||
|
bitvend = (import ./default.nix);
|
||||||
|
cfgFile = pkgs.writeText "bitvend.cfg"
|
||||||
|
''
|
||||||
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///${cfg.stateDir}/bitvend.db'
|
||||||
|
SPACEAUTH_CONSUMER_KEY = '${cfg.spaceauthConsumerKey}'
|
||||||
|
SPACEAUTH_CONSUMER_SECRET = '${cfg.spaceauthConsumerSecret}'
|
||||||
|
BLOCKCYPHER_TOKEN = '${cfg.blockcypherToken}'
|
||||||
|
SECRET_KEY = '${cfg.secretKey}'
|
||||||
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.services.bitvend = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Whether to enable bitvend";
|
||||||
|
};
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/db/bitvend";
|
||||||
|
description = "Location of bitvend's config/data directory";
|
||||||
|
};
|
||||||
|
spaceauthConsumerKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "spaceauth consumer key";
|
||||||
|
};
|
||||||
|
spaceauthConsumerSecret = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "spaceauth consumer secret";
|
||||||
|
};
|
||||||
|
blockcypherToken = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "blockcypher token";
|
||||||
|
};
|
||||||
|
secretKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "blockcypher token";
|
||||||
|
};
|
||||||
|
hostName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "vending.waw.hackerspace.pl";
|
||||||
|
description = "hostname";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.bitvend = {
|
||||||
|
environment = {
|
||||||
|
BITVEND_SETTINGS = cfgFile;
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
script = ''
|
||||||
|
${bitvend}/bin/bitvend-run.py
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d '${cfg.stateDir}' 0750 'root' 'root' - -"
|
||||||
|
];
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
appendHttpConfig = ''
|
||||||
|
proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=qrcode_cache:10m max_size=50m inactive=60m;
|
||||||
|
'';
|
||||||
|
virtualHosts."${cfg.hostName}" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:5000";
|
||||||
|
};
|
||||||
|
locations."/qrcode/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:5000";
|
||||||
|
extraConfig = ''
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
proxy_cache qrcode_cache;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue