Sharp aquos TCP/IP control with Client Socket

Hi members of the Symcon forum,

I am new hear and new to IP-Symcon and I trying to learn “everything” (reading a lot hear on the forum and Symcons documentation) but I am struggling a bit with the Google translations. And I have not found a script example with enough documentation that a new beginner like myself could figure out so I would appreciate some help.

I am trying to control a Sharp Aquos TV with TCP/IP control features.

For some reason the forum editor removes my padding blank spaces

The TV takes commands of 8 characters length e.g POWR?, POWR0, POWR1 etc. (commands are padded with trailing blank spaces) followed by a „\r“ (carrier return).

The string sent to the TV to see if it is powered on or off locks like this “POWR? \r”. If the TV is on it returns “1”, and if it is off it returns “0”.

The only other return messages are “OK” and “ERR’”.

When the e.g. power on command (“POWR1 \r”) is sent the TV replies with “OK” if command is successfully carried out, if not “ERR’” is replied.

The Socket connection needs to be binding or it will not be possible to determine which request is being answered.

So fare I have created a Client Socket in Symcon that connects to the TV and 2 scripts.

Script 1

<?
 echo "On";
 includeScript(20443 /*[Aquos]*/);   
  
function includeScript($scriptID) 
{ 
    $s = IPS_GetScript($scriptID); 
    include($s['ScriptFile']); 
}  

?>

Script 2

<?

CSCK_SendText(37475 /*[Client Socket]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345

?>

When I click on “On” (generated with echo „On“) in in the WebFront script 1 is run which calls script 2 that uses Client Socket (37475).

This works, the TV powers on but what do I do next? How do I take care of the return value?

Should I be using CSCK_SendText or something else? I was recommended to look at the RegisterVariable documentation but that made little sense to me (most likely do to my lack of knowledge regrding Symcon and php)

I am hoping somebody hear in the forum could give me some pointers, what to do next and where to look!

Thanks in advance.

yoggi

Welcome and have a good start with IP-Symcon.

First of all you could check if you get the response. For that double click the client socket in the object tree. In the windows on the right side you find an icon bug with the label Debug. If you click on Debug another windows should open, where you should see the response.

If you use


<? 

CSCK_SendText(37475 /*[Client Socket]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345 

?>

it should be possible to get the response with


<? 

$result = CSCK_SendText(37475 /*[Client Socket]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345 
var_dump($result);
?>

Hi Fonzo,

Thanks for taking the time!

Good tip regarding the debug feature.

<?  

CSCK_SendText(37475 /*[Client Socket]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345  

?>

With the debug feature I can see that "POWR1 " is sent out and that „OK“ is received so this is good news.

Your suggestion (with added echo) script prints out „bool(true)1“.

<?  

$result = CSCK_SendText(37475 /*[Client Socket]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345  
var_dump($result);

 echo $result; 

?>

Now I get something back (which is good as before I had nothing) but I would like the value („OK<CR>“), any ideas how to do that?

I will be reading and searching this forum but if anybody has any idea feal free to pitch in!

My end goole is a module so if there is module that uses TCP/IP in a similar way (to study, copy and learn from) please let me know!

yoggi

IP-Symcon is very asynchronous. Therefore the answer will be delivered only to the Register Variable into another script run.

paresy

You can write this data into a variable with a RegisterVariable.
I think this would be the first choice.

if you have more experience with IP-Symcon you can also use a PHP module. Therefore you would need ReceiveData. One example of many can be found here. I would recommend first to try it with a register variable it is for the start easier and its easier to get help or hints.

Hi again,

It took me a while to figure out how RegisterVariable Modul is added (Add Instance > Register Variable, under Misc Vendor).

So now I have a RegisterVariable that is listening to Client Socket that is connected to the TV and it works as I can see the return messages from the TV (e.g. I get strings back like „OK\r“, „0\r“, „1\r“, „ERR\r“ ).

But I am still quite lost. At the moment I use a separate script to send a command to the TV using Client Socket that the RegisterVariable is „connected“ to and I am using to send the command.

CSCK_SendText(37475 /*[Client Socket Aquos]*/, "POWR1   \r"); //Sends the text "Any record" on the client socket with ID 12345

I see that Register Variable has a RegVar_SendText and I am thinking that I should be using it instead of CSCK_SendText.

I am working with the script below that takes what comes in and puts it together (not sure if I need buffering as the strings are short) so I think I need to look for end of line (\r carriage return) in order to know if I received all that the TV sent. I can see that I can catch the carriage return as my if statement echo statement prints.

Can anybody please give me some leads on how to continue.

Thanks

yoggi

<?php

if ($_IPS['SENDER'] == "RegisterVariable") // if the script was called from a RegisterVariable instance
{
    $data  = RegVar_GetBuffer($_IPS['INSTANCE']); // copy data in the buffer of the RegisterVariable instance to $data
    $data .= $_IPS['VALUE']; // attach newly received data to $data
	
	if(strstr($data, "\r")) { // appears to catch carriage return <CR>
	    echo "Found end of line
";
	}else{
	    echo "Nothing found
";	
	}
	
    if (strlen($data) >= 9) // if $data is at least 9 characters long
    {
        $datasets = str_split($data, 9); // split $data into blocks of up to 9 characters
        $data = ""; // empty $data
        for ($i = 0; $i < count($datasets); $i++) // go through all records
        {
            if (strlen($datasets[$i]) == 9)
            {
                echo "received Record: ".$datasets[$i]."
";
            }
            else
            {
                $data = $datasets[$i]; // incomplete data in $data, last record does not have exactly 9 characters

            }
        }
    }

    RegVar_SetBuffer($_IPS['INSTANCE'], $data); // store the contents of $data in the buffer of the RegisterVariable instance

}
?>

If you check for the sender you can combine both parts in one skript and use this script also as an Action Script in IP-Symcon.

I would recommend to use IPS_LogMessage to see whats going on.

You want to send commands to the tv and on the other hand set the current state from the tv to a variable in IP-Symcon.
So first of all you have to create a „device“ tv. Go to the object tree and create an instance called „Dummy Module“.
Under the „Dummy Module“ you create variables for controlling the tv for example a variable Power. Give the variable a variable profile in case of Power ~Switch otherwise create a profile on you own.

You have to set the action from the variable to your action script. For better handling i would recommend to give your variable an ident.


<?

 $object_id = 43497 /*[Geräte\Sharp\Sharp TV\Power]*/; // Objekt ID from the variable
 IPS_SetIdent($object_id, "Power");

?>

Now you can search for this variable with the ident without knowing the exact id with IPS_GetObjectIDByIdent

So one skript could look like this


<?
//part for webfront
 if($_IPS['SENDER'] == "WebFront")
 {
 	$id_dummy = 16961 /*[Geräte\Sharp\Sharp TV]*/; // $object id of the parent, dummy module
	$id_power = IPS_GetObjectIDByIdent("Power", $id_dummy); // gets $object id of the power variable
	SetValue($_IPS['VARIABLE'], $_IPS['VALUE']);
	$state = $_IPS['VALUE'];
	if($state)
	{
		// switch on
		IPS_LogMessage("Sharp TV", "Power On Sharp TV");
		SetValue(52182 /*[Geräte\Sharp\Sharp TV\Last Command]*/, "Power On");
		// .. add command
		//CSCK_SendText(37475 /*[Client Socket Aquos]*/, "POWR1   \r");
	}
	else
	{
		// switch off
		IPS_LogMessage("Sharp TV", "Power Off Sharp TV");
		SetValue(52182 /*[Geräte\Sharp\Sharp TV\Last Command]*/, "Power Off");
		// add command ..
		
	}
 }
 
 
 // part for imcoming messages
 if($_IPS['SENDER'] == "RegisterVariable")
 {
 
 }

?>

In the RegisterVariable part you have to indentify which command belongs to wich variable and and the end set this value with SetValue.

First try this for example with Power and then add every command with a varibale you would like to use.

Hi Fonzo,

I appreciate you taking the time to help me, thanks!

Your last post help me in getting some more understanding but with that said I am still struggling. Could you please clarify a few things for me.

In your post you have two script, is the first one the action script or are the two scripts supposed to be one action script (both have start and end tags)?

If I have Symcon create the action script from within the Power variable under the dummy

SetValue($_IPS['VARIABLE'], $_IPS['VALUE']);

is automatically added to the action script but you have this line in script two (from my test this needs to be in the action script for the on off switch to work in the webfront).

Regarding the five numbered id that is given to objects I think that I have worked out how to translate yours to the one I am getting, all except 52182 in SetValue, where is it pointing, to what objekt?

Ones again thanks for trying to help me!

yoggi

The first script is only needed once for setting the ident of a variable. The bigger second script is the action script and also the script for your register variable.

As mentioned above the bigger second script is the action script for all your variables. You have to add code for every variable.

52182 in this case is only a string variable where you can write in the last command send from the webfront. This is optional and not needed only if you like to see what was send as last command.

Hi Fonzo,

I got your example working, thanks!

One question that I still have, is there an easy way to make the request/command binding (so that only one command can be sent out and that it will need to wait for a respons from the TV or a timeout before an other can be sent out)?

To me it appear that Register Variable is working asynchronous and except responses even if a command is not sent out (the TV dose not send anything unless a request is made but I am currently emulating the TV so I see from my testing that this appears to be the case).

Ones again thanks.

IP-Symcon has step learning curve (at least for me) but the more I read (and figure out) the more sure I get in that I made the right choice to go with it.

yoggi

You can use semaphore for that or additional a buffer or a variable where set blocked or free and check that with a while loop in php.

Yes you have to build this in your script or later a module, but therefore use semaphore and a buffer.

Hi all,

If the RegisterVariable can’t do blocking socket calls would it make sense to use phps stream_socket_client to connect to the TV from within Ip-Symcon?

I think I need to use blocking socket calls as the TV replies with „0“, „1“, „OK“ and „ERR“ when a command is called but with no information what command actually generated the reply.

I have placed the below php script within a normal Symcon script and it appears to work. Although I have no idea if this will cause problems further down the line nor do I have any idea yet on how to integrate it.

The TV keeps the connection open for 3 minutes so I would make sense to reuse an open connection if possible.

I welcome input so please let me know if this approach is bad or if there is a better way.

Thanks in advance.

yoggi

<?
$fp = stream_socket_client("tcp://192.168.1.102:6004", $errno, $errstr, 6);
if (!$fp) {
    echo "Unable to open
";
	IPS_LogMessage("StreamSocketTest", "Unable to open
");
} else {

    fwrite($fp, "POWR1   \r"); // Here I send the power on command
    stream_set_timeout($fp, 2);
    $res = fread($fp, 2000);

    $info = stream_get_meta_data($fp);
    fclose($fp);

    if ($info['timed_out']) {
        echo 'Connection timed out!';
		IPS_LogMessage("StreamSocketTest", "Connection timed out!");
    } else {
        echo $res;
		IPS_LogMessage("StreamSocketTest", "We got this back: " . $res); // Here $res holds the return confirmation from the power on command ("OK\r")
    }

}
?>

Hi,

I am looking for some high level advice and I hope there is a little help to find here.

I am using IPS Client Socket to connect to my TV over tcp/ip, I have a Dummy Module with boolean variables connected to an action script and On Off buttons in the WebFront. The return is handle by a splitter/cutter and a Register Variable.

My problem is that when I toggle, eg. the power switch in the webFront it sends the power on command to the TV and it turns on (replies OK if successful) and IPS sets the power variable that is connected to webfronts OnOff button to on/true but this is done without verifying if the TV actually is on.

Now I want to pull the status from the TV and write the status, but how do I make this work with IPS variables (that are connected to the webfront)?

I have no idea where to start, should I use the variables that the webfront is using or create mirror variables to use with some kind of pull function and then copy over the values to the variables the webfront is using?

Help would be appreciated.

Do you get an answer to the command from the TV? If this is the case you know the current status. Only if the answer was correct then the variable in the web front is set to state on.

I case the TV does send an response to a command you could check if the TV is reachable in the network with ping (Sys_Ping).
Just add a script with a cyclic timer that pings the TV in a defined intervall. If the ping was successfull the TV is on otherwise the TV is off. Depending on the known state you can set the variables in the webfront. But you have to check first if the TV really does not respond to a ping if the device is turned off to standby.


$state = Sys_Ping("192.168.1.10", 1000); //Wait max. 1 second
if($state)
{
SetValue(12345, true); // 12345 is the Variable in the Webfront
}
else
{
SetValue(12345, false);  // 12345 is the Variable in the Webfront
}

Hi Fonzo,

Eg. I send power on command („POWR1“) to the TV and it replies „OK“ if it received command successful and „ERR“ if unsuccessful.

To verify that the TV is on I’ll send the command „POWR?“ and the TV replies „1“ if it is on or „0“ if it is off.

I only want the webfront to show the real status of the TV by pulling the values, how do I combine this in the variable that is connected to webfront with action script (On Off button)?

Thanks

In this case you get a response you can set the variable value depended on the response.


// .. add your command and get the response in $state
if($state == "OK")
{
SetValue(12345, true); // Webfront Variable
}
elseif($state == "ERR")
{
SetValue(12345, false); // Webfront Variable
}
else
{
SetValue(12345, false); // Webfront Variable

}

Just create an extra script. In the script you send your command „POWR?“ and get the response in $state. Then set the value of the webfront variable dependend of the state you got. The Script is then bind to an cylic timer which starts the script every x seconds.


// .. add your command and get the response in $state
if($state == 1)
{
SetValue(12345, true); // Webfront Variable
}
elseif($state == 0)
{
SetValue(12345, false); // Webfront Variable
}

Hi Fonzo,

Thanks for helping!

I have been thinking along the same lines but my problem is were to put these things and how to get them working together.

Eg.

SetValue($_IPS['VARIABLE'], $_IPS['VALUE']); // WebFront set variable

in the „if ($_IPS[‚SENDER‘] == „WebFront“) {“ part sets the variable but the „OK“ arrives in the „if ($_IPS[‚SENDER‘] == „RegisterVariable“) {“ part and I don’t know how to combine these two with a if statement before the switch is flipped and the variable updated!

Trimmed version of my actions script


<?
 $id_dummy   = 49081 /*[Aquos TV\Dummy Module Aquos TV]*/;
 $id_sending = IPS_GetObjectIDByIdent("Sending", $id_dummy); // gets $object id of the sending variable/flag
 
 $id_power   = IPS_GetObjectIDByIdent("Power", $id_dummy); // gets $object id of the power variable

// Part for webfront
if ($_IPS['SENDER'] == "WebFront") {

	// is Client Socket connected and not waiting for return confirmation (102 Instance is active, >= 200 Instance is faulty)
	// are we sending, we can only send command if $id_sending is false
	if ( (IPS_GetInstance( 34862 /*[Client Socket (Aquos TV)]*/ ) ["InstanceStatus"] ) < 200 && !GetValueBoolean($id_sending))
    {
		SetValue($_IPS['VARIABLE'], $_IPS['VALUE']); // WebFront set variable
        
		$state = $_IPS['VALUE'];
                $sender = $_IPS['VARIABLE'];
		
		// 
		IPS_LogMessage("Action Script Aquos TV", "sender: " . $sender . " state: " . $state);
		
		// are we getting power on or power off from WebFront?
        if ($sender == $id_power) {
		
		// Set Sending to true to prevent sending more then one command at the time
		SetValueBoolean($id_sending, true);
		
			if ($state) { // 1 as in true
                // switch on TV
                RegVar_SendText(47048 /*[Client Socket (Aquos TV)\Register Variable Aquos TV]*/ , "POWR1   \r");
                IPS_LogMessage("Aquos Action Script", "Command sent to TV: " . "'POWR1   \\r', Power On");    
            } else {
                // switch off TV
                RegVar_SendText(47048 /*[Client Socket (Aquos TV)\Register Variable Aquos TV]*/ , "POWR0   \r");
                IPS_LogMessage("Aquos Action Script", "Command sent to TV: " . "'POWR0   \\r', Power Off");
            }
        }
    }
}

// Part for imcoming messages, RegisterVariable
if ($_IPS['SENDER'] == "RegisterVariable") {
    
    $data = RegVar_GetBuffer($_IPS['INSTANCE']); // copy data in the buffer of the RegisterVariable instance to $data 
    $data = $_IPS['VALUE']; // attach newly received data to $data
	
	receiveCommand($data); // will move receive to own function
    // IPS_LogMessage("Aquos Action Script", "Return from TV: " . $data);
	
	// Set Sending flag to false so a new command can be sent out (37830 /*[Aquos TV\Dummy Module Aquos TV\Sending]*/)
    SetValueBoolean($id_sending, false);
}

?>

In your case it makes no sense to set the variable directly from the webfront because you get a response.
So first of all delete


 SetValue($_IPS['VARIABLE'], $_IPS['VALUE']); // WebFront set variable 

in the if ($_IPS[‚SENDER‘] == „WebFront“) part.

So you are woundering how is the state then set? The state is allways set via the part if ($_IPS[‚SENDER‘] == „RegisterVariable“), because there you get the response. For example if you press a button in the webfront first of all the variable does not change. It changes it the moment of the response you get from your TV.

For a continous check of the TV you have to add a third part


// Part for check the state with cylic timer 
if ($_IPS['SENDER'] == "TimerEvent") { 
     
    // check state 
    RegVar_SendText(47048 /*[Client Socket (Aquos TV)\Register Variable Aquos TV]*/ , "POWR?   \r"); 
} 

Then add add cyclic timer that starts the script every x seconds. Now every time when the script is executed from the cyclic timer a command is send to the TV to ask for the current state. The response returns as allways in the if ($_IPS[‚SENDER‘] == „RegisterVariable“) part. In the part from the „RegisterVariable“ the value is set. So you have to check what kind of response you have and set the variable dependend of your response you got from the TV.

Hi again,

Yes this makes more sense to me now… but I have one question…

Should I use something along the lines of

SetValueBoolean(52847 /*[Aquos TV\Dummy Module Aquos TV\Power]*/, true);

as I can’t get

SetValue($_IPS['VARIABLE'], $_IPS['VALUE']); // WebFront set variable

to work (I tried to hard code $_IPS[‚VARIABLE‘], and $_IPS[‚VALUE‘] but without any luck).

Thanks for helping!

yoggi

This one works better because it is independent of the trigger.