Sending Commands with Perl, IO::Socket

Hello everybody,

I’m trying to control my Yeelight Wifi from FHEM. Because FHEM is written in Perl, the code needs to be in Perl, too.
I successfully have written a shell script which controls the Yeelight. But I’m not able to send the commands with Perl. I can connect the TCP socket, which I know, because when I try a wrong IP or port I get an error message. With the right IP und port the socket is createt successfully.
When I try to send an “on”-command, nothing happens. It’s the same command with the same sintax as in my shell script, but I got no reaction. The Perl-code waits afterwards for a response. When I set the light on with my phone, the script reads the “props”-answer, so everything seems to be fine with the socket. Just the commands don’t work.

I thought, maybe there is a problem with the enconding in Perl, so I tried to convert the message to utf8 and utf-8. Still no reaction.

If anyone can help me, I really would appreciate it.

Here is my Perl-code:


#!/usr/bin/perl -w
use strict;
use IO::Socket;

my $host = ‘192.168.xxx.xxx’;
my $port = 55443;

my $tcp = new IO::Socket::INET(
PeerHost => $host,
PeerPort => $port,
Proto => “tcp”) or die(“Error opening socket: $!.\n”);
print “Verbunden mit $host:$port\n” if ($tcp->connected());

send data to server

my $data = ‘{ “id”:1, “method”:“set_power”, “params”:[“on”,“smooth”,500]}\r\n’;
print “$data\n”;
$tcp->send($data) || die “Couldn’t send: $!”;

receive a response of up to 1024 characters from server

my $response = “”;
$tcp->recv($response, 1024);
print “received response: $response\n”;

$tcp->close();

Change this line to
my $data = qq({ “id”:1, “method”:“set_power”, “params”:[“on”,“smooth”,500]}\r\n);

In your code, “\r\n” is not escaped as expected because it’s in ‘’ instead of “”. When the bulb got this data, it didn’t see ‘0x0d0x0a’ and will wait for more bytes from TCP and hence no response.

That worked. Thanks for your help.

I tried with the shutdown command to tell the bulb, that the transmission is over (shutdown($tcp, 1):wink: which it also doesn’t aknowledged. But your code worked.

Thanks again.

Hello again,

the perl code I wrote for FHEM works really fine. I can control the bulb with most of the functions they have. But I wanted to add the possibility, to search for bulbs and identify them by id. I really tried a lot with the code, but the bulbs won’t answer my search message. I can receive the periodic state refresh. Here is my code. Can you help me again and tell me, what I’m doing wrong?

my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};

my $sock = IO::Socket::Multicast->new(Proto=>'udp',PeerAddr=>$dev, ReuseAddr=>1);

my $send = qq(M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1982\r\nMAN: "ssdp:discover"\r\nST: wifi_bulb\r\n);
$sock->mcast_send($send, $dev) or return "Couldn't send message: $!";
$sock->close();

I receive the search message with my listening socket, too. I tried everything. I put the message in double quotes and escaped the double quotes inside. I tried it without spaces. I also left out the last “\r\n”, because in the python script example from you, it’s also left out.

Thanks in advance.

From your code, I can’t see the destination port of your UDP multi-cast message. Did you send it to port 1982?
Do you know “tcpdump command? You can use it to monitor the traffic,simply type ### tcpdump -i any -X -s0 “port 1982” ### and check the packet going through you interface.
Besides, if your router is running Openwrt, then you need to change an option to disable some IGMP feature to receive the multiple cast response from your bulb.

Hello again,

yes, you were right. The port and IP where defined at the beginning of the code.

    use constant GROUP => '239.255.255.250';    #Multicast Group
    use constant MPORT => 1982;                    #Multicast Port
    $hash->{DeviceName}            = GROUP.':'.MPORT;

I set up a socket to listen on this port on a rpi with:

 #!/usr/bin/perl
 # client

 use strict;
 use IO::Socket::Multicast;

 use constant GROUP => '239.255.255.250';
 use constant PORT  => '1982';

 my $sock = IO::Socket::Multicast->new(Proto=>'udp',LocalPort=>PORT);
 $sock->mcast_add(GROUP) || die "Couldn't set group: $!\n";

 while (1) {
   my $data;
   next unless $sock->recv($data,1024);
   print $data;
 }

When I send the search request with the code from my second post I receive:

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1982
MAN: "ssdp:discover"
ST: wifi_bulb

But the bulbs won’t answer and I still don’t know why.

I also used tcpdump and got this:

15:05:54.292515 IP fhem.fritz.box.42558 > 239.255.255.250.1982: UDP, length 86
        0x0000:  4500 0072 5d2c 4000 0111 0776 c0a8 6436  E..r],@....v..d6
        0x0010:  efff fffa a63e 07be 005e 76f8 4d2d 5345  .....>...^v.M-SE
        0x0020:  4152 4348 202a 2048 5454 502f 312e 310d  ARCH.*.HTTP/1.1.
        0x0030:  0a48 4f53 543a 2032 3339 2e32 3535 2e32  .HOST:.239.255.2
        0x0040:  3535 2e32 3530 3a31 3938 320d 0a4d 414e  55.250:1982..MAN
        0x0050:  3a20 2273 7364 703a 6469 7363 6f76 6572  :."ssdp:discover
        0x0060:  220d 0a53 543a 2077 6966 695f 6275 6c62  "..ST:.wifi_bulb
        0x0070:  0d0a

I receive the multicast-messages from the bulbs, when they are power on and when they send the periodic messages. But I want them to answer the search request, which they don’t do.

I would appreciate some help.

If you are using openwrt, please refer to following topic:

Thanks for your reply, but I don’t think, that’s the problem.
I don’t have a router with OpenWrt. And even more important: with your python-script from the developer site (https://www.yeelight.com/download/developer/yeelight_demo_lan_ctrl_python.zip) it does work. So I think there is something “wrong” with my code…Maybe with the encoding or some “hidden” chars or…I don’t know…

Do you mind sending your entire code to me and I will take a look to see if I can find some clue?

I found the problem. I sent the search message from the same socket on which I listened to the multicast, which was a IO::Socket::Multicast object. But for sending the search message and receiving the answer a normal udp-socket is needed. So I opened a new socket (technically I close the multicast socket for the duration of the search and open the new one) and now it works.

Thanks for the help.

Here’s some of the best Perl tutorials & books. They might help you solve the issue.