Jump to content
C4 Forums | Control4

Various driver coding questions


zaphod

Recommended Posts

  1. Is there a C4: API command to get the current date/time or do you have to use Lua commands for this?
  2. There are various properties for some types of drivers - like power_delay and power_command_needed, etc.  How do I access these properties from Lua code in the driver?
  3. How do I get my icons to show up?  I have created the /www/icons folders in my c4z file like 20x20, 70x70, 90x90, 140x140, 400x400 but C4 is still using generic icons.  Do the icon files have to have to have specific names as well, like icon.png?  Do you have to specify the icons to be used somewhere in the XML file?
Link to comment
Share on other sites


For icons:

These are the icons that show in composer:

<small image_source="c4z">icons/device_sm.png</small>
	<large image_source="c4z">icons/device_lg.png</large>

for the icons for Navigator

<capabilities>
		<navigator_display_option proxybindingid="5001">
			<display_icons>
				<Icon height="140" width="140">controller://driver/nocontrol_laptop/icons/device/experience_140.png</Icon>
				<Icon height="130" width="120">controller://driver/nocontrol_laptop/icons/device/experience_130.png</Icon>
				<Icon height="120" width="120">controller://driver/nocontrol_laptop/icons/device/experience_120.png</Icon>
				<Icon height="110" width="110">controller://driver/nocontrol_laptop/icons/device/experience_110.png</Icon>
				<Icon height="100" width="100">controller://driver/nocontrol_laptop/icons/device/experience_100.png</Icon>
				<Icon height="90" width="90">controller://driver/nocontrol_laptop/icons/device/experience_90.png</Icon>
				<Icon height="80" width="80">controller://driver/nocontrol_laptop/icons/device/experience_80.png</Icon>
				<Icon height="70" width="70">controller://driver/nocontrol_laptop/icons/device/experience_70.png</Icon>
				<Icon height="60" width="60">controller://driver/nocontrol_laptop/icons/device/experience_60.png</Icon>
				<Icon height="50" width="50">controller://driver/nocontrol_laptop/icons/device/experience_50.png</Icon>
				<Icon height="40" width="40">controller://driver/nocontrol_laptop/icons/device/experience_40.png</Icon>
				<Icon height="30" width="30">controller://driver/nocontrol_laptop/icons/device/experience_30.png</Icon>
				<Icon height="20" width="20">controller://driver/nocontrol_laptop/icons/device/experience_20.png</Icon>
			</display_icons>
		</navigator_display_option>
	</capabilities>

obviously you would update the name and paths

Link to comment
Share on other sites

What exactly will I have to replace?  Does "controller" get changed to the actual IP of the controller?  I am guessing not For example, here is the complete path to my 140x140 icon is:

/mnt/internal/c4z/SageExtender/www/icons/140x140/sage400.png

<Icon height="140" width="140">controller://driver/SageExtender/icons/140x140/sage140.png</Icon>

or

<Icon height="140" width="140">controller://driver/SageExtender/www/icons/140x140/sage400.png</Icon>

or even something else.

And where do you enter this in DriverEditor 3.01?  Or do you just paste it directly into the XML file by editing driver.xml within the c4z zip file?  If it is the latter is there an easy way to edit files within a zip archive rather than unzipping and then rezipping?

 

Link to comment
Share on other sites

What exactly will I have to replace?  Does "controller" get changed to the actual IP of the controller?  I am guessing not For example, here is the complete path to my 140x140 icon is:
/mnt/internal/c4z/SageExtender/www/icons/140x140/sage400.png
controller://driver/SageExtender/icons/140x140/sage140.png
or
controller://driver/SageExtender/www/icons/140x140/sage400.png
or even something else.
And where do you enter this in DriverEditor 3.01?  Or do you just paste it directly into the XML file by editing driver.xml within the c4z zip file?  If it is the latter is there an easy way to edit files within a zip archive rather than unzipping and then rezipping?
 
I don't use driver editor. I use vs code and the command line to build

controller://driver/SageExtender/icons/140x140/sage140.png

Sent from my Pixel XL using Tapatalk

Link to comment
Share on other sites

Ok thanks.   So you can just use on folder for all of the icons rather than separate ones?  That is much easier!

 

One other thing that isn't working for me is Actions.  Here is the XML code which is within the config section:

    <actions>
      <action>
        <name>Test Action</name>
        <command>test1</command>
      </action>
      <action>
        <name>Test Action2</name>
        <command>test2</command>
      </action>
    </actions>

And I have the following code within my lua file:

function LUA_ACTION.test1(str)  --Required for Actions screen
     print ("Action1 Testing Connection"..str)
    g_URLPacket = url1.."/sage/Home"
    C4:urlGet(g_URLPacket)
end

function LUA_ACTION.test2(str)
     print ("Action2 Testing Connection"..str)
    g_URLPacket = url1.."/sage/Home"
    C4:urlGet(g_URLPacket)
end

function LUA_ACTION.test1(str)  --Required for Actions screen
     print ("Action1 Testing Connection"..str)
	g_URLPacket = url1.."/sage/Home"
	C4:urlGet(g_URLPacket)
end

function LUA_ACTION.test2(str)
     print ("Action2 Testing Connection"..str)
	g_URLPacket = url1.."/sage/Home"
	C4:urlGet(g_URLPacket)
end

But nothing happens - I never even get anything from the print command in the Lua window but it seems like C4 is getting the action, just not acting on it as nothing prints out to the Lua window.  If I look in the driver-event log files I do see:

2017-12-31 13:19:03 -0500 home-controller-hc250-000FFF163637 [16662] INFO: Executing command (LUA_ACTION) on driver SageTV(215)

Link to comment
Share on other sites

Do you have this bit of code in there?

--[[
	EX_CMD.LUA_ACTION
		Function called for any actions executed by the user from the Actions Tab in Composer.
--]]
function EX_CMD.LUA_ACTION(tParams)
	if tParams ~= nil then
		for cmd,cmdv in pairs(tParams) do
			if cmd == "ACTION" then
				if (LUA_ACTION[cmdv] ~= nil) then
					LUA_ACTION[cmdv]()
				else
					Dbg:Alert("Undefined Action")
					Dbg:Alert("Key: " .. cmd .. " Value: " .. cmdv)
				end
			else
				Dbg:Alert("Undefined Command")
				Dbg:Alert("Key: " .. cmd .. " Value: " .. cmdv)
			end
		end
	end
end

 

Link to comment
Share on other sites

I have my icons showing up very nicely in Navigator but still no luck for Composer.  I have tried stopping and starting Composer and I can verify that the files exist as:
/mnt/internal/c4z/SageExtender/www/icons/device_sm.png and/mnt/internal/c4z/SageExtender/www/icons/device_lg.png.  These are 16x16 and 32x32 png files created in paint.net.

 I notice that these files also seem to be defined in a proxy line.  So I have added a line like this:

  <proxies>
    <proxy proxybindingid="5001" name="SageTV" image_source="c4z" small_image="icons/device_sm.png" large_image="icons/device_lg.png">media_player</proxy>
  </proxies>

But still no luck.  Does the name field here have significance?  

Is there anything else that you have to do to get Composer to refresh the icons it uses?

 

Link to comment
Share on other sites

I still can't get actions to work at all.  Here is the Actions part from my XML file.

    <actions>
      <action>
        <name>Test Action</name>
        <command>testit</command>
      </action>
    </actions>

And here is the code from my Lua file - I don't know if all of this is needed:

--Function called for any actions executed by the user from the Actions Tab in Composer.
function EX_CMD.LUA_ACTION(tParams)
	if tParams ~= nil then
		for cmd,cmdv in pairs(tParams) do
			if cmd == "ACTION" then
				if (LUA_ACTION[cmdv] ~= nil) then
					LUA_ACTION[cmdv]()
				else
					--Dbg:Alert("Undefined Action")
					print("Undefined Action")  -- all print items were changed from Dbg:Alert as above
					print("Key: " .. cmd .. " Value: " .. cmdv)
				end
			else
				print("Undefined Command")
				print("Key: " .. cmd .. " Value: " .. cmdv)
			end
		end
	end
end

function LUA_ACTION.testit(str)  --Required for Actions screen
    print ("Action1 Testing Connection"..str)
    TestURL()
end

And nothing happens when I push my actions button - although the Sage driver_event log is obviously seeing something as I do see this entry,  But no code appears to be executed

2017-12-31 22:15:10 -0500 home-controller-hc250-000FFF163637 [2020] INFO: Executing command (LUA_ACTION) on driver SageTV(215)

 

Link to comment
Share on other sites

What does your ExecuteCommand function look like?

Ultimately, all commands and actions go through ExecuteCommand.  The templates and sample code go through, and based on the name of the command, fire the relevant EX_CMD or LUA_ACTION.

For most drivers, mine are much simpler than all that:

function ExecuteCommand(strCmd, tParams)
  tParams = tParams or {}

  dbg("ExecuteCommand: " .. strCmd)
  for k,v in pairs(tParams) do dbg(k,v) end

  if (strCmd == "LUA_ACTION") then
    local action = tParams["ACTION"] or ""
    if (action == "SETUP_ACCOUNT") then
      -- set up account code goes here...
    end
  end

  if (strCmd == "Set Home Status") or (strCmd == "Set All Home Status") then
    local status = tParams["Status"] or ""
    if (status == "") then return end

    -- do Set Home Status stuff here...
  end
end

RyanE

Link to comment
Share on other sites

I don't have an ExecuteCommand function so I guess that is the issue.  I just pasted in your code into my Lua file (other than changing the dbg to a print in line 4) but it doesn't help at all - as I still don't get any output printed to the Lua window, nor do any actions occur.  

So do you need all three functions - a ExecuteCommand, a LUA_ACTION and a EX.CMD_LUA_ACTION?  What is the process flow when a LUA_ACTION occurs.  I now it is actually seeing a function from the driver event log.

Link to comment
Share on other sites

If the ExecuteCommand you added in (mine) from above isn't showing anything (assuming you have a 'dbg' function), then there is a different definition for the ExecuteCommand defined somewhere else in your code, that's writing over mine.

If you're using a template file, it likely includes other .lua files, which is where it could be defined.

If you post the whole file, we could see what's going on.

RyanE

 

Link to comment
Share on other sites

Ok, here is all of the Lua code.  Do you need any of the XML file as well?  Do I need some XML dealing with Commands?  Maybe that is what I am missing?  I have an <actions> section in my XML but no <commands> section.  If I do need this then how do you set it up?  Does the name have to tie back exactly to the command name in  the Action?

--[[     12/30/2017/ 13:00 Add URL test to verify connection.  Tests for good URL, authentication and makes sure that
	   context/extender is alive.
	   ]]

CMDS = {

PLAY			= "Play",
PAUSE		= "Pause",
STOP			= "Stop",
OPEN_CLOSE	= "Stop%2FEject",
SKIP_FWD		= "Right%2FSkip+Fwd",
SKIP_FWD2		= "Skip%20Fwd%20%232",
SKIP_REV		= "Skip+Bkwd%2FPage+Left",
SCAN_FWD		= "Skip%20Fwd%20%232",
SCAN_REV		= "Skip%20Bkwd%20%232",
RECORD		= "Record",
ENTER		= "Select",
UP			= "Up",
GUIDE          = "Guide",
DOWN			= "Down",
LEFT			= "Left",
RIGHT		= "Right",
MENU			= "Home",
START		= "Power+On",
BACK			= "Back",
RECALL         = "Previous+Channel",
CANCEL		= "Back",
INFO			= "Info",
WINDOW_HOME	= "Home",
WINDOW_MUSIC	= "Music+Jukebox",
WINDOW_VIDEOS	= "Video+Library",
MUTE_TOGGLE	= "Mute",
PVR			= "Options",
PAGE_UP		= "Channel+Up%2FPage+Up",
PAGE_DOWN		= "Channel+Down%2FPage+Down",
NUMBER_0		= "Num+0",
NUMBER_1		= "Num+1",
NUMBER_2		= "Num+2",
NUMBER_3		= "Num+3",
NUMBER_4		= "Num+4",
NUMBER_5		= "Num+5",
NUMBER_6		= "Num+6",
NUMBER_7		= "Num+7",
NUMBER_8		= "Num+8",
NUMBER_9		= "Num+9",
PROGRAM_A	     = "Delete",
PROGRAM_B	     = "Guide",
PROGRAM_C	     = "Power",
PROGRAM_D	     = "Options",

}

ip 		 = C4:GetBindingAddress(6001)
serverport  = Properties["Web Server Port"]
mac		 = Properties["Extender MACID"]
pass		 = Properties["Password"]
user		 = Properties["User"]
url1	  	 =  "http://" .. user .. ":" .. pass .."@"..ip ..":".. serverport
g_Receivebuffer=""
C4:CreateNetworkConnection (6100, Properties["Extender IP Address"]) -- this is for telnet control to extender
    
--=-=-=-=-=-=-=-=- FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-

function ReceivedFromNetwork(idBinding, nPort, strData)
    print ("Reciving from Network on Binding and Port"..idBinding .. nPort..strData)
    g_Receivebuffer=g_Receivebuffer .. strData
end

function PowerOffExtender()
    C4:SendToProxy(5001, "OFF","")
    g_URLPacket = url1 .. url2 ..  "Stop" 
    C4:urlGet(g_URLPacket) --send Sage a Stop command
    if Properties["Send Power Commands"] == "True" then
	   C4:NetConnect(6100,23)
	   local t = C4:SetTimer(2000, function(timer) --this is a way to pass for 2000 ms
		   C4:SendToNetwork(6100, 23, "root\nkillall miniclient\nexit\n")
	    end)
    end
 end
 
function PowerOnExtender()
    InitializeURL()
    if Properties["Send Power Commands"] == "True" then
	   C4:NetConnect(6100,23)
	   local t = C4:SetTimer(2000, function(timer) --this is a way to pass for 2000 ms
		   C4:SendToNetwork(6100, 23, "root\nkillall waitpower\nexit\n")
	    end)
    end
 end 

function ReceivedFromProxy(idBinding, strCommand, tParams)
  if Properties["Debug Mode"] == "True" then
   print("ReceivedFromProxy [" .. idBinding .. "] : " .. strCommand)
   if (tParams ~= nil) then
    for ParamName, ParamValue in pairs(tParams) do
     print(ParamName, ParamValue)
    end
   end
  end

  cmd = CMDS[strCommand]
  if (cmd ~= nill) then
    if Properties["Debug Mode"] == "True" then
      print(            "Send to Device: " .. cmd)
    end
    g_URLPacket = url1 .. url2 ..  cmd 
    if Properties["Debug Mode"] == "True" then
      print("URL is: " .. g_URLPacket)
    end 
    C4:urlGet(g_URLPacket)
  else
    print ("going to Command Interpreter with command: "..strCommand)
    CommandInterpreter(strCommand, tParams)
  end
end

function TestURL()
    g_URLPacket = url1..url2.."Home"
      if Properties["Debug Mode"] == "True" then
        print("Sending... g_URLPacket: " .. g_URLPacket)
      end

    C4:urlGet(g_URLPacket, {}, false, function(ticketId, strData, responseCode, tHeaders, strError)
             if (strError == nil) then
			 if string.find(strData,"UNAUTHORIZED") then
				print "C4:urlGet() failed as UNAUTHORIZED was returned - check username and password"
			 else
				if string.find(strData,"not connected") then
				    print("C4:urlGet() succeeded but the extender or clients is not connected " )
				else
				    print("C4:urlGet() succeeded! " )
				end
			 end
             else
                    print("C4:urlGet() failed: " .. strError)
             end
       end)
end

function OnPropertyChanged(strProperty)
    InitializeURL()
    print ("Property "..strProperty.." has changed to "..Properties[strProperty])
    print ("Updating URL info")
    print ("New URL is: "..url1..url2)
    TestURL()
end

function InitializeURL()
    ip 		 = C4:GetBindingAddress(6001)
    serverport  = Properties["Web Server Port"]
    mac		 = Properties["Extender MACID"]
    pass		 = Properties["Password"]
    user		 = Properties["User"]
    url1	  	 =  "http://" .. user .. ":" .. pass .."@"..ip ..":".. serverport
    url2      	 =  "/sage/SageCommand?context=" .. mac .."&command="
end

function OnDriverLateInit()
    InitializeURL()
end

function CommandInterpreter(strCommand, tParams)
    print (strCommand)
    if (strCommand == "ON") then
	   print ("Turning the SageTV ON")
	   --Put command here to turn on extender
	   PowerOnExtender()
    else
	   if (strCommand == "OFF") then
		  print ("Turning the SageTV OFF")
		  PowerOffExtender()
		  --Put command here to turn off extender
	   end
    end
end


--Function called for any actions executed by the user from the Actions Tab in Composer.
function EX_CMD.LUA_ACTION(tParams)
	if tParams ~= nil then
		for cmd,cmdv in pairs(tParams) do
			if cmd == "ACTION" then
				if (LUA_ACTION[cmdv] ~= nil) then
					LUA_ACTION[cmdv]()
				else
					--Dbg:Alert("Undefined Action")
					print("Undefined Action")  -- all print items were changed from Dbg:Alert as above
					print("Key: " .. cmd .. " Value: " .. cmdv)
				end
			else
				print("Undefined Command")
				print("Key: " .. cmd .. " Value: " .. cmdv)
			end
		end
	end
end

function LUA_ACTION.testit()  --Required for Actions screen
    print ("Action1 Testing Connection")
    TestURL()
end

function ExecuteCommand(strCmd, tParams)
  tParams = tParams or {}

  print("ExecuteCommand: " .. strCmd)
  for k,v in pairs(tParams) do dbg(k,v) end

  if (strCmd == "LUA_ACTION") then
    local action = tParams["ACTION"] or ""
    if (action == "SETUP_ACCOUNT") then
      -- set up account code goes here...
    end
  end

  if (strCmd == "Set Home Status") or (strCmd == "Set All Home Status") then
    local status = tParams["Status"] or ""
    if (status == "") then return end

    -- do Set Home Status stuff here...
  end
end
-- INIT --

 

Link to comment
Share on other sites

Looks to me like you're not seeing anything when ExecuteCommand executes, since there's no 'dbg' function, which is what is being called in there to print out the debug statements.

 

If you add this line:

dbg = print

You should at least see the ExecuteCommand being called.

Without seeing your XML, it's not possible to know what actions and commands you have defined, to see what you'd need to code in your ExecuteCommand function.

Personally, on such a simple driver, I would *just* parse things in the ExecuteCommand, not have that then call EX_CMD and LUA_ACTION.

RyanE

 

Link to comment
Share on other sites

ExecuteCommand only gets called for 2 things:

  • Actions the user triggers in Composer, on the Actions tab, which are defined in your XML.
  • Commands that are triggered by Composer programming, which are also defined in your XML.

If you have neither of those defined, your ExecuteCommand won't ever get fired.

RyanE

 

Link to comment
Share on other sites

ExecuteCommand(strCommand, tParams) takes both commands from the <commands> portion of the XML and the <actions> portion of the XML.

The only difference is that the strCommand portion will be "LUA_ACTION" and the tParams table will contain an "ACTION" key when you receive an action. A command will come directly into 'strCommand' and its parameters will reside in the table.

Examples:

Command:

<command>
<name>Synthesize Text</name>
<description>Synthesize text - PARAM1</description>
<params>
  <param>
  <name>Text</name>
  <type>STRING</type>
  <readonly>False</readonly>
  <default />
  </param>
</params>
</command>

-- The above would send you this
ExecuteCommand("Synthesize Text", {Text = "This is my text"})


Action:

<action>
  <name>Update Now</name>
  <command>UPDATE</command>
</action>

-- The above would send you this
ExecuteCommand("LUA_ACTION", {ACTION="UPDATE"})



This is what it looks like in my Clare driver, this is only worrying about <actions> and not <commands>.

<actions>
  <action>
    <name>Toggle DND</name>
    <command>TOGGLE_DND</command>
  </action>
  <action>
    <name>Refresh Announcements</name>
    <command>REFRESH_ANNOUNCEMENTS</command>
  </action>
  <action>
    <name>Configure Sub Stream</name>
    <command>CONFIGURE_SUB_STREAM</command>
  </action>
</actions>

    function class.ExecuteCommand(strCommand, tParams)
        Logger.Trace("Driver.ExecuteCommand[" .. strCommand .. "]")
        Logger.Debug(tParams)

        if (strCommand == "LUA_ACTION") then
            local action = tParams["ACTION"]

            if (action == "CONFIGURE_SUB_STREAM") then
                class.clare:configure()
            elseif (action == "REFRESH_ANNOUNCEMENTS") then
                class.RefreshAnnouncements()
            elseif (action == "TOGGLE_DND") then
                class.isDND:set(not class.isDND:get())
            end
        end
    end

 

Link to comment
Share on other sites

I must be a total idiot as I still can't get anything to fire on Actions.  Here is what I have for actions under the <config> section of my XML:

    <actions>
      <action>
        <name>Test Action</name>
        <command>TESTIT</command>
      </action>
    </actions>

And here is the Lua code (a slight variation on what was posted above):

function class.ExecuteCommand(strCmd, tParams)
        Logger.Trace("Driver.ExecuteCommand[" .. strCommand .. "]")
        Logger.Debug(tParams)

  if (strCmd == "LUA_ACTION") then
    local action = tParams["ACTION"]
    if (action == "TESTIT") then
      print ("Action1 Testing Connection!!!!!!!!!!!!!!")
	 TestURL()
    end
  end
end

So shouldn't it print Action1 Testing Connection!!!!!!!!!!!!!! to the window and run the TestURL function when I click on Action "Test Action"?  I do see the following in the driver_event.log file:  "2018-01-03 23:29:59 -0500 home-controller-hc250-000FFF163637 [2020] INFO: Executing command (LUA_ACTION) on driver SageTV(215)"  So the action is registering, it just doesn't appear to be triggering any commands to execute.

Link to comment
Share on other sites

what is class?

ExecuteCommand is the name of the function Director will call in a DriverWorks driver.

It shouldn't be declared in any class.  TheWizard must do some handling where he calls his class.ExecuteCommand when Director calls ExecuteCommand.

Remove the 'class.' in front of your ExecuteCommand declaration, and it should work.

Just to ensure your print statements will work, I'd also not use a Logger.Trace class to print.

Just change those to print('blah').

RyanE

 

Link to comment
Share on other sites

function ExecuteCommand(strCmd, tParams)
  print("ExecuteCommand")
  if (strCmd == "LUA_ACTION") then
    print("Executing an action")
    local action = tParams["ACTION"]
    if (action == "TESTIT") then
      print ("Action1 Testing Connection!!!!!!!!!!!!!!")
	  TestURL()
    end
  end
end

Try the above.

The "Logger" and "class" objects posted previously are part of some other stuff which are not relevant.

Logger is my own logging utility and Ryan assumed correctly that the function I posted was being called by the global "ExecuteCommand"

Link to comment
Share on other sites

So I finally got this working but I had to put the ExecuteCommand function at the top of the functions area of my Lua file.  All I did was cut it from the bottom and paste it in the top and it worked.  Maybe some other character or something was causing problems.

Link to comment
Share on other sites

5 hours ago, zaphod said:

So I finally got this working but I had to put the ExecuteCommand function at the top of the functions area of my Lua file.  All I did was cut it from the bottom and paste it in the top and it worked.  Maybe some other character or something was causing problems.

that means you have either a duplicate function or you had ending of functions wrong

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.