In order to call the webhooks either a browser can be used, another progam can call it or even a another PHP file can be used.
Start you webhook server program running in a terminal window on a machine that the website can communicate with (and open the port on your firewall if necessary).Browser
Enter the following in to a browser address bar on the Raspberry Pi or on another machine on the same network.
Remember to change the IP address to the one that is running your website.
http://192.168.2.29/webhooks.php?com=show&val=1
If it has worked the browser will show the following message:
Hello
ONLINE
OK
If you get an error message or no response, check you've typed the address correctly then ensure the server program is running and that the firewall is letting the message through.
Your server will show the following messages as it decodes the information received:
Waiting for connection
Client connected
Data received from client
Received: show,1,,,
Data at argument 0: show
Data at argument 1: 1
Data at argument 2:
Data at argument 3:
Data at argument 4:
Command received
1 Selected
Replying with: OK
Closing client connection
Waiting for connection
In order to use the 'get' command:
http://192.168.2.29/webhooks.php?com=get&key=value1
Again, if it has worked the browser will show the following message:
Hello
ONLINE
Value 1
Your server shows the following:
Waiting for connection
Client connected
Data received from client
Received: get,,value1,,
Data at argument 0: get
Data at argument 1:
Data at argument 2: value1
Data at argument 3:
Data at argument 4:
Get variable
Replying with: Value 1
Closing client connection
Waiting for connection
C
Here's a simple client program written in C to call a webhook on our server. It takes the IP address of the server as a parameter.
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
if(argc != 2)
{
printf("Usage: %s \n",argv[0]);
return 1;
}
memset(recvBuff, '0',sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Error creating socket\n");
return 1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8090);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("Error converting address\n");
return 1;
}
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("Error connecting\n");
return 1;
}
if (send(sockfd, "show,1,,,\0", 10, 0) == -1)
{
printf("Error sending\n");
return (1);
}
while ((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
{
recvBuff[n] = 0;
if(fputs(recvBuff, stdout) == EOF)
{
printf("Error receiving data\n");
}
}
if(n < 0)
{
printf("Read error \n");
}
return 0;
}
C#
To call a webhook from C# the following function can be used. Just change the string parameters to the values you want, separated by commas.
The IP address in this example is the local machine and the port is set in a text box.
This example is available in the download as the C# client program.
byte[] bytes = new byte[1024];
try
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[1];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, int.Parse(textBox2.Text));
Socket client = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
client.Connect(remoteEP);
textBox1.AppendText("Connected to: " + client.RemoteEndPoint.ToString() + Environment.NewLine);
byte[] msg = Encoding.ASCII.GetBytes("msg,Hello,,,PC");
int bytesSent = client.Send(msg);
int bytesRec = client.Receive(bytes);
textBox1.AppendText("Received: " + Encoding.ASCII.GetString(bytes, 0, bytesRec) + Environment.NewLine);
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (ArgumentNullException ane)
{
Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
}
catch (SocketException se)
{
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Unexpected exception : {0}", ex.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
PHP
I host a PHP file on my server on the internet and intercept the 'dev' parameter to determine which port (and therefore) device on my local network the request goes to. The IP address of my home network is written to 'local_ip_address.txt' once an hour via a script on my Raspberry Pi but you could use a dynamic DNS service.
My router is set up to port forward the message to the appropriate machine on my local network.
This is available in the download as webhooks_call.php.
<?php
function Send($port, $file, $command)
{
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
echo "Target port: ".$port."<BR>";
echo "Command to send: ".$command."<BR>";
$connection = @socket_connect($socket, $file, $port);
if( $connection )
{
echo "Connected<BR>";
$a = socket_write($socket, $command);
//echo $a."<BR>";
$read = socket_read( $socket, 1024 );
if( $read == false )
{
echo "Read failed<BR>";
}
else
{
echo "Target device returned: <BR>".$read;
echo "<BR>";
}
}
else
{
echo 'OFFLINE:' . socket_strerror(socket_last_error( $socket ));
}
}
echo "Hello<BR>";
$device=$_GET["dev"];
$command=$_GET["com"];
$value=$_GET["val"];
$key=$_GET["key"];
$subdev=$_GET["sub"];
if(isset($_GET['com']) && isset($_GET['dev']))
{
$file = file_get_contents('http://iotha.co.uk/local_ip_address.txt');
echo "IP Address: ".$file."<BR>";
$port=8080;
$all=false;
echo "Target device: ".$device."<BR>";
switch ($device)
{
case "RPI":
$port=8090;
break;
case "PC":
$port=8081;
break;
case "WS1":
$port=8082;
break;
case "WS2":
$port=8083;
break;
case "LT":
$port=8084;
break;
default:
$all=true;
}
$entityBody = file_get_contents('php://input');
$command = $command.",".$value.",".$key.",".$subdev.",".$device.",".$entityBody."\0";
if($all == true)
{
Send(8090, $file, $command);
Send(8081, $file, $command);
Send(8082, $file, $command);
Send(8083, $file, $command);
Send(8084, $file, $command);
}
else
{
if($device == "RPI")
{
Send(8090, $file, $command);
}
if($device == "PC")
{
Send(8081, $file, $command);
}
if($device == "WS1")
{
Send(8082, $file, $command);
}
if($device == "WS2")
{
Send(8083, $file, $command);
}
if($device == "LT")
{
Send(8084, $file, $command);
}
}
}
else
{
echo "FAIL: com and dev not set";
}
?>
Security
These examples do not take security into account as they run on a local network and assume the local port used is not accessible to the outside world. If you want to do this from the internet you could add a secure key as a parameter that is checked in the server program to verify the sender is trusted.