docs: function (sender, msg) {
          if (!msg) {
              logger.log('onApiMessage - no message? got ' + JSON.stringify(arguments), { coreID: this.getHexCoreID() });
              return;
          }
          var that = this;
  
          //if we're not the owner, then the socket is busy
          var isBusy = (!this._checkOwner(null, function(err) {
              logger.error(err + ": " + msg.cmd , { coreID: that.getHexCoreID() });
          }));
          if (isBusy) {
              this.sendApiResponse(sender, { error: "This core is locked during the flashing process." });
              return;
          }
  
          //TODO: simplify this more?
          switch (msg.cmd) {
              case "Describe":
  
                  if (isBusy) {
                      if (settings.logApiMessages) {
                          logger.log('Describe - flashing', { coreID: that.coreID });
                      }
                      that.sendApiResponse(sender, {
                          cmd: "DescribeReturn",
                          name: msg.name,
                          state: { f: [], v: [] },
                          product_id: that.spark_product_id,
                          firmware_version: that.product_firmware_version
                      });
                  }
                  else {
                      when(this.ensureWeHaveIntrospectionData()).then(
                          function () {
                              that.sendApiResponse(sender, {
                                  cmd: "DescribeReturn",
                                  name: msg.name,
                                  state: that.coreFnState,
                                  product_id: that.spark_product_id,
                                  firmware_version: that.product_firmware_version
                              });
                          },
                          function (msg) {
                              that.sendApiResponse(sender, {
                                  cmd: "DescribeReturn",
                                  name: msg.name,
                                  err: "Error, no device state"
                              });
                          }
                      );
                  }
  
                  break;
              case "GetVar":
                  if (settings.logApiMessages) {
                      logger.log('GetVar', { coreID: that.coreID });
                  }
                  this.getVariable(msg.name, msg.type, function (value, buf, err) {
  
                      //don't forget to handle errors!
                      //if 'error' is set, then don't return the result.
                      //so we can correctly handle "Variable Not Found"
  
                      that.sendApiResponse(sender, {
                          cmd: "VarReturn",
                          name: msg.name,
                          error: err,
                          result: value
                      });
  
                  });
                  break;
              case "SetVar":
                  if (settings.logApiMessages) {
                      logger.log('SetVar', { coreID: that.coreID });
                  }
                  this.setVariable(msg.name, msg.value, function (resp) {
  
                      //that.sendApiResponse(sender, resp);
  
                      var response = {
                          cmd: "VarReturn",
                          name: msg.name,
                          result: resp.getPayload().toString()
                      };
                      that.sendApiResponse(sender, response);
                  });
                  break;
              case "CallFn":
                  if (settings.logApiMessages) {
                      logger.log('FunCall', { coreID: that.coreID });
                  }
                  this.callFunction(msg.name, msg.args, function (fnResult) {
                      var response = {
                          cmd: "FnReturn",
                          name: msg.name,
                          result: fnResult,
                          error: fnResult.Error
                      };
  
                      that.sendApiResponse(sender, response);
                  });
                  break;
              case "UFlash":
                  if (settings.logApiMessages) {
                      logger.log('FlashCore', { coreID: that.coreID });
                  }
  
                  this.flashCore(msg.args.data, sender);
                  break;
  
              case "FlashKnown":
                  if (settings.logApiMessages) {
                      logger.log('FlashKnown', { coreID: that.coreID, app: msg.app });
                  }
  
                  // Responsibility for sanitizing app names lies with API Service
                  // This includes only allowing apps whose binaries are deployed and thus exist
                  fs.readFile('known_firmware/' + msg.app + '_' + settings.environment + '.bin', function (err, buf) {
                      if (err) {
                          logger.log("Error flashing known firmware", { coreID: that.coreID, err: err });
                          that.sendApiResponse(sender, { cmd: "Event", name: "Update", message: "Update failed - " + JSON.stringify(err) });
                          return;
                      }
  
                      that.flashCore(buf, sender);
                  });
                  break;
  
              case "RaiseHand":
                  if (isBusy) {
                      if (settings.logApiMessages) {
                          logger.log('SignalCore - flashing', { coreID: that.coreID });
                      }
                      that.sendApiResponse(sender, { cmd: "RaiseHandReturn", result: true });
                  }
                  else {
                      if (settings.logApiMessages) {
                          logger.log('SignalCore', { coreID: that.coreID });
                      }
  
                      var showSignal = (msg.args && msg.args.signal);
                      this.raiseYourHand(showSignal, function (result) {
                          that.sendApiResponse(sender, {
                              cmd: "RaiseHandReturn",
                              result: result
                          });
                      });
                  }
  
                  break;
              case "Ping":
                  if (settings.logApiMessages) {
                      logger.log('Pinged, replying', { coreID: that.coreID });
                  }
  
                  this.sendApiResponse(sender, { cmd: "Pong", online: (this.socket != null), lastPing: this._lastCorePing });
                  break;
              default:
                  this.sendApiResponse(sender, {error: "unknown message" });
          }
      }