virtualhere

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2025 License: MIT Imports: 11 Imported by: 0

README

virtualhere-go

A Go library for controlling VirtualHere USB clients programmatically.

Overview

virtualhere-go provides a Go interface to interact with VirtualHere USB client software, allowing you to manage USB devices shared over the network. It communicates with the VirtualHere client daemon through platform-specific IPC mechanisms:

  • Windows: Named pipe (\\.\pipe\vhclient)
  • Linux/macOS: Unix domain sockets (/tmp/vhclient and /tmp/vhclient_response)

Features

  • Control VirtualHere USB client from Go applications
  • List available USB servers and devices
  • Use and stop using remote USB devices
  • Manage server connections and auto-use settings
  • Run client as a background service/daemon
  • Parse command responses into structured data
  • Support for all VirtualHere API commands

Installation

go get github.com/Tryanks/virtualhere-go

Requirements

Usage

Starting the VirtualHere Client Daemon

Before using this library, you need to start the VirtualHere client as a daemon:

Linux:

# Start the daemon
sudo ./vhclientx86_64 -n

# Or use systemd (recommended)
sudo systemctl start virtualhereclient.service

macOS:

# Run the client in background
./vhclient &

Windows:

# Install as service (requires admin rights)
vhui64.exe -i

# Or just run the GUI client (it creates the named pipe automatically)
vhui64.exe

The simplest way to use the library is with NewPipeClient(), which connects to an already-running VirtualHere service:

package main

import (
    "fmt"
    "log"

    vh "github.com/Tryanks/virtualhere-go"
)

func main() {
    // Create a pipe client (connects to running VirtualHere service)
    // No binary path needed - uses platform-specific IPC automatically
    client, err := vh.NewPipeClient()
    if err != nil {
        log.Fatal(err)
    }

    // List all available hubs and devices
    state, err := client.List()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Auto-Find: %v\n", state.AutoFindEnabled)
    fmt.Printf("Running as Service: %v\n\n", state.RunningAsService)

    for _, hub := range state.Hubs {
        fmt.Printf("Hub: %s (%s)\n", hub.Name, hub.Address)
        for _, device := range hub.Devices {
            fmt.Printf("  - %s (%s) [In Use: %v]\n",
                device.Name, device.Address, device.InUse)
        }
    }

    // Use a device
    err = client.Use("raspberrypi.114", "")
    if err != nil {
        log.Fatal(err)
    }

    // Stop using a device
    err = client.StopUsing("raspberrypi.114")
    if err != nil {
        log.Fatal(err)
    }
}
Alternative: Using NewClient with Binary Path

If you need to manage the VirtualHere service lifecycle or need the binary path:

client, err := vh.NewClient("/path/to/vhclient")
if err != nil {
    log.Fatal(err)
}
// Use the client same as above...
Running Client as a Managed Service

You can also let the library manage the VirtualHere client process:

package main

import (
    "fmt"
    "log"

    vh "github.com/Tryanks/virtualhere-go"
)

func main() {
    // Create client with service management
    client, err := vh.NewClient("/path/to/vhclient",
        vh.WithService(true),
        vh.WithOnProcessTerminated(func() {
            fmt.Println("VirtualHere client terminated!")
        }),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close() // Gracefully shutdown the service

    // Now use the client...
    state, err := client.List()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d hubs\n", len(state.Hubs))
}
More Examples
// Add a manual hub
client.ManualHubAdd("192.168.1.100:7575")

// Enable auto-use for all devices on a hub
client.AutoUseHub("raspberrypi:7575")

// Get device information
info, err := client.DeviceInfo("raspberrypi.114")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Vendor: %s\nProduct: %s\n", info.Vendor, info.Product)

// Get detailed client state as XML
xmlState, err := client.GetClientState()
if err != nil {
    log.Fatal(err)
}

How It Works

This library communicates with the VirtualHere client daemon using platform-specific IPC:

Windows

Uses Windows Named Pipe at \\.\pipe\vhclient. The library opens the pipe, writes the command, and reads the response in message mode.

Linux/macOS

Uses two Unix domain sockets:

  • /tmp/vhclient - for sending commands (write only)
  • /tmp/vhclient_response - for receiving responses (read only)

The command must be terminated with a newline character (\n).

API Documentation

See the GoDoc for full API documentation.

License

MIT License - see LICENSE file for details.

Author

Tryanks (tryanks@outlook.com)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrCommandFailed   = errors.New("command failed")
	ErrCommandTimeout  = errors.New("command timeout (>5 seconds)")
	ErrInvalidAddress  = errors.New("invalid address")
	ErrServerNotFound  = errors.New("server not found")
	ErrDeviceNotFound  = errors.New("device not found")
	ErrDeviceInUse     = errors.New("device already in use")
	ErrBinaryNotFound  = errors.New("virtualhere binary not found")
	ErrInvalidResponse = errors.New("invalid response from client")
)

Common errors

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a VirtualHere USB client controller

func NewClient

func NewClient(binaryPath string, opts ...ClientOption) (*Client, error)

NewClient creates a new VirtualHere client controller with the specified binary path The binary should be the path to vhui64.exe (Windows), vhclientx86_64 (Linux), or vhclient (macOS) Options can be provided to configure the client behavior

func NewPipeClient added in v0.1.0

func NewPipeClient() (*Client, error)

NewPipeClient creates a new VirtualHere client that communicates via IPC (named pipe/socket) without requiring a binary path. This is the recommended method when you want to communicate with an already-running VirtualHere service.

The client will connect to:

  • Windows: Named pipe at \\.\pipe\vhclient
  • Linux/macOS: Unix sockets at /tmp/vhclient and /tmp/vhclient_response

Note: WithService option is not supported with NewPipeClient since no binary path is provided.

func (*Client) AddReverse

func (c *Client) AddReverse(serverSerial string, clientAddress string) error

AddReverse adds a reverse client to the server

func (*Client) AutoFind

func (c *Client) AutoFind() error

AutoFind toggles auto-find functionality

func (*Client) AutoUseAll

func (c *Client) AutoUseAll() error

AutoUseAll turns auto-use all devices on

func (*Client) AutoUseClearAll

func (c *Client) AutoUseClearAll() error

AutoUseClearAll clears all auto-use settings

func (*Client) AutoUseDevice

func (c *Client) AutoUseDevice(address string) error

AutoUseDevice toggles auto-use for a specific device on any port

func (*Client) AutoUseDevicePort

func (c *Client) AutoUseDevicePort(address string) error

AutoUseDevicePort toggles auto-use for a specific device on a specific port

func (*Client) AutoUseHub

func (c *Client) AutoUseHub(serverName string) error

AutoUseHub toggles auto-use for all devices on a specific hub

func (*Client) AutoUsePort

func (c *Client) AutoUsePort(address string) error

AutoUsePort toggles auto-use for any device on a specific port

func (*Client) ClearLog

func (c *Client) ClearLog() error

ClearLog clears the client log

func (*Client) Close

func (c *Client) Close() error

Close stops the background service if running

func (*Client) CustomEvent

func (c *Client) CustomEvent(address string, event string) error

CustomEvent sets a custom device event

func (*Client) DeviceInfo

func (c *Client) DeviceInfo(address string) (*DeviceInfo, error)

DeviceInfo returns information about a specific device

func (*Client) DeviceRename

func (c *Client) DeviceRename(address string, nickname string) error

DeviceRename sets a nickname for a device

func (*Client) Exit

func (c *Client) Exit() error

Exit shuts down the client

func (*Client) GetBinaryPath

func (c *Client) GetBinaryPath() string

GetBinaryPath returns the path to the VirtualHere binary

func (*Client) GetClientState

func (c *Client) GetClientState() (*XMLClientState, error)

GetClientState returns the detailed full client state as an XML document

func (*Client) Help

func (c *Client) Help() (string, error)

Help returns the help message with available commands

func (*Client) LicenseServer

func (c *Client) LicenseServer(licenseKey string) error

LicenseServer licenses a server with a license key

func (*Client) List

func (c *Client) List() (*ClientState, error)

List returns a list of all available devices and hubs

func (*Client) ListLicenses

func (c *Client) ListLicenses() ([]string, error)

ListLicenses returns a list of licenses

func (*Client) ListReverse

func (c *Client) ListReverse(serverSerial string) ([]string, error)

ListReverse lists all reverse clients for a server

func (*Client) ManualHubAdd

func (c *Client) ManualHubAdd(address string) error

ManualHubAdd adds a manually specified hub to connect to address can be in format "address:port" or "EasyFind address"

func (*Client) ManualHubList

func (c *Client) ManualHubList() ([]string, error)

ManualHubList returns a list of manually specified hubs

func (*Client) ManualHubRemove

func (c *Client) ManualHubRemove(address string) error

ManualHubRemove removes a manually specified hub

func (*Client) ManualHubRemoveAll

func (c *Client) ManualHubRemoveAll() error

ManualHubRemoveAll removes all manually specified hubs

func (*Client) RemoveReverse

func (c *Client) RemoveReverse(serverSerial string, clientAddress string) error

RemoveReverse removes a reverse client from the server

func (*Client) Reverse

func (c *Client) Reverse() error

Reverse toggles reverse lookup functionality

func (*Client) SSLReverse

func (c *Client) SSLReverse() error

SSLReverse toggles reverse SSL lookup

func (*Client) ServerInfo

func (c *Client) ServerInfo(serverName string) (*ServerInfo, error)

ServerInfo returns information about a specific server

func (*Client) ServerRename

func (c *Client) ServerRename(hubAddress string, newName string) error

ServerRename renames a server

func (*Client) StopUsing

func (c *Client) StopUsing(address string) error

StopUsing disconnects from a device

func (*Client) StopUsingAll

func (c *Client) StopUsingAll(serverAddress string) error

StopUsingAll stops using all devices on all servers or a specific server If serverAddress is empty, stops all devices on all servers serverAddress can be in format "address:port" or "EasyFind address"

func (*Client) StopUsingAllLocal

func (c *Client) StopUsingAllLocal() error

StopUsingAllLocal stops using all devices just for this client

func (*Client) Use

func (c *Client) Use(address string, password string) error

Use connects to and uses a remote device address: device address (e.g., "raspberrypi.114") password: optional password for the device (empty string if none)

type ClientOption

type ClientOption func(*Client)

ClientOption is a function that configures a Client

func WithOnProcessTerminated added in v0.0.2

func WithOnProcessTerminated(callback func()) ClientOption

WithOnProcessTerminated sets a callback function that will be called when the managed service process is terminated externally or by the user. The client will automatically cleanup resources when this occurs. This option only works when WithService(true) is enabled.

func WithService

func WithService(enable bool) ClientOption

WithService configures the client to run as a background service

type ClientState

type ClientState struct {
	Hubs              []Hub `json:"hubs"`
	AutoFindEnabled   bool  `json:"auto_find_enabled"`
	AutoUseAllEnabled bool  `json:"auto_use_all_enabled"`
	ReverseLookup     bool  `json:"reverse_lookup"`
	RunningAsService  bool  `json:"running_as_service"`
}

ClientState represents the overall state of the VirtualHere client

type CommandResult

type CommandResult struct {
	Success bool   `json:"success"`
	Output  string `json:"output"`
	Error   error  `json:"error,omitempty"`
}

CommandResult represents the result of a VirtualHere command execution

type Device

type Device struct {
	Address  string `json:"address"`  // e.g., "raspberrypi.114"
	Name     string `json:"name"`     // e.g., "Ultra USB 3.0"
	AutoUse  bool   `json:"auto_use"` // Whether auto-use is enabled for this device
	InUse    bool   `json:"in_use"`   // Whether the device is currently in use
	Nickname string `json:"nickname"` // Custom nickname if set
}

Device represents a USB device connected to a VirtualHere hub

type DeviceInfo

type DeviceInfo struct {
	Address   string `json:"address"`
	Vendor    string `json:"vendor"`
	VendorID  string `json:"vendor_id"`
	Product   string `json:"product"`
	ProductID string `json:"product_id"`
	Serial    string `json:"serial"`
	InUseBy   string `json:"in_use_by"` // "NO ONE" if not in use, otherwise client hostname
}

DeviceInfo represents detailed information about a device

type Hub

type Hub struct {
	Name    string   `json:"name"`    // e.g., "Raspberry Hub"
	Address string   `json:"address"` // e.g., "raspberrypi:7575"
	Devices []Device `json:"devices"` // Devices connected to this hub
}

Hub represents a VirtualHere USB server/hub

type License

type License struct {
	Key    string `json:"key"`
	Status string `json:"status"`
}

License represents a VirtualHere license

type ReverseClient

type ReverseClient struct {
	ServerSerial  string `json:"server_serial"`
	ClientAddress string `json:"client_address"`
}

ReverseClient represents a reverse client connection

type ServerInfo

type ServerInfo struct {
	Name         string `json:"name"`
	Version      string `json:"version"`
	State        string `json:"state"`
	Address      string `json:"address"`
	Port         string `json:"port"`
	ConnectedFor string `json:"connected_for"` // e.g., "9265 sec"
	MaxDevices   string `json:"max_devices"`
	ConnectionID string `json:"connection_id"`
	Interface    string `json:"interface"`
	SerialNumber string `json:"serial_number"`
	EasyFind     string `json:"easy_find"`
}

ServerInfo represents detailed information about a server/hub

type XMLClientState

type XMLClientState struct {
	XMLName xml.Name    `xml:"state" json:"-"`
	Servers []XMLServer `xml:"server" json:"servers"`
}

XMLClientState represents the root XML structure from GET CLIENT STATE

type XMLDevice

type XMLDevice struct {
	Vendor                            string `xml:"vendor,attr" json:"vendor"`
	Product                           string `xml:"product,attr" json:"product"`
	IDVendor                          int    `xml:"idVendor,attr" json:"id_vendor"`
	IDProduct                         int    `xml:"idProduct,attr" json:"id_product"`
	Address                           int    `xml:"address,attr" json:"address"`
	ConnectionID                      int    `xml:"connectionId,attr" json:"connection_id"`
	State                             int    `xml:"state,attr" json:"state"`
	ServerSerial                      string `xml:"serverSerial,attr" json:"server_serial"`
	ServerName                        string `xml:"serverName,attr" json:"server_name"`
	ServerInterfaceName               string `xml:"serverInterfaceName,attr" json:"server_interface_name"`
	DeviceSerial                      string `xml:"deviceSerial,attr" json:"device_serial"`
	ConnectionUUID                    string `xml:"connectionUUID,attr" json:"connection_uuid"`
	BoundConnectionUUID               string `xml:"boundConnectionUUID,attr" json:"bound_connection_uuid"`
	BoundConnectionIP                 string `xml:"boundConnectionIp,attr" json:"bound_connection_ip"`
	BoundConnectionIP6                string `xml:"boundConnectionIp6,attr" json:"bound_connection_ip6"`
	BoundClientHostname               string `xml:"boundClientHostname,attr" json:"bound_client_hostname"`
	Nickname                          string `xml:"nickname,attr" json:"nickname"`
	ClientID                          string `xml:"clientId,attr" json:"client_id"`
	NumConfigurations                 int    `xml:"numConfigurations,attr" json:"num_configurations"`
	NumInterfacesInFirstConfiguration int    `xml:"numInterfacesInFirstConfiguration,attr" json:"num_interfaces_in_first_configuration"`
	FirstInterfaceClass               int    `xml:"firstInterfaceClass,attr" json:"first_interface_class"`
	FirstInterfaceSubClass            int    `xml:"firstInterfaceSubClass,attr" json:"first_interface_sub_class"`
	FirstInterfaceProtocol            int    `xml:"firstInterfaceProtocol,attr" json:"first_interface_protocol"`
	HideClientInfo                    bool   `xml:"hideClientInfo,attr" json:"hide_client_info"`
	BadSerial                         bool   `xml:"badSerial,attr" json:"bad_serial"`
	ParentHubPort                     int    `xml:"parentHubPort,attr" json:"parent_hub_port"`
	ParentHubAddress                  int    `xml:"parentHubAddress,attr" json:"parent_hub_address"`
	ParentHubContainerID              string `xml:"parentHubContainerID,attr" json:"parent_hub_container_id"`
	ParentHubContainerIDPrefix        int    `xml:"parentHubContainerIDPrefix,attr" json:"parent_hub_container_id_prefix"`
	ContainerID                       string `xml:"containerID,attr" json:"container_id"`
	ContainerIDPrefix                 int    `xml:"containerIDPrefix,attr" json:"container_id_prefix"`
	NumPorts                          int    `xml:"numPorts,attr" json:"num_ports"`
	AutoUse                           string `xml:"autoUse,attr" json:"auto_use"` // "not-set", "on", "off", etc.
}

XMLDevice represents a device in the XML state

type XMLServer

type XMLServer struct {
	XMLName    xml.Name            `xml:"server" json:"-"`
	Connection XMLServerConnection `xml:"connection" json:"connection"`
	Devices    []XMLDevice         `xml:"device" json:"devices"`
}

XMLServer represents a server connection in the XML state

type XMLServerConnection

type XMLServerConnection struct {
	ConnectionID       int       `xml:"connectionId,attr" json:"connection_id"`
	Secure             bool      `xml:"secure,attr" json:"secure"`
	ServerMajor        int       `xml:"serverMajor,attr" json:"server_major"`
	ServerMinor        int       `xml:"serverMinor,attr" json:"server_minor"`
	ServerRevision     int       `xml:"serverRevision,attr" json:"server_revision"`
	RemoteAdmin        bool      `xml:"remoteAdmin,attr" json:"remote_admin"`
	ServerName         string    `xml:"serverName,attr" json:"server_name"`
	InterfaceName      string    `xml:"interfaceName,attr" json:"interface_name"`
	Hostname           string    `xml:"hostname,attr" json:"hostname"`
	ServerSerial       string    `xml:"serverSerial,attr" json:"server_serial"`
	LicenseMaxDevices  int       `xml:"license_max_devices,attr" json:"license_max_devices"`
	State              int       `xml:"state,attr" json:"state"`
	ConnectedTime      time.Time `xml:"connectedTime,attr" json:"connected_time"`
	Host               string    `xml:"host,attr" json:"host"`
	Port               int       `xml:"port,attr" json:"port"`
	Error              bool      `xml:"error,attr" json:"error"`
	UUID               string    `xml:"uuid,attr" json:"uuid"`
	TransportID        string    `xml:"transportId,attr" json:"transport_id"`
	EasyFindEnabled    bool      `xml:"easyFindEnabled,attr" json:"easy_find_enabled"`
	EasyFindAvailable  bool      `xml:"easyFindAvailable,attr" json:"easy_find_available"`
	EasyFindID         string    `xml:"easyFindId,attr" json:"easy_find_id"`
	EasyFindPin        string    `xml:"easyFindPin,attr" json:"easy_find_pin"`
	EasyFindAuthorized int       `xml:"easyFindAuthorized,attr" json:"easy_find_authorized"`
	IP                 string    `xml:"ip,attr" json:"ip"`
}

XMLServerConnection represents server connection details

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL