diff --git a/VendServer/OpenDispense.py b/VendServer/OpenDispense.py
index 6ada5be678525c43905f745bb76ee10eadcda1d8..f1100ed9293764a8603489052ca9a866e9c731e1 100644
--- a/VendServer/OpenDispense.py
+++ b/VendServer/OpenDispense.py
@@ -14,8 +14,6 @@ import re
 import pwd
 import base64
 import socket
-from subprocess import Popen, PIPE
-#from .LDAPConnector import get_uid,get_uname, set_card_id
 
 DISPENSE_ENDPOINT = ("localhost", 11020)
 
@@ -39,7 +37,7 @@ class OpenDispense(DispenseInterface):
 		return self.authUserIdPin_db(userId, pin)
 		#return self.authUserIdPin_file(userId, pin)
 	
-	def _connect(self, authenticte:bool=True, set_euid:bool=False):
+	def _connect(self, authenticate:bool=True, set_euid:bool=False):
 
 		sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
 		try:
@@ -49,7 +47,7 @@ class OpenDispense(DispenseInterface):
 			return None
 		logging.debug('connected to dispsrv')
 		conn = Connection( sock.makefile('rw', encoding='utf-8') )
-		if authenticte:
+		if authenticate:
 			rsp = conn.send_command("AUTHIDENT")
 			if not "200" in rsp:
 				logging.info('Server said no to AUTHIDENT! - %r' % (rsp,))
@@ -73,7 +71,7 @@ class OpenDispense(DispenseInterface):
 			logging.info('getting pin for uid %d: user not in password file'%userId)
 			return False
 		
-		conn = self._connect(authenticte=True)
+		conn = self._connect()
 		if conn is None:
 			logging.error("getting pin for uid {}: Unable to open connection".format(userId))
 			return False
@@ -202,35 +200,83 @@ class OpenDispense(DispenseInterface):
 
 	def getBalance(self):
 		# Balance checking
-		if self.isLoggedIn():
-			acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE, encoding='utf-8').communicate()
-		else:
-			return None
-		balance = acct[acct.find("$")+1:acct.find("(")].strip()
-		return balance
+		if not self.isLoggedIn():
+			return "?"
+		
+		conn = self._connect(authenticate=False)
+		if conn is None:
+			return "?"
+		cmd = "USER_INFO {}".format(self._username)
+		rsp = conn.send_command(cmd)
+		try:
+			code,rest = rsp.split(" ", 1)
+			if code != "202":
+				raise ValueError("Code not 202")
+			_user,_name,balance,flags = rest.split(" ")
+			return "{:.2f}".format(int(balance) / 100)
+		except ValueError as e:
+			logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e))
+			return "?"
 
-	def getItemInfo(self, itemId):
+	def getItemInfo(self, itemId: str):
 		logging.debug("getItemInfo(%s)" % (itemId,))
 		itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
-		args = ('dispense', 'iteminfo', itemId)
-		info, unused = Popen(args, close_fds=True, stdout=PIPE, encoding='utf-8').communicate()
-		m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
-		if m == None:
-			return("dead", 0)
-		cents = int(m.group(1))*100 + int(m.group(2))
-		# return (name, price in cents)
-		return (m.group(3), cents)
+
+		conn = self._connect(authenticate=False)
+		if conn is None:
+			return ("dead", 0,)
+		cmd = "ITEM_INFO {}".format(itemId)
+		rsp = conn.send_command(cmd)
+		try:
+			code,rest = rsp.split(" ", 1)
+			if code != "202":
+				raise ValueError("Code not 202")
+			_item,itemid,status,price_cents,name = rest.split(" ", 4)
+			return (name, int(price_cents),)
+		except ValueError as e:
+			logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e))
+			return ("dead", 0,)
 
 	def isDisabled(self):
 		return self._disabled
 
 	def dispenseItem(self, itemId):
+		logging.debug("getItemInfo(%s)" % (itemId,))
 		if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead":
+			return "999"
+
+		conn = self._connect(set_euid=True)
+		if conn is None:
+			return "999"
+
+		cmd = "DISPENSE {}".format( OpenDispenseMapping.vendingMachineToOpenDispense(itemId) )
+		rsp = conn.send_command(cmd)
+		try:
+			code,rest = rsp.split(" ", 1)
+			return code
+		except ValueError as e:
+			logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e))
+			return 999
+	def openDoor(self):
+		if not self.isLoggedIn():
+			return False
+
+		conn = self._connect(set_euid=True)
+		if conn is None:
+			return False
+
+		cmd = "DISPENSE door:0".format()
+		rsp = conn.send_command(cmd)
+		try:
+			code,rest = rsp.split(" ", 1)
+			if code == "200":
+				return True
+			if code == "402":	# "Poor You"
+				return False
+			raise ValueError("Unknown code")
+		except ValueError as e:
+			logging.warn("OpenDispense: {!r} response malformed ({!r}) - exception {}".format(cmd, rsp, e))
 			return False
-		else:
-			print(('dispense -u "%s" %s'%(self._username, itemId)))
-			#os.system('dispense -u "%s" %s'%(self._username, itemId))
-			return True
 
 class Connection(object):
 	def __init__(self, sockf):
diff --git a/VendServer/VendServer.py b/VendServer/VendServer.py
index 46b31418523f2264be1a94f781fe81253bc150a6..4e059ece3e2f941cb5a335aa8f0bee56a57e2d29 100755
--- a/VendServer/VendServer.py
+++ b/VendServer/VendServer.py
@@ -8,7 +8,6 @@ import sys, os, string, re, pwd, signal, math, syslog
 import logging, logging.handlers
 from traceback import format_tb
 from time import time, sleep, mktime, localtime
-from subprocess import Popen, PIPE
 #from .LATClient import LATClient, LATClientException
 from .SerialClient import SerialClient, SerialClientException
 from .VendingMachine import VendingMachine, VendingException
@@ -198,7 +197,7 @@ class VendServer():
 	"""
 	Format text so it will appear centered on the screen.
 	"""
-	def center(self, str):
+	def center(self, str: str):
 		LEN = 10
 		return ' '*((LEN-len(str))//2)+str
 
@@ -412,11 +411,7 @@ class VendServer():
 		if self.vstatus.cur_selection == '55':
 			self.vstatus.mk.set_message('OPENSESAME')
 			logging.info('dispensing a door for %s'%self.vstatus.username)
-			if geteuid() == 0:
-				ret = os.system('dispense -u "%s" door'%self.vstatus.username)
-			else:
-				ret = os.system('dispense door')
-			if ret == 0:
+			if self.dispense.openDoor():
 				logging.info('door opened')
 				self.vstatus.mk.set_message(self.center('DOOR OPEN'))
 			else:
@@ -430,21 +425,21 @@ class VendServer():
 			self.vstatus.cur_selection = ''
 			return
 		elif self.vstatus.cur_selection[1] == '8':
+			# Drinks: No need to vend, just print a funny message and ask the server to vend from coke
+			logging.info('dispensing drink {} for {}'.format(self.vstatus.cur_selection, self.vstatus.username))
 			self.v.display('GOT DRINK?')
-			if ((os.system('dispense -u "%s" coke:%s'%(self.vstatus.username, self.vstatus.cur_selection[0])) >> 8) != 0):
+			if self.dispense.dispenseItem(self.vstatus.cur_selection) == "200":
 				self.v.display('SEEMS NOT')
 			else:
 				self.v.display('GOT DRINK!')
 		else:
-			# first see if it's a named slot
-			try:
-				price, shortname, name = get_snack( self.vstatus.cur_selection )
-			except:
-				price, shortname, name = get_snack( '--' )
+			logging.info('dispensing snack {} for {}'.format(self.vstatus.cur_selection, self.vstatus.username))
+			# Snacks: Show the name/price then dispense it
+			name, price = self.dispense.getItemInfo( self.vstatus.cur_selection )
 			dollarprice = "$%.2f" % ( price / 100.0 )
 			self.v.display(self.vstatus.cur_selection+' - %s'%dollarprice)
-			exitcode = os.system('dispense -u "%s" snack:%s'%(self.vstatus.username, self.vstatus.cur_selection)) >> 8
-			if (exitcode == 0):
+			status = self.dispense.dispenseItem(self.vstatus.cur_selection)
+			if status == "200":
 				# magic dispense syslog service
 				(worked, code, string) = self.v.vend(self.vstatus.cur_selection)
 				if worked:
@@ -454,14 +449,12 @@ class VendServer():
 					print("Vend Failed:", code, string)
 					syslog.syslog(syslog.LOG_WARNING | syslog.LOG_LOCAL4, "vending %s (slot %s) for %s FAILED %r %r" % (name, self.vstatus.cur_selection, self.vstatus.username, code, string))
 					self.v.display('VEND FAIL')
-			elif (exitcode == 5):	# RV_BALANCE
+			elif status == "402":	# RV_BALANCE
 				self.v.display('NO MONEY?')
-			elif (exitcode == 4):	# RV_ARGUMENTS (zero give causes arguments)
-				self.v.display('EMPTY SLOT')
-			elif (exitcode == 1):	# RV_BADITEM (Dead slot)
+			elif status == "406":	# RV_BADITEM (Dead slot)
 				self.v.display('EMPTY SLOT')
 			else:
-				syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, self.vstatus.cur_selection, self.vstatus.username, exitcode))
+				syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %s)" % (name, self.vstatus.cur_selection, self.vstatus.username, status))
 				self.v.display('UNK ERROR')
 		sleep(1)
 
@@ -675,16 +668,16 @@ class VendServer():
 		### we live in interesting times
 		now = localtime()
 
-		quarterhour = mktime([now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]])
-		halfhour = mktime([now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]])
-		threequarterhour = mktime([now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]])
-		fivetothehour = mktime([now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]])
+		quarterhour      = mktime((now[0],now[1],now[2],now[3],15,0,now[6],now[7],now[8]))
+		halfhour         = mktime((now[0],now[1],now[2],now[3],30,0,now[6],now[7],now[8]))
+		threequarterhour = mktime((now[0],now[1],now[2],now[3],45,0,now[6],now[7],now[8]))
+		fivetothehour    = mktime((now[0],now[1],now[2],now[3],55,0,now[6],now[7],now[8]))
 
 		hourfromnow = localtime(time() + 3600)
 		
 	#	onthehour = mktime([now[0],now[1],now[2],now[3],03,0,now[6],now[7],now[8]])
-		onthehour = mktime([hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
-			0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]])
+		onthehour = mktime((hourfromnow[0],hourfromnow[1],hourfromnow[2],hourfromnow[3], \
+			0,0,hourfromnow[6],hourfromnow[7],hourfromnow[8]))
 
 
 		#print "when it fashionable to wear a onion on your hip"
diff --git a/VendServer/VendingMachine.py b/VendServer/VendingMachine.py
index 86a1342db1a3bf92b4b5ec4135e96d4fe9330911..b10f7505382a9ee21067132e56ba36c17d7ef6ba 100644
--- a/VendServer/VendingMachine.py
+++ b/VendServer/VendingMachine.py
@@ -116,9 +116,10 @@ class VendingMachine:
 			logging.warning('Unhandled event! (%s %s)\n'%(code,text))
 
 	def authed_message(self, message):
-		print('self.challenge = %04x' % self.challenge)
 		if self.challenge == None:
+			print('self.challenge = None')
 			return message
+		print('self.challenge = %04x' % self.challenge)
 		crc = do_crc('%c%c'%(self.challenge >> 8, self.challenge & 0xff))
 		crc = do_crc(self.secret, crc)
 		crc = do_crc(message, crc)