require 'protocols/websocket'
class WebsocketClient
include ::Orchestrator::Constants
include ::Orchestrator::Transcoder
generic_name :Websocket
descriptive_name 'Websocket example'
tcp_port 80
wait_response false
def connected
new_websocket_client
end
def disconnected
# clear the keepalive ping
schedule.clear
end
# Send a text message
def some_request
@ws.text "hello"
# or json format etc
@ws.text({
some: "message",
count: 234
}.to_json)
end
# send a binary message
def binary_send
@ws.binary("binstring".bytes)
# or
@ws.binary hex_to_byte("0xdeadbeef")
end
protected
def new_websocket_client
# NOTE:: you must use wss:// when using port 443 (TLS connection)
@ws = Protocols::Websocket.new(self, "ws://#{remote_address}/path/to/ws/endpoint")
# @ws.add_extension # https://github.com/faye/websocket-extensions-ruby
# @ws.set_header(name, value) # Sets a custom header to be sent as part of the handshake
@ws.start
end
def received(data, resolve, command)
@ws.parse(data)
:success
end
# ====================
# Websocket callbacks:
# ====================
# websocket ready
def on_open
logger.debug { "Websocket connected" }
schedule.every('30s') do
@ws.ping('keepalive')
end
end
def on_message(raw_string)
logger.debug { "received: #{raw_string}" }
# Process request here
# request = JSON.parse(raw_string)
# ...
end
def on_ping(payload)
logger.debug { "received ping: #{payload}" }
# optional
end
def on_pong(payload)
logger.debug { "received pong: #{payload}" }
# optional
end
# connection is closing
def on_close(event)
logger.debug { "closing... #{event.code} #{event.reason}" }
end
# connection is closing
def on_error(error)
logger.debug { "ERROR! #{error.message}" }
end
# ====================
end
Telnet
Implements the telnet standard so that it is easy to communicate with devices that implement control codes or require negotiation.
require 'protocols/telnet'
class TelnetClient
def on_load
new_telnet_client
# Telnet client returns only relevant data for buffering
config before_buffering: proc { |data|
@telnet.buffer data
}
end
def disconnected
# Ensures the buffer is cleared
new_telnet_client
end
def some_request
# Telnet deals with end of line characters
# (may have been negotiated on initial connection)
send @telnet.prepare('some request')
end
protected
def new_telnet_client
# Telnet client needs access to IO stream
@telnet = Protocols::Telnet.new do |data|
send data
end
end
end
KNX
Constructs KNX standard datagrams that make it easy to communicate with devices on KNX networks.
require 'protocols/oauth'
class HttpClient
# =====================================
# Hook into HTTP request via middleware
# All requests will be sent with OAuth
# =====================================
def on_update
connected
end
# This is called directly after on_load.
# Middleware is not available until connected
def connected
@oauth = Protocols::OAuth.new({
key: setting(:consumer_key),
secret: setting(:consumer_secret),
site: remote_address
})
update_middleware
end
protected
def update_middleware
# middleware is service helper function
mid = middleware
mid.clear
mid << @oauth
end
end
require 'protocols/snmp'
class SnmpClient
include ::Orchestrator::Constants
udp_port 161
def on_unload
@client.close
end
# This is called directly after on_load.
# Middleware is not available until connected
def connected
proxy = Protocols::Snmp.new(self)
@client = NETSNMP::Client.new({
proxy: proxy, version: "2c",
community: "public"
})
end
def query_something
self[:status] = @client.get(oid: '1.3.6.1.2.1.1.1.0')
end
def set_something(val)
@client.set('1.3.6.1.2.1.1.3.0', value: val)
self[:something] = val
end
protected
def received(data, resolve, command)
# return the data which resolves the request promise.
# the proxy uses fibers to provide this to the NETSNMP client
data
end
end
SOAP Services
Probably the easiest way to use these services at the moment via a Logic module. There are a number of supported ruby gems:
# Ensure we are not blocking the IO reactor loop
require 'httpi/adapter/libuv'
require 'savon'
HTTPI.adapter = :libuv
# Make requests as per the savon documentation
client = Savon.client(wsdl: 'https://aca.im/service.wsdl')
logger.debug { "Available operations: #{client.operations}" }
Handsoap usage:
# Ensure we are not blocking the IO reactor loop
require 'handsoap/http/drivers/libuv_driver'
Handsoap.http_driver = :libuv
# Make requests as per the handsoap documentation
Wake on LAN
Wake on LAN is available to drivers of all types
# Supports any string with the correct number of hex digits
# as well as common formats (these are some examples)
mac_address_string = '0x62f81d4b6f00'
mac_address_string = '62:f8:1d:4b:6f:00'
mac_address_string = '62-f8-1d-4b-6f-00'
# Defaults to broadcast address `'255.255.255.255'`
wake_device(mac_address_string)
# You can define a VLan gateway for a directed broadcast (most common in enterprise)
wake_device(mac_address_string, '192.168.3.1')
ICMP (ping)
Uses the operating systems ping utility to perform a connectivity check.
# perform the ping
ping = ::UV::Ping.new(remote_address)
ping.ping # true / false to indicate success / failure
# check out the ping results
ping.pingable # true / false to indicate success / failure
ping.ip # IP pinged (remote_address can be a domain name)
ping.exception # any error messages
ping.warning # any warning messages