class Buttplug::Client

Our Client for a buttplug.io server

Public Class Methods

new(serverLocation, clientName="buttplugrb") click to toggle source

Creates a new client for buttplug.io

Arguments:

  • serverLocation (string) - Where our buttplug.io server is hosted. this will tend to be: "wss://localhost:12345/buttplug"

Returns:

  • A shiney new buttplug client ready for some action

# File lib/buttplugrb.rb, line 34
def initialize(serverLocation, clientName="buttplugrb")
  @messageID=1
  @location=serverLocation
  #Ok Explanation time!
  # * @EventQueue - The events we are triggering on the server, Expected to be an array, with the first element being the message Id, and the second being the message itself!
  @eventQueue=EM::Queue.new
  @logLevel=Buttplug::LogLevel::Off
  @scanning=false
  @currentDevices=[];
  startEventMachine()
  @eventMachine.run
end

Public Instance Methods

currentDevices() click to toggle source
# File lib/buttplugrb.rb, line 122
def currentDevices()
  return @currentDevices
end
deviceSusbscribe(id,&code) click to toggle source
# File lib/buttplugrb.rb, line 125
def deviceSusbscribe(id,&code)
  #TODO: Add Code here to allow a class like Buttplug::Device to subscribe to events, annnnd realize that the device has disconnected when that does happen (like the hush has a tendeancy to do ... )
end
generateID() click to toggle source

Does exactly what it says on the tin, generates a random id for our messages

Returns:

  • a number between 2 and 4294967295

# File lib/buttplugrb.rb, line 118
def generateID()
  @messageID+=1
  return @messageID
end
isScanning?() click to toggle source
# File lib/buttplugrb.rb, line 66
def isScanning?()
  return @scanning
end
listDevices() click to toggle source

Lists all devices available to the server

Returns:

  • An array of available devices from the server

Example:

client.listDevices()
   [{"DeviceName"=>"XBox Compatible Gamepad (XInput)", "DeviceIndex"=>1, "DeviceMessages"=>{"SingleMotorVibrateCmd"=>{}, "VibrateCmd"=>{"FeatureCount"=>2}, "StopDeviceCmd"=>{}}}]

end

# File lib/buttplugrb.rb, line 79
def listDevices()
  id=generateID()
  deviceRequest=[id,"[{\"RequestDeviceList\": {\"Id\":#{id}}}]"]
  @eventQueue.push(deviceRequest)
  while(deviceRequest.length<3) do
    sleep 0.01#Just so we arn't occupying all the time on the system while we are waiting for our device list to come back.
  end
  return deviceRequest[2]["DeviceList"]["Devices"]
end
sendMessage(message) click to toggle source

Sends a message to our buttplug server

Arguments:

  • message (JSON formatted string) - The message we are sending to our server

Returns:

  • the Response from our server

# File lib/buttplugrb.rb, line 105
def sendMessage(message)
  @eventQueue.push(message)
  while(message.length<3) do
    sleep 0.01
  end
  return message[3]
end
setLogLevel(logLevel) click to toggle source
# File lib/buttplugrb.rb, line 46
def setLogLevel(logLevel)
  @logLevel=logLevel
end
startScanning() click to toggle source

Tells our server to start scanning for new devices

# File lib/buttplugrb.rb, line 52
def startScanning()
  id=generateID()
  response=sendMessage([id,"[{\"StartScanning\":{\"Id\":#{id}}}]"])
  if(response[0].keys.include? "Error")
    #TODO: Add Error Handling code
  end
end
stopAllDevices() click to toggle source

Stops all devices currently controlled by the server

# File lib/buttplugrb.rb, line 91
def stopAllDevices()
  id=generateID()
  deviceRequest=[id,"[{\"StopAllDevices\": {\"ID\":#{id}}}]"]
  @eventQueue.push(deviceRequest)
end
stopScanning() click to toggle source

Tells our server to stop scanning for new devices

# File lib/buttplugrb.rb, line 62
def stopScanning()
  id=generateID()
  @eventQueue.push([id,"[{\"StopScanning\":{\"Id\":#{id}}}]"])
end

Protected Instance Methods

startEventMachine() click to toggle source
# File lib/buttplugrb.rb, line 129
def startEventMachine()
  @eventMachine=Thread.new{EM.run{
    eventQueue=@eventQueue
    messageWatch={}
    logLevel=@logLevel
    scanning=@scanning
    currentDevices=@currentDevices
    ws = Faye::WebSocket::Client.new(@location)
    tickLoop=EM.tick_loop do #Should improve response times~
      eventQueue.pop{|msg|
        ws.send msg[1]
        messageWatch[msg[0]]=msg
        p [Time.now, :message_send, msg[1]] 
      }
    end
    ws.on :open do |event|
      p [Time.now, :open]
      ws.send "[{\"RequestServerInfo\": {\"Id\": 1, \"ClientName\": \"#{clientName}\", \"MessageVersion\": 1}}]"
      #TODO: Add MaxPingTime Code
    end
    ws.on :message do |event|
      #Ok, first of all let's grab
      message=JSON::parse(event.data).each{|event|
        message.each{|key,value|
          #We don't really care about the key just yet ... We are going to just care about finding our ID
          if(messageWatch.keys.include?(value["Id"]))
            messageWatch[value["Id"]]<<{key => value}#And now we care about our key!
            puts messageWatch[value["Id"]].object_id
            messageWatch.delete(value["Id"])
            p [Time.now, :message_recieved, [{key => value}]]
            next
          #If we are currently scanning, we should Probably check and see if we recieved a ScanningFinished message
          elsif(scanning&&key=="ScanningFinished")
            p [Time.now,:ScanningFinished]
            scanning=false
          #If we are logging, we should probably Check and see if this is a log ...
          elsif(logLevel>Buttplug::LogLevel::Off&&key=="Log")
            p [Time.now,:ServerLog,value]
          #and last but not least if we spot our server info we should probably log it ...
          elsif(key=="DeviceAdded")
            #Oh? Sweet let's go ahead and add it's information to our array!
            currentDevices.push(
              {"DeviceName" => value["DeviceName"], "DeviceIndex" => value["DeviceIndex"], "DeviceMessages" => value["DeviceMessages"]})
          elsif(key=="DeviceRemoved")
            #well darn, and to just have compatability with the current js version of buttplug.io we are gonna do this a bit diffrently than I'd like ... we are totally not doing this because I'm feeling lazy and want to push out this itteration, no sir
            currentDevices.reject!{|device|
              device["Id"]==value["Id"]
            }
          elsif(key=="ServerInfo")
            p [Time.now, :server_info, value]
          end
        }
      }
    end
    ws.on :close do |event|
      p [Time.now, :close, event.code, event.reason]
      ws = nil
      #TODO: Add Nil checks for Sends, and Nil out the ping when closed
    end
    EM.add_periodic_timer(0.5){
      ws.send "[{\"Ping\": {\"Id\": #{generateID()}}}]"
    }
    #TODO: Add Error code https://metafetish.github.io/buttplug/status.html#error
      #So, I should probably add some basic error handling to most of the code then ...
    #TODO: Add Log code https://metafetish.github.io/buttplug/status.html#requestlog
      #Done, I think ... please correct me if I'm wrong
  }}
end