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”;
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.
I tried with the shutdown command to tell the bulb, that the transmission is over (shutdown($tcp, 1) which it also doesn’t aknowledged. But your code worked.
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.
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.
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:
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.
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…
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.