diff --git a/plugins/edsm.py b/plugins/edsm.py
index 7d3841f4..5eed8d1f 100644
--- a/plugins/edsm.py
+++ b/plugins/edsm.py
@@ -8,6 +8,8 @@ import sys
 import time
 import urllib2
 from calendar import timegm
+from Queue import Queue
+from threading import Thread
 
 import Tkinter as tk
 from ttkHyperlinkLabel import HyperlinkLabel
@@ -17,18 +19,19 @@ from config import appname, applongname, appversion, config
 import companion
 import coriolis
 import edshipyard
+import plug
 
 if __debug__:
     from traceback import print_exc
 
 EDSM_POLL = 0.1
-_TIMEOUT = 10
+_TIMEOUT = 20
 FAKE = ['CQC', 'Training', 'Destination']	# Fake systems that shouldn't be sent to EDSM
 
 
 this = sys.modules[__name__]	# For holding module globals
-this.syscache = set()	# Cache URLs of systems with known coordinates
 this.session = requests.Session()
+this.queue = Queue()	# Items to be sent to EDSM by worker thread
 this.lastship = None	# Description of last ship that we sent to EDSM
 this.lastlookup = False	# whether the last lookup succeeded
 
@@ -60,13 +63,24 @@ def plugin_start():
     config.delete('edsm_autoopen')
     config.delete('edsm_historical')
 
+    this.thread = Thread(target = worker, name = 'EDSM worker')
+    this.thread.daemon = True
+    this.thread.start()
+
     return 'EDSM'
 
 def plugin_app(parent):
     this.system_label = tk.Label(parent, text = _('System') + ':')	# Main window
     this.system = HyperlinkLabel(parent, compound=tk.RIGHT, popup_copy = True)
+    this.system.bind_all('<<EDSMStatus>>', update_status)
     return (this.system_label, this.system)
 
+def plugin_close():
+    # Signal thread to close and wait for it
+    this.queue.put(None)
+    this.thread.join()
+    this.thread = None
+
 def plugin_prefs(parent, cmdr, is_beta):
 
     PADX = 10
@@ -201,7 +215,7 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
                     if state['PaintJob'] is not None:
                         props.append(('paintJob', state['PaintJob']))
                     updateship(cmdr, state['ShipID'], state['ShipType'], props)
-                elif entry['event'] in ['ShipyardBuy', 'ShipyardSell']:
+                elif entry['event'] in ['ShipyardBuy', 'ShipyardSell', 'SellShipOnRebuy']:
                     sellship(cmdr, entry.get('SellShipID'))
 
             # Send cargo to EDSM on startup or change
@@ -222,8 +236,6 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
             if system and entry['event'] in ['Location', 'FSDJump']:
                 this.lastlookup = False
                 writelog(cmdr, timegm(time.strptime(entry['timestamp'], '%Y-%m-%dT%H:%M:%SZ')), system, 'StarPos' in entry and tuple(entry['StarPos']), state['ShipID'])
-                this.lastlookup = True
-                this.system.update_idletasks()
 
         except Exception as e:
             if __debug__: print_exc()
@@ -249,10 +261,7 @@ def cmdr_data(data, is_beta):
         # Send flightlog to EDSM if FSDJump failed to do so
         if not this.lastlookup:
             try:
-                this.lastlookup = False
                 this.writelog(data['commander']['name'], int(time.time()), system, None, data['ship']['id'])
-                this.lastlookup = True
-                this.system.update_idletasks()
             except Exception as e:
                 if __debug__: print_exc()
                 return unicode(e)
@@ -276,32 +285,51 @@ def cmdr_data(data, is_beta):
             if __debug__: print_exc()
 
 
-# Call an EDSM endpoint with args (which should be quoted)
-def call(cmdr, endpoint, args, check_msgnum=True):
-    try:
-        (username, apikey) = credentials(cmdr)
-        url = 'https://www.edsm.net/%s?commanderName=%s&apiKey=%s&fromSoftware=%s&fromSoftwareVersion=%s' % (
+# Worker thread
+def worker():
+    while True:
+        item = this.queue.get()
+        if not item:
+            return	# Closing
+        else:
+            (url, callback) = item
+
+        retrying = 0
+        while retrying < 3:
+            try:
+                r = this.session.get(url, timeout=_TIMEOUT)
+                r.raise_for_status()
+                reply = r.json()
+                (msgnum, msg) = reply['msgnum'], reply['msg']
+                break
+            except:
+                retrying += 1
+        else:
+            if callback:
+                callback(None)
+            else:
+                plug.show_error(_("Error: Can't connect to EDSM"))
+            return
+
+        # Message numbers: 1xx = OK, 2xx = fatal error, 3xx = error (but not generated in practice), 4xx = ignorable errors
+        if callback:
+            callback(reply)
+        elif msgnum // 100 != 1:
+            plug.show_error(_('Error: EDSM {MSG}').format(MSG=msg))
+
+
+# Queue a call an EDSM endpoint with args (which should be quoted)
+def call(cmdr, endpoint, args, callback=None):
+    (username, apikey) = credentials(cmdr)
+    this.queue.put(
+        ('https://www.edsm.net/%s?commanderName=%s&apiKey=%s&fromSoftware=%s&fromSoftwareVersion=%s' % (
             endpoint,
             urllib2.quote(username.encode('utf-8')),
             urllib2.quote(apikey),
             urllib2.quote(applongname),
             urllib2.quote(appversion),
-        ) + args
-        r = this.session.get(url, timeout=_TIMEOUT)
-        r.raise_for_status()
-        reply = r.json()
-        if not check_msgnum:
-            return reply
-        (msgnum, msg) = reply['msgnum'], reply['msg']
-    except:
-        if __debug__: print_exc()
-        raise Exception(_("Error: Can't connect to EDSM"))
-
-    # Message numbers: 1xx = OK, 2xx = fatal error, 3xx = error (but not generated in practice), 4xx = ignorable errors
-    if msgnum // 100 not in (1,4):
-        raise Exception(_('Error: EDSM {MSG}').format(MSG=msg))
-    else:
-        return reply
+        ) + args,
+         callback))
 
 
 # Send flight log and also do lookup
@@ -318,16 +346,31 @@ def writelog(cmdr, timestamp, system_name, coordinates, shipid = None):
         args += '&x=%.3f&y=%.3f&z=%.3f' % coordinates
     if shipid is not None:
         args += '&shipId=%d' % shipid
-    try:
-        reply = call(cmdr, 'api-logs-v1/set-log', args)
-        if reply.get('systemCreated'):
-            this.system['image'] = this._IMG_NEW
-        else:
-            this.system['image'] = this._IMG_KNOWN
-        this.syscache.add(system_name)
-    except:
+    call(cmdr, 'api-logs-v1/set-log', args, writelog_callback)
+
+def writelog_callback(reply):
+    this.lastlookup = reply
+    this.system.event_generate('<<EDSMStatus>>', when="tail")	# calls update_status in main thread
+
+def update_status(event=None):
+    reply = this.lastlookup
+    if not reply or not reply.get('msgnum'):
         this.system['image'] = this._IMG_ERROR
-        raise
+        plug.show_error(_("Error: Can't connect to EDSM"))
+    elif reply['msgnum'] // 100 not in (1,4):
+        this.system['image'] = this._IMG_ERROR
+        plug.show_error(_('Error: EDSM {MSG}').format(MSG=reply['msg']))
+    elif reply.get('systemCreated'):
+        this.system['image'] = this._IMG_NEW
+    else:
+        this.system['image'] = this._IMG_KNOWN
+
+
+# When we don't care about return msgnum from EDSM
+def null_callback(reply):
+    if not reply or not reply.get('msgnum'):
+        plug.show_error(_("Error: Can't connect to EDSM"))
+
 
 def setranks(cmdr, ranks):
     args = ''
@@ -365,4 +408,4 @@ def updateship(cmdr, shipid, shiptype, props=[]):
 
 def sellship(cmdr, shipid):
     if shipid is not None:
-        call(cmdr, 'api-commander-v1/sell-ship', '&shipId=%d' % shipid)
+        call(cmdr, 'api-commander-v1/sell-ship', '&shipId=%d' % shipid, null_callback)