Skip to content

Instantly share code, notes, and snippets.

@cmsj cmsj/tangent.lua
Last active Dec 23, 2017

Embed
What would you like to do?
hs.console.clearConsole()
local hubMessage = {
["INITIATE_COMMS"] = 0x01,
["PARAMETER_CHANGE"] = 0x02,
["PARAMETER_RESET"] = 0x03,
["PARAMETER_VALUE_REQUEST"] = 0x04,
["MENU_CHANGE"] = 0x05,
["MENU_RESET"] = 0x06,
["MENU_STRING_REQUEST"] = 0x07,
["ACTION_ON"] = 0x08,
["MODE_CHANGE"] = 0x09,
["TRANSPORT"] = 0x0A,
["ACTION_OFF"] = 0x0B,
["UNMANAGED_PANEL_CAPABILITIES"] = 0x30,
["UNMANAGED_BUTTON_DOWN"] = 0x31,
["UNMANAGED_BUTTON_UP"] = 0x32,
["UNMANAGED_ENCODER_CHANGE"] = 0x33,
["UNMANAGED_DISPLAY_REFRESH"] = 0x34,
["PANEL_CONNECTION_STATE"] = 0x35,
}
local appMessage = {
["APPLICATION_DEFINITION"] = 0x81,
["PARAMETER_VALUE"] = 0x82,
["MENU_STRING"] = 0x83,
["ALL_CHANGE"] = 0x84,
["MODE_VALUE"] = 0x85,
["DISPLAY_TEXT"] = 0x86,
["UNMANAGED_PANEL_CAPABILITIES_REQUEST"] = 0xA0,
["UNMANAGED_DISPLAY_WRITE"] = 0xA1,
["RENAME_CONTROL"] = 0xA2,
["HIGHLIGHT_CONTROL"] = 0xA3,
["INDICATE_CONTROL"] = 0xA4,
["REQUEST_PANEL_CONNECTION_STATES"] = 0xA5,
}
local panelType = {
["CP200-BK"] = 0x03,
["CP200-K"] = 0x04,
["CP200-TS"] = 0x05,
["CP200-S"] = 0x09,
["Wave"] = 0x0A,
["Element-Tk"] = 0x0C,
["Element-Mf"] = 0x0D,
["Element-Kb"] = 0x0E,
["Element-Bt"] = 0x0F,
["Ripple"] = 0x11,
}
local logger = require("hs.logger")
logger.defaultLogLevel = 'debug'
local log = logger.new("tangent")
local socket = require("hs.socket")
local utf8 = require("hs.utf8")
messageBuffer = {}
messageLength = 0
messageCount = 0
readBytesRemaining = 0
function getPanelType(id)
for i,v in pairs(panelType) do
if id == v then
return i
end
end
end
function byteStringToNumber(str, offset, num_bytes)
assert(num_bytes >= 1 and num_bytes <= 4)
local x = 0
for i = 1, num_bytes do
x = x * 0x0100
x = x + math.fmod(string.byte(str, i + offset - 1) or 0, 0x0100)
end
return x
end
function numberToByteString(n)
local t = {}
local char = string.char
t[1] = char(n >> 24 & 0xFF)
t[2] = char(n >> 16 & 0xFF)
t[3] = char(n >> 08 & 0xFF)
t[4] = char(n >> 00 & 0xFF)
return table.concat(t)
end
function buildMessage(msgType, msgParts)
local byteString = numberToByteString(msgType)
for _,partValue in pairs(msgParts) do
byteString = byteString .. numberToByteString(#partValue)
byteString = byteString .. partValue
end
log.df("buildMessage constructed: "..utf8.hexDump(byteString))
return byteString
end
function processHubCommand(data)
local id = byteStringToNumber(data, 1, 4)
if id == hubMessage["INITIATE_COMMS"] then
-- 0x01, <protocolRev>, <numPanels>, (<panelType>, <panelID>)...
log.df("InitiateComms (0x01) Triggered:")
local protocolRev = byteStringToNumber(data, 5, 4)
local numberOfPanels = byteStringToNumber(data, 9, 4)
log.df(" protocolRev: %s", protocolRev)
log.df(" numberOfPanels: %s", numberOfPanels)
local startNumber = 13
for i=1, numberOfPanels do
local currentPanelType = byteStringToNumber(data, startNumber, 4)
startNumber = startNumber + 4
local currentPanelID = byteStringToNumber(data, startNumber, 4)
startNumber = startNumber + 4
log.df(" panelType: %s panelID: %s", getPanelType(currentPanelType), currentPanelID)
end
-- Respond with ApplicationDefinition (0x81):
log.df("Responding with ApplicationDefinition (0x81)")
local byteString = buildMessage(appMessage["APPLICATION_DEFINITION"], {"CommandPost", "/Users/cmsj", ""})
tangentSocket:send(byteStringToNumber(#byteString)..byteString)
else
log.df("Unknown Reply: %s", hs.inspect(data))
end
end
log.df("CONNECTING TO TANGENT HUB...")
tangentSocket = socket.new()
:setCallback(function(data, tag)
local hexDump = utf8.hexDump(data)
log.df("Received data: "..hexDump)
if readBytesRemaining == 0 then
-- Each message starts with an integer value indicating the number of bytes to follow
-- We don't have any bytes left to read of a previous message, so this must be the first 4 bytes
readBytesRemaining = byteStringToNumber(data, 1, 4)
log.df("New command received from hub, of length: "..readBytesRemaining)
hs.timer.doAfter(0.5, function() tangentSocket:read(readBytesRemaining) end)
else
-- We've read the rest of a command
readBytesRemaining = 0
processHubCommand(data)
-- Get set up for the next command
hs.timer.doAfter(0.5, function() tangentSocket:read(4) end)
end
end)
:connect("127.0.0.1", 64246, function()
log.df("CONNECTION TO TANGENT HUB ESTABLISHED.")
end)
tangentSocket:read(4) -- Read the first 4 bytes, which will trigger the callback.
@latenitefilms

This comment has been minimized.

Copy link

latenitefilms commented Dec 23, 2017

FYI: Line 126 should be:

tangentSocket:send(numberToByteString(#byteString)..byteString)

Apart from that works BRILLIANTLY! THANK YOU!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.