2017-11-04 11:53:58 -07:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyleft (C) 2017 http://sikofitt.com sikofitt@gmail.com
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Sikofitt\SamsungTV\Actions;
|
|
|
|
|
|
|
|
use React\EventLoop\StreamSelectLoop;
|
|
|
|
use React\Stream\WritableResourceStream;
|
|
|
|
use Sikofitt\SamsungTV\Packet\PacketFactory;
|
|
|
|
|
2017-11-21 18:31:03 -08:00
|
|
|
/**
|
|
|
|
* All available (supported (by me)) TV Keys.
|
|
|
|
*/
|
2017-11-04 11:53:58 -07:00
|
|
|
class Actions
|
|
|
|
{
|
|
|
|
public const KEY_ENTER = 'KEY_ENTER';
|
|
|
|
public const KEY_0 = 'KEY_0';
|
|
|
|
public const KEY_1 = 'KEY_1';
|
|
|
|
public const KEY_2 = 'KEY_2';
|
|
|
|
public const KEY_3 = 'KEY_3';
|
|
|
|
public const KEY_4 = 'KEY_4';
|
|
|
|
public const KEY_5 = 'KEY_5';
|
|
|
|
public const KEY_6 = 'KEY_6';
|
|
|
|
public const KEY_7 = 'KEY_7';
|
|
|
|
public const KEY_8 = 'KEY_8';
|
|
|
|
public const KEY_9 = 'KEY_9';
|
|
|
|
public const KEY_PLUS100 = 'KEY_PLUS100'; // - key
|
|
|
|
|
|
|
|
public const KEY_HDMI = 'KEY_HDMI'; // Apparently not
|
|
|
|
public const KEY_HDMI1 = 'KEY_HDMI1'; // the same
|
|
|
|
public const KEY_HDMI2 = 'KEY_HDMI2';
|
|
|
|
public const KEY_HDMI3 = 'KEY_HDMI3';
|
|
|
|
public const KEY_HDMI4 = 'KEY_HDMI4';
|
|
|
|
|
|
|
|
public const KEY_AV1 = 'KEY_AV1';
|
|
|
|
public const KEY_AV2 = 'KEY_AV2';
|
|
|
|
public const KEY_AV3 = 'KEY_AV3';
|
|
|
|
|
|
|
|
public const KEY_TV = 'KEY_TV';
|
2017-11-21 18:31:03 -08:00
|
|
|
public const KEY_POWEROFF = 'KEY_POWEROFF';
|
|
|
|
|
2017-11-04 11:53:58 -07:00
|
|
|
public static $keyMap = [
|
|
|
|
'9' => self::KEY_9,
|
|
|
|
'8' => self::KEY_8,
|
|
|
|
'7' => self::KEY_7,
|
|
|
|
'6' => self::KEY_6,
|
|
|
|
'5' => self::KEY_5,
|
|
|
|
'4' => self::KEY_4,
|
|
|
|
'3' => self::KEY_3,
|
|
|
|
'2' => self::KEY_2,
|
|
|
|
'1' => self::KEY_1,
|
|
|
|
'0' => self::KEY_0,
|
|
|
|
'-' => self::KEY_PLUS100,
|
|
|
|
'.' => self::KEY_PLUS100,
|
|
|
|
'E' => self::KEY_ENTER,
|
|
|
|
'H' => self::KEY_HDMI,
|
|
|
|
'H1' => self::KEY_HDMI1,
|
|
|
|
'H2' => self::KEY_HDMI2,
|
|
|
|
'H3' => self::KEY_HDMI3,
|
|
|
|
'H4' => self::KEY_HDMI4,
|
|
|
|
'T' => self::KEY_TV,
|
|
|
|
'AV1' => self::KEY_AV1,
|
|
|
|
'AV2' => self::KEY_AV2,
|
|
|
|
'AV3' => self::KEY_AV3,
|
2017-11-21 18:31:03 -08:00
|
|
|
'POWEROFF' => self::KEY_POWEROFF,
|
2017-11-04 11:53:58 -07:00
|
|
|
];
|
|
|
|
|
2017-11-21 18:31:03 -08:00
|
|
|
/**
|
|
|
|
* @var \Sikofitt\SamsungTV\Packet\PacketFactory
|
|
|
|
*/
|
2017-11-04 11:53:58 -07:00
|
|
|
private $packetFactory;
|
2017-11-21 18:31:03 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
2017-11-04 11:53:58 -07:00
|
|
|
private $address;
|
|
|
|
|
|
|
|
public function __construct(string $tvAddress, int $port = 55000)
|
|
|
|
{
|
|
|
|
$this->packetFactory = new PacketFactory();
|
|
|
|
$this->address = sprintf('%s:%d', $tvAddress, $port);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function transformChannels(string $channel): \Generator
|
|
|
|
{
|
|
|
|
$codes = str_split($channel);
|
|
|
|
|
|
|
|
foreach ($codes as $code) {
|
|
|
|
yield $this->transformChannel($code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function transformChannel(string $channel): string
|
|
|
|
{
|
|
|
|
if (array_key_exists(strtoupper($channel), self::$keyMap)) {
|
|
|
|
return base64_encode(self::$keyMap[strtoupper($channel)]);
|
|
|
|
}
|
2017-11-21 18:31:03 -08:00
|
|
|
@trigger_error(sprintf('Channel "%s" is not yet supported.', $channel));
|
2017-11-04 11:53:58 -07:00
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function sendKey(string $keyCode): void
|
|
|
|
{
|
|
|
|
$loop = new StreamSelectLoop();
|
|
|
|
|
2017-11-21 18:31:03 -08:00
|
|
|
// Required for old TVs. The computer presses the buttons too fast.
|
2017-11-04 11:53:58 -07:00
|
|
|
$loop->nextTick(function (): void {
|
|
|
|
usleep(140000);
|
|
|
|
});
|
|
|
|
|
|
|
|
$connection = stream_socket_client($this->address);
|
|
|
|
|
|
|
|
$stream = new WritableResourceStream($connection, $loop);
|
|
|
|
|
|
|
|
$stream->write($this->packetFactory->getStartPacket());
|
|
|
|
|
|
|
|
$key = $this->transformChannel($keyCode);
|
|
|
|
|
|
|
|
$stream->write($this->packetFactory->getKeyPacket($key));
|
2017-11-21 18:31:03 -08:00
|
|
|
|
2017-11-04 11:53:58 -07:00
|
|
|
$loop->run();
|
2017-11-21 18:31:03 -08:00
|
|
|
|
2017-11-04 11:53:58 -07:00
|
|
|
$stream->close();
|
|
|
|
}
|
|
|
|
|
2017-11-21 18:31:03 -08:00
|
|
|
/**
|
|
|
|
* @param string $channel
|
|
|
|
*/
|
2017-11-04 11:53:58 -07:00
|
|
|
public function changeChannel(string $channel): void
|
|
|
|
{
|
|
|
|
// Add enter at the end of the channel string.
|
|
|
|
$channelArr = str_split($channel.'E');
|
|
|
|
foreach ($channelArr as $item) {
|
|
|
|
$this->sendKey($item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-21 18:31:03 -08:00
|
|
|
public function powerOff(): void
|
|
|
|
{
|
|
|
|
$this->sendKey('POWEROFF');
|
|
|
|
}
|
|
|
|
|
2017-11-04 11:53:58 -07:00
|
|
|
/**
|
|
|
|
* @param string $input the source code in self::$keyMap
|
|
|
|
*/
|
|
|
|
public function changeInput(string $input): void
|
|
|
|
{
|
|
|
|
switch (strtoupper($input)) {
|
|
|
|
default:
|
|
|
|
case 'TV':
|
|
|
|
$this->sendKey('T');
|
|
|
|
break;
|
|
|
|
case 'HDMI':
|
|
|
|
$this->sendKey('H');
|
|
|
|
break;
|
|
|
|
case 'HDMI1':
|
|
|
|
$this->sendKey('H1');
|
|
|
|
break;
|
|
|
|
case 'HDMI2':
|
|
|
|
$this->sendKey('H2');
|
|
|
|
break;
|
|
|
|
case 'HDMI3':
|
|
|
|
$this->sendKey('H3');
|
|
|
|
break;
|
|
|
|
case 'HDMI4':
|
|
|
|
$this->sendKey('H4');
|
|
|
|
break;
|
|
|
|
case 'AV1':
|
|
|
|
$this->sendKey('AV1');
|
|
|
|
break;
|
|
|
|
case 'AV2':
|
|
|
|
$this->sendKey('AV2');
|
|
|
|
break;
|
|
|
|
case 'AV3':
|
|
|
|
$this->sendKey('AV3');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|