### GET Preset Command Source: https://github.com/snap-one/docs-driverworks/blob/master/sample_drivers/generic_http/www/documentation.md Sends an HTTP GET request to a predefined URL configured in the driver's properties. ```APIDOC ## GET Preset [PRESET] ### Description Send an HTTP GET command to the URL predefined on the Properties page in Preset [PRESET] ### Method GET ### Endpoint [PRESET] ### Parameters #### Path Parameters - **PRESET** (number) - Required - The preset number (1-5) for the URL. ``` -------------------------------- ### Example Output: Table with Indexed Entries Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/README.md This snippet shows a table ({34}) containing a series of indexed entries, similar to the previous example. Investigating such tables, like gGlobalTicketHandlers, is crucial for identifying potential memory leaks related to unhandled responses or timed-out operations. ```xml - Table: {34} { {77} {78}, {79}, {80}, {81}, {82}, {83}, {84}, {85}, {86}, {87}, {88}, {89}, {90}, ``` -------------------------------- ### GET Manual Command Source: https://github.com/snap-one/docs-driverworks/blob/master/sample_drivers/generic_http/www/documentation.md Sends an HTTP GET request to a manually specified URL. ```APIDOC ## GET Manual [URL] ### Description Send an HTTP GET command to the URL specified in [URL] ### Method GET ### Endpoint [URL] ### Parameters #### Path Parameters - **URL** (string) - Required - The full URL to send the GET request to. ``` -------------------------------- ### Example Output: Global Table Contents Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/README.md This snippet shows the first 15 lines of output from the table logger utility, detailing the contents of the global table _G. It illustrates how strings, numbers, booleans, and subtables are represented. ```xml 1. return { Table: {1} 2. { 3. ["tPictureModeCommandMap"]={2}, 4. ["gNetworkIPAddress"]="192.168.1.196", 5. ["COMMAND_QUEUE_SIZE"]=100, 6. ["TV_PROXY_BINDINGID"]=5001, 7. ["sqlite3"]={3}, 8. ["JSON"]={4}, 9. ["debug"]={5} 10. ["C4SystemEvents"]={6}, 11. ["socket"]={7}, 12. ["gSendTimer"]={8}, 13. ["CMDS"]={9}, 14. ["TVAppDeviceID"]=1150, 15. ["OneShotTimer"]={10}, ``` -------------------------------- ### Lua Error Output Example Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md Example of the error output generated when a Lua function is called incorrectly or does not exist. The output provides details about the error type, location, and traceback. ```Text Hello, World! LUA_ERROR [id: 450][name: Generic HTTP Sender][file: generic_http.c4z]: [string "C4Commands"]:2: attempt to call global 'printError' (a nil value) stack traceback: [C]: in function 'printError' [string "C4Commands"]:2: in main chunk ``` -------------------------------- ### Main JSON Parsing Dispatcher Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Dispatches parsing to the appropriate function based on the starting character. Handles strings, numbers, objects, arrays, booleans, and null. ```lua grok_one = function(self, text, start, etc) -- Skip any whitespace start = skip_whitespace(text, start) if start > text:len() then self:onDecodeError("unexpected end of string", text, nil, etc) end if text:find('^"', start) then return grok_string(self, text, start, etc) elseif text:find('^[-0123456789 ]', start) then return grok_number(self, text, start, etc) elseif text:find('^%{', start) then return grok_object(self, text, start, etc) elseif text:find('^%[', start) then return grok_array(self, text, start, etc) elseif text:find('^true', start) then return true, start + 4 elseif text:find('^false', start) then return false, start + 5 elseif text:find('^null', start) then return nil, start + 4 else self:onDecodeError("can't parse JSON", text, start, etc) end end ``` -------------------------------- ### Lua Error Output from Driver File Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md Example of an error originating from a driver's Lua file. The output indicates the specific file and line number where the error occurred, along with the error message. ```Text LUA_ERROR [id: 450][name: Generic HTTP Sender][file: generic_http.c4z]: driver.lua:136: attempt to concatenate local 'filename' (a nil value) stack traceback: [C]: in ? driver.lua:136: in function 'SendMultipart' [string "C4Commands"]:1: in main chunk ``` -------------------------------- ### Example Output: Table with Anonymous Entries Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/README.md This snippet demonstrates a table composed entirely of subtables, indicated by anonymous entries (e.g., {214}). This format often signifies an array-like structure in Lua and can be a point of interest for memory leak analysis if entries are added without proper removal. ```xml - Table: {15} { {214}, {215}, {216}, {217}, {218}, {219}, {220}, ``` -------------------------------- ### Whitespace Skipping Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Skips leading whitespace characters (space, newline, carriage return, tab) from a given starting position in a string. ```lua local function skip_whitespace(text, start) local match_start, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 if match_end then return match_end + 1 end ``` -------------------------------- ### Print 'Hello, World!' in Lua Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md The most basic Lua command to print a string to the output. This is useful for initial testing and verifying the Lua environment is responsive. ```Lua print ('Hello, World!') ``` -------------------------------- ### Timer Configuration Table Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Defines parameters for various timers, including name, ID, units, interval, and timer ID. ```Lua { [_name]="ChannelUpDownTimer", [_id]=0, [_units]="SECONDS", [_info]="", [_interval]=45, [_timerID]=4, } ``` ```Lua { [_name]="SetInputInProgressTimer", [_id]=0, [_units]="SECONDS", [_info]="", [_interval]=60, [_timerID]=3, } ``` ```Lua { [_name]="PowerOnDelayTimer", [_id]=0, [_units]="SECONDS", [_info]="", [_interval]=12, [_timerID]=1, } ``` -------------------------------- ### POST Preset Command Source: https://github.com/snap-one/docs-driverworks/blob/master/sample_drivers/generic_http/www/documentation.md Sends an HTTP POST request to a predefined URL with specified data. ```APIDOC ## POST Preset [PRESET, DATA] ### Description Send an HTTP POST command to the URL predefined on the Properties page in Preset [PRESET] with attached data as specified in [DATA] ### Method POST ### Endpoint [PRESET] ### Parameters #### Path Parameters - **PRESET** (number) - Required - The preset number (1-5) for the URL. - **DATA** (string) - Required - The data to be sent in the POST request body. ``` -------------------------------- ### Input Mapping Table Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Maps string identifiers to internal names for various audio/video inputs. ```Lua ["SAT"]="sat", ["TUNEIN"]="tunein", ["TOS LINK"]="toslink", ["MATRIX16"]="hdmi_matrix_16", ["LINE IN"]="linein", ["MATRIX4"]="hdmi_matrix_4", ["LGHDMI3"]="hdmi_13", ["MATRIX7"]="hdmi_matrix_7", ["A.AUX"]="AUX_A", ["RADIO"]="radio", ["AV IN"]="av", ["MUSIC"]="music", ["MATRIX13"]="hdmi_matrix_13", ["MATRIX11"]="hdmi_matrix_11", ["MATRIX10"]="hdmi_matrix_10", ["HOMEMEDIA"]="homemedia", ["LGHDMI1"]="hdmi_11" ``` -------------------------------- ### Metadata Table Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Contains metadata for a library, including URL, version, and copyright information. ```Lua { ["__index"]={27}, ["URL"]="http://regex.info/blog/", ["VERSION"]=20111207.5, ["COPYRIGHT"]="2010-2011 Jeffrey Friedl", } ``` -------------------------------- ### POST Manual Command Source: https://github.com/snap-one/docs-driverworks/blob/master/sample_drivers/generic_http/www/documentation.md Sends an HTTP POST request to a manually specified URL with specified data. ```APIDOC ## POST Manual [URL, DATA] ### Description Send an HTTP POST command to the URL specified in [URL] with attached data as specified in [DATA] ### Method POST ### Endpoint [URL] ### Parameters #### Path Parameters - **URL** (string) - Required - The full URL to send the POST request to. - **DATA** (string) - Required - The data to be sent in the POST request body. ``` -------------------------------- ### JSON Decode Method Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt The main entry point for decoding a JSON string. Performs initial validation and calls the primary parsing function. Handles UTF-8 encoding. ```lua function OBJDEF:decode(text, etc) if type(self) ~= 'table' or self.__index ~= OBJDEF then OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc) end if text == nil then self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc) elseif type(text) ~= 'string' then self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc) end if text:match('^%s*$') then return nil end if text:match('^%s*<') then -- Can't be JSON... we'll assume it's HTML self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc) end -- -- Ensure that it's not UTF-32 or UTF-16. -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), -- but this package can't handle them. -- if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc) end local success, value = pcall(grok_one, self, text, 1, etc) if success then return value else -- should never get here... JSON parse errors should have been caught earlier assert(false, value) return nil end end ``` -------------------------------- ### OBJDEF:new Constructor for JSON Encoder/Decoder Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt A constructor function for creating new instances of the OBJDEF object, optionally accepting arguments to initialize the object's properties. ```lua function OBJDEF:new(args) local new = { } if args then for key, val in pairs(args) do new[key] = val end end return setmetatable(new, OBJDEF) end ``` -------------------------------- ### Define a Lua Function Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md Defines a reusable Lua function that accepts an argument and prints a greeting. This function becomes globally available after execution. ```Lua function protoHello (what) print ('Hello, ' .. tostring (what)) end ``` -------------------------------- ### Error Handling and Assertion Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Handles formatting error messages and asserting failure. It checks for the presence of 'self.assert' and falls back to the global 'assert' if not found. ```lua if text then if location then message = string.format("%s at char %d of: %s", message, location, text) else message = string.format("%s: %s", message, text) end end if etc ~= nil then message = message .. " (" .. OBJDEF:encode(etc) .. ")" end if self.assert then self.assert(false, message) else assert(false, message) end end ``` -------------------------------- ### Create New JSON Object Instance Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Creates a new Lua table intended to represent a JSON object, applying the `isObject` metatable. An optional table can be provided as the initial content. ```lua function OBJDEF:newObject(tbl) return setmetatable(tbl or {}, isObject) end ``` -------------------------------- ### Execute Lua Code with an Error Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md This snippet intentionally includes a call to a non-existent function to demonstrate Lua error handling. It shows that code before the error executes, but code after does not. ```Lua print ('Hello, World!') printError ('Hello, Error!') ``` -------------------------------- ### Create New JSON Array Instance Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Creates a new Lua table intended to represent a JSON array, applying the `isArray` metatable. An optional table can be provided as the initial content. ```lua function OBJDEF:newArray(tbl) return setmetatable(tbl or {}, isArray) end ``` -------------------------------- ### OBJDEF:encode_pretty Method for Pretty JSON Printing Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Provides a method interface for pretty-printing Lua values to JSON strings. It initializes parent tracking and calls the internal `encode_pretty_value` function with an empty initial indent. ```lua function OBJDEF:encode_pretty(value, etc) local parents = {} local subtable_indent = "" return encode_pretty_value(self, value, parents, subtable_indent, etc) end ``` -------------------------------- ### Error Handler Aliases Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Sets up aliases for error handling functions, mapping 'onDecodeOfNilError' and 'onDecodeOfHTMLError' to 'onDecodeError'. ```lua OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError ``` -------------------------------- ### Numeric Array Table Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt A simple table containing a sequence of numbers. ```Lua { [0]=0, } ``` -------------------------------- ### Call a Defined Lua Function Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md Invokes a previously defined global Lua function. This demonstrates that functions defined in the Lua command window persist within the current session. ```Lua protoHello ('Driverworks') ``` -------------------------------- ### OBJDEF:encode Method for JSON Encoding Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Provides a method interface for encoding Lua values to JSON strings. It initializes the parent tracking and calls the internal `encode_value` function. ```lua function OBJDEF:encode(value, etc) if type(self) ~= 'table' or self.__index ~= OBJDEF then OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) end local parents = {} return encode_value(self, value, parents, etc) end ``` -------------------------------- ### Define Custom Array Metatable Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Defines a metatable for Lua tables representing JSON arrays. This metatable includes a `__tostring` function for clear identification. ```lua local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray ``` -------------------------------- ### Create New JSON Object with Custom Error Handlers Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Create a separate Lua JSON object with its own error handlers by reloading JSON.lua or using the `:new()` method. This allows for isolated error handling configurations. ```lua -- If you want to create a separate Lua JSON object with its own error handlers, -- you can reload JSON.lua or use the :new() method. ``` -------------------------------- ### JSON Library Chunk Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt A Lua code chunk for JSON encoding and decoding, including error handling mechanisms. ```Lua --\n-- Copyright 2010-2011 Jeffrey Friedl\n-- http://regex.info/blog/\n--\nlocal COPYRIGHT = "2010-2011 Jeffrey Friedl" local URL = "http://regex.info/blog/" local VERSION = 20111207.5 -- version history at end of file OBJDEF = { VERSION = VERSION, URL = URL, COPYRIGHT = COPYRIGHT } --\n-- Simple JSON encoding and decoding in pure Lua. -- http://www.json.org/ --\n--\n-- JSON = loadfile "JSON.lua" -- one-time load of the routines --\n-- local lua_value = JSON:decode(raw_json_text) --\n-- local raw_json_text = JSON:encode(lua_table_or_value) -- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability --\n--\n-- DECODING --\n-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines --\n-- local lua_value = JSON:decode(raw_json_text) --\n-- If the JSON text is for an object or an array, e.g. -- { "what": "books", "count": 3 } -- or -- [ "Larry", "Curly", "Moe" ] --\n-- the result is a Lua table, e.g. -- { what = "books", count = 3 } -- or -- { "Larry", "Curly", "Moe" } --\n--\n-- The encode and decode routines accept an optional second argument, "etc", which is not used -- during encoding or decoding, but upon error is passed along to error handlers. It can be of any -- type (including nil). --\n-- With most errors during decoding, this code calls --\n-- JSON:onDecodeError(message, text, location, etc) --\n-- with a message about the error, and if known, the JSON text being parsed and the byte count -- where the problem was discovered. You can replace the default JSON:onDecodeError() with your -- own function. --\n-- The default onDecodeError() merely augments the message with data about the text and the -- location if known (and if a second 'etc' argument had been provided to decode(), its value is -- tacked onto the message as well), and then calls JSON.assert(), which itself defaults to Lua's -- built-in assert(), and can also be overridden. --\n-- For example, in an Adobe Lightroom plugin, you might use something like --\n-- function JSON:onDecodeError(message, text, location, etc) -- LrErrors.throwUserError("Internal Error: invalid JSON data") -- end --\n-- or even just --\n-- function JSON.assert(message) -- LrErrors.throwUserError("Internal Error: " .. message) -- end --\n-- If JSON:decode() is passed a nil, this is called instead: --\n-- JSON:onDecodeOfNilError(message, nil, nil, etc) --\n-- and if JSON:decode() is passed HTML instead of JSON, this is called: --\n-- JSON:onDecodeOfHTMLError(message, text, nil, etc) --\n-- The use of the fourth 'etc' argument allows stronger coordination between decoding and error -- reporting, especially when you provide your own error-handling routines. Continuing with the -- the Adobe Lightroom plugin example: --\n-- function JSON:onDecodeError(message, text, location, etc) -- LrErrors.throwUserError("Internal Error: invalid JSON data") -- end -- ``` -------------------------------- ### Define Custom Object Metatable Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Defines a metatable for Lua tables representing JSON objects. This metatable includes a `__tostring` function for clear identification. ```lua local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject ``` -------------------------------- ### Encode Error Handling Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Handles encoding errors by appending additional information if available and then asserting failure. Similar to 'onDecodeError', it uses 'self.assert' or the global 'assert'. ```lua function OBJDEF:onEncodeError(message, etc) if etc ~= nil then message = message .. " (" .. OBJDEF:encode(etc) .. ")" end if self.assert then self.assert(false, message) else assert(false, message) end end ``` -------------------------------- ### Load JSON Library and Set Strict Types Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Load the JSON.lua routines and enable strict type checking for decoding. This ensures that JSON objects and arrays are preserved as distinct Lua table types upon decoding, allowing for accurate re-encoding. ```lua JSON = (loadfile "JSON.lua")() --load the routines JSON.strictTypes = true ``` -------------------------------- ### Convert Unicode Codepoint to UTF-8 Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Converts a numeric Unicode codepoint into its UTF-8 string representation. Handles codepoints up to 127, 2047, 65535, and beyond, including checks for invalid UTF-8 sequences. ```lua local function unicode_codepoint_as_utf8(codepoint) -- -- codepoint is a number -- if codepoint <= 127 then return string.char(codepoint) elseif codepoint <= 2047 then -- -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 -- local highpart = math.floor(codepoint / 0x40) local lowpart = codepoint - (0x40 * highpart) return string.char(0xC0 + highpart, 0x80 + lowpart) elseif codepoint <= 65535 then -- -- 1110yyyy 10yyyyxx 10xxxxxx -- local highpart = math.floor(codepoint / 0x1000) local remainder = codepoint - 0x1000 * highpart local midpart = math.floor(remainder / 0x40) local lowpart = remainder - 0x40 * midpart highpart = 0xE0 + highpart midpart = 0x80 + midpart lowpart = 0x80 + lowpart -- -- Check for an invalid character (thanks Andy R. at Adobe). -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 -- if ( highpart == 0xE0 and midpart < 0xA0 ) or ( highpart == 0xED and midpart > 0x9F ) or ( highpart == 0xF0 and midpart < 0x90 ) or ( highpart == 0xF4 and midpart > 0x8F ) then return "?" else return string.char(highpart, midpart, lowpart) end else -- -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx -- local highpart = math.floor(codepoint / 0x40000) local remainder = codepoint - 0x40000 * highpart local midA = math.floor(remainder / 0x1000) remainder = remainder - 0x1000 * midA local midB = math.floor(remainder / 0x40) local lowpart = remainder - 0x40 * midB return string.char(0xF0 + highpart, 0x80 + midA, 0x80 + midB, 0x80 + lowpart) end end ``` -------------------------------- ### Encode Lua Table to JSON Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Encode a Lua table or value into a JSON string. Use `encode` for a compact representation or `encode_pretty` for a human-readable, indented format. ```lua local raw_json_text = JSON:encode(lua_table_or_value) local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability ``` -------------------------------- ### String Parsing with Escape Sequences Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Parses a string enclosed in double quotes, handling various escape sequences including backspace, form feed, newline, carriage return, tab, and Unicode characters (both standalone and surrogate pairs). ```lua local function grok_string(self, text, start, etc) if text:sub(start,start) ~= '"' then self:onDecodeError("expected string's opening quote", text, start, etc) end local i = start + 1 -- +1 to bypass the initial quote local text_len = text:len() local VALUE = "" while i <= text_len do local c = text:sub(i,i) if c == '"' then return VALUE, i + 1 end if c ~= '\\' then VALUE = VALUE .. c i = i + 1 elseif text:match('^\\b', i) then VALUE = VALUE .. "\b" i = i + 2 elseif text:match('^\\f', i) then VALUE = VALUE .. "\f" i = i + 2 elseif text:match('^\\n', i) then VALUE = VALUE .. "\n" i = i + 2 elseif text:match('^\\r', i) then VALUE = VALUE .. "\r" i = i + 2 elseif text:match('^\\t', i) then VALUE = VALUE .. "\t" i = i + 2 else local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) if hex then i = i + 6 -- bypass what we just read -- We have a Unicode codepoint. It could be standalone, or if in the proper range and -- followed by another in a specific range, it'll be a two-code surrogate pair. local codepoint = tonumber(hex, 16) if codepoint >= 0xD800 and codepoint <= 0xDBFF then -- it's a hi surrogate... see whether we have a following low local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) if lo_surrogate then i = i + 6 -- bypass the low surrogate we just read codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) else -- not a proper low, so we'll just leave the first codepoint as is and spit it out. end end VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) else -- just pass through what's escaped VALUE = VALUE .. text:match('^\\(.)', i) i = i + 2 end end end self:onDecodeError("unclosed string", text, start, etc) end ``` -------------------------------- ### Number Parsing with Locale Support Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Parses a number from a string, supporting optional decimal and exponential parts. It includes a localized 'tonumber_loc' function to handle both '.' and ',' as decimal separators. ```lua local function grok_number(self, text, start, etc) -- -- Grab the integer part -- local integer_part = text:match('^-?[1-9]%d*', start) or text:match("^-?0", start) if not integer_part then self:onDecodeError("expected number", text, start, etc) end local i = start + integer_part:len() -- -- Grab an optional decimal part -- now with comma-separated decimal support (probably won't happen as JSON standard suggests only period-separated decimals) -- local decimal_part = text:match('^[%.%,]%d+', i) or "" i = i + decimal_part:len() -- -- Grab an optional exponential part -- local exponent_part = text:match('^[eE][-+]?%d+', i) or "" i = i + exponent_part:len() local full_number_text = integer_part .. decimal_part .. exponent_part -- localized function to handle comma-separated decimals local tonumber_loc = function (str, base) local s = str:gsub(",", ".") -- Assume US Locale decimal separator local num = tonumber(s, base) if (num == nil) then s = str:gsub("%.", ",") -- Non-US Locale decimal separator num = tonumber(s, base) end return num end local as_number = tonumber_loc (full_number_text) if not as_number then self:onDecodeError("bad number", text, start, etc) end return as_number, i end ``` -------------------------------- ### Create JSON String Literal Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Constructs a JSON string literal by enclosing a value in double quotes and escaping any special characters using a predefined pattern and replacement function. ```lua local function json_string_literal(value) local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) return '"' .. newval .. '"' end ``` -------------------------------- ### Trigger Lua Error from Driver Code Source: https://github.com/snap-one/docs-driverworks/blob/master/driver_development_training/Fast Prototyping and Debugging with Composer.md Calls a driver-specific function without providing necessary arguments, intentionally causing a Lua error. This demonstrates how errors originating from driver files are reported. ```Lua SendMultipart () ``` -------------------------------- ### Pretty Print Lua Table to JSON String Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Encodes a Lua value into a JSON string with indentation for pretty-printing. Handles nested tables, objects, and arrays, applying specific formatting for keys and values. ```lua local encode_pretty_value -- must predeclare because it calls itself function encode_pretty_value(self, value, parents, indent, etc) if type(value) == 'string' then return json_string_literal(value) elseif type(value) == 'number' then return tostring(value) elseif type(value) == 'boolean' then return tostring(value) elseif type(value) == 'nil' then return 'null' elseif type(value) ~= 'table' then self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) else -- -- A table to be converted to either a JSON object or array. -- local T = value if parents[T] then self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) end parents[T] = true local result_value local object_keys = object_or_array(self, T, etc) if not object_keys then -- -- An array... -- local ITEMS = { } for i = 1, #T do table.insert(ITEMS, encode_pretty_value(self, T[i], parents, indent, etc)) end result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" else -- -- An object -- can keys be numbers? -- local KEYS = { } local max_key_length = 0 for _, key in ipairs(object_keys) do local encoded = encode_pretty_value(self, tostring(key), parents, "", etc) max_key_length = math.max(max_key_length, #encoded) table.insert(KEYS, encoded) end local key_indent = indent .. " " local subtable_indent = indent .. string.rep(" ", max_key_length + 2 + 4) local FORMAT = "%s%" .. tostring(max_key_length) .. "s: %s" local COMBINED_PARTS = { } for i, key in ipairs(object_keys) do local encoded_val = encode_pretty_value(self, T[key], parents, subtable_indent, etc) table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) end result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" end parents[T] = false return result_value end end ``` -------------------------------- ### Backslash Escape Sequence Replacement Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt A function to replace common backslash escape sequences in strings with their literal representations. ```lua local function backslash_replacement_function(c) if c == "\n" then return "\\n" elseif c == "\r" then return "\\r" elseif c == "\t" then return "\\t" elseif c == "\b" then ``` -------------------------------- ### Encode Lua Value to JSON Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Recursively encodes a Lua value into its JSON representation. Handles nil, strings, numbers (including NaN and infinities), booleans, and tables. Detects circular references in tables. ```lua -- -- Encode -- local encode_value -- must predeclare because it calls itself function encode_value(self, value, parents, etc) if value == nil then return 'null' end if type(value) == 'string' then return json_string_literal(value) elseif type(value) == 'number' then if value ~= value then -- -- NaN (Not a Number). -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. -- return "null" elseif value >= math.huge then -- -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should -- really be a package option. Note: at least with some implementations, positive infinity -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" -- case first. -- return "1e+9999" elseif value <= -math.huge then -- -- Negative infinity. -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. -- return "-1e+9999" else return tostring(value) end elseif type(value) == 'boolean' then return tostring(value) elseif type(value) ~= 'table' then self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) else -- -- A table to be converted to either a JSON object or array. -- local T = value if parents[T] then self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) else parents[T] = true end local result_value local object_keys, maximum_number_key = object_or_array(self, T, etc) if maximum_number_key then -- -- An array... -- local ITEMS = { } for i = 1, maximum_number_key do table.insert(ITEMS, encode_value(self, T[i], parents, etc)) end result_value = "[" .. table.concat(ITEMS, ",") .. "]" ``` -------------------------------- ### Define JSON String Escaping Pattern Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Defines a Lua pattern string that matches characters requiring escaping within a JSON string. This includes double quotes, backslashes, null characters, and control characters. ```lua local chars_to_be_escaped_in_JSON_string = '[' .. '"' -- class sub-pattern to match a double quote .. '%\\' -- class sub-pattern to match a backslash .. '%z' -- class sub-pattern to match a null .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters .. ']' ``` -------------------------------- ### Parse JSON Object Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Parses a JSON object from a string. Expects keys to be strings and values to be parsed recursively. Handles nested objects and arrays. ```lua local function grok_object(self, text, start, etc) if not text:sub(start,start) == '{' then self:onDecodeError("expected '{'", text, start, etc) end local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' local VALUE = self.strictTypes and self:newObject { } or { } if text:sub(i,i) == '}' then return VALUE, i + 1 end local text_len = text:len() while i <= text_len do local key, new_i = grok_string(self, text, i, etc) i = skip_whitespace(text, new_i) if text:sub(i, i) ~= ':' then self:onDecodeError("expected colon", text, i, etc) end i = skip_whitespace(text, i + 1) local val, new_i = grok_one(self, text, i) VALUE[key] = val -- -- Expect now either '}' to end things, or a ',' to allow us to continue. -- i = skip_whitespace(text, new_i) local c = text:sub(i,i) if c == '}' then return VALUE, i + 1 end if text:sub(i, i) ~= ',' then self:onDecodeError("expected comma or '}'", text, i, etc) end i = skip_whitespace(text, i + 1) end self:onDecodeError("unclosed '{'", text, start, etc) end ``` -------------------------------- ### Parse JSON Array Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Parses a JSON array from a string. Handles nested arrays and objects. Ensures the array is properly closed with ']'. ```lua local function grok_array(self, text, start, etc) if not text:sub(start,start) == '[' then self:onDecodeError("expected '['", text, start, etc) end local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' local VALUE = self.strictTypes and self:newArray { } or { } if text:sub(i,i) == ']' then return VALUE, i + 1 end local text_len = text:len() while i <= text_len do local val, new_i = grok_one(self, text, i) table.insert(VALUE, val) i = skip_whitespace(text, new_i) -- -- Expect now either ']' to end things, or a ',' to allow us to continue. -- local c = text:sub(i,i) if c == ']' then return VALUE, i + 1 end if text:sub(i, i) ~= ',' then self:onDecodeError("expected comma or '['", text, i, etc) end i = skip_whitespace(text, i + 1) end self:onDecodeError("unclosed '['", text, start, etc) end ``` -------------------------------- ### Encode Lua Table to JSON String Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Encodes a Lua value into a JSON string. Handles nested tables, objects, and arrays. Includes basic error checking for circular references and unsupported types. ```lua local encode_value -- must predeclare because it calls itself function encode_value(self, value, parents, etc) if type(value) == 'string' then return json_string_literal(value) elseif type(value) == 'number' then return tostring(value) elseif type(value) == 'boolean' then return tostring(value) elseif type(value) == 'nil' then return 'null' elseif type(value) ~= 'table' then self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) else -- -- A table to be converted to either a JSON object or array. -- local T = value if parents[T] then self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) end parents[T] = true local result_value local object_keys = object_or_array(self, T, etc) if not object_keys then -- -- An array... -- local ITEMS = { } for i = 1, #T do table.insert(ITEMS, encode_value(self, T[i], parents, etc)) end result_value = "[" .. table.concat(ITEMS, ",") .. "]" else -- -- An object -- -- -- We'll always sort the keys, so that comparisons can be made on -- the results, etc. The actual order is not particularly -- important (e.g. it doesn't matter what character set we sort -- as); it's only important that it be deterministic... the same -- every time. -- local PARTS = { } for _, key in ipairs(object_keys) do local encoded_key = encode_value(self, tostring(key), parents, etc) local encoded_val = encode_value(self, T[key], parents, etc) table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) end result_value = "{" .. table.concat(PARTS, ",") .. "}" else -- -- An empty array/object... we'll treat it as an array, though it should really be an option -- result_value = "[]" end parents[T] = false return result_value end end ``` -------------------------------- ### Determine if Table is Object or Array Source: https://github.com/snap-one/docs-driverworks/blob/master/table_logger_utility/tables/Sample Driver Tables.txt Analyzes a Lua table to determine if it should be encoded as a JSON object or array. It checks for the presence of string keys versus numeric keys. Mixed key types will result in an error. ```lua local function object_or_array(self, T, etc) -- -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON -- object. If there are only numbers, it's a JSON array. -- -- If we'll be converting to a JSON object, we'll want to sort the keys so that the -- end result is deterministic. -- local string_keys = { } local seen_number_key = false local maximum_number_key for key in pairs(T) do if type(key) == 'number' then seen_number_key = true if not maximum_number_key or maximum_number_key < key then maximum_number_key = key end elseif type(key) == 'string' then table.insert(string_keys, key) else self:onEncodeError("can't encode table with a key of type " .. type(key), etc) end end if seen_number_key and #string_keys > 0 then -- -- Mixed key types... don't know what to do, so bail -- self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) elseif #string_keys == 0 then -- -- An array -- if seen_number_key then return nil, maximum_number_key -- an array else -- -- An empty table... -- if tostring(T) == "JSON array" then return nil elseif tostring(T) == "JSON object" then return { } else -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects return nil end end else -- -- An object, so return a list of keys -- table.sort(string_keys) return string_keys end end ```