Skip to content

Commit

Permalink
[viera-connector-homekit-connector-bridge] Prepared viera connector t…
Browse files Browse the repository at this point in the history
…o homekit connector bridge (#306)
  • Loading branch information
actions-user committed Sep 29, 2024
1 parent 6c5fc0d commit 2727abf
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 181 deletions.
30 changes: 26 additions & 4 deletions src/API/OpenApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

namespace FastyBird\Connector\Tuya\API;

use DateTimeInterface;
use FastyBird\Connector\Tuya;
use FastyBird\Connector\Tuya\Exceptions;
use FastyBird\Connector\Tuya\Helpers;
Expand Down Expand Up @@ -142,6 +143,8 @@ final class OpenApi
/** @var Promise\Deferred<bool>|null */
private Promise\Deferred|null $refreshTokenPromise = null;

private DateTimeInterface|null $refreshTokenFailed = null;

public function __construct(
private readonly string $identifier,
private readonly string $accessId,
Expand Down Expand Up @@ -188,6 +191,7 @@ public function connect(bool $async = true): Promise\PromiseInterface|bool
->then(function (Message\ResponseInterface $response) use ($deferred, $request): void {
try {
$this->tokenInfo = $this->parseGetAccessToken($request, $response)->getResult();
$this->refreshTokenFailed = null;

$deferred->resolve(true);
} catch (Throwable $ex) {
Expand All @@ -202,20 +206,32 @@ public function connect(bool $async = true): Promise\PromiseInterface|bool
}

$this->tokenInfo = $this->parseGetAccessToken($request, $result)->getResult();
$this->refreshTokenFailed = null;

return true;
}

public function disconnect(): void
{
$this->tokenInfo = null;
$this->refreshTokenFailed = null;
}

public function isConnected(): bool
{
return $this->tokenInfo !== null;
}

public function getRefreshFailed(): DateTimeInterface|null
{
return $this->refreshTokenFailed;
}

public function isRefreshFailed(): bool
{
return $this->refreshTokenFailed !== null;
}

/**
* @throws Exceptions\OpenApiCall
* @throws Exceptions\OpenApiError
Expand Down Expand Up @@ -1129,6 +1145,8 @@ private function callRequest(
try {
await($refreshTokenResult);
} catch (Throwable $ex) {
$this->refreshTokenFailed = $this->clock->getNow();

return Promise\reject(
new Exceptions\OpenApiCall(
'Awaiting for refresh token promise failed',
Expand All @@ -1141,6 +1159,10 @@ private function callRequest(
}
}

if ($refreshTokenResult === false) {
$this->connect(false);
}

$this->logger->debug(
sprintf(
'Request: method = %s url = %s',
Expand Down Expand Up @@ -1443,23 +1465,23 @@ private function getResponseBody(
}

/**
* @return Promise\PromiseInterface<bool>|false
* @return Promise\PromiseInterface<bool>|bool
*
* @throws Exceptions\OpenApiCall
* @throws Exceptions\OpenApiError
*/
private function refreshAccessToken(Request $request): Promise\PromiseInterface|false
private function refreshAccessToken(Request $request): Promise\PromiseInterface|bool
{
if (str_contains(strval($request->getUri()), self::GET_ACCESS_TOKEN_API_ENDPOINT)) {
return false;
return true;
}

if ($this->tokenInfo === null) {
return false;
}

if (!$this->tokenInfo->isExpired($this->clock->getNow())) {
return false;
return true;
}

if ($this->refreshTokenPromise !== null) {
Expand Down
113 changes: 73 additions & 40 deletions src/Clients/Cloud.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
use Throwable;
use TypeError;
use ValueError;
use function array_filter;
use function array_key_exists;
use function array_map;
use function assert;
use function in_array;
use function React\Async\async;

Expand All @@ -61,6 +63,8 @@ final class Cloud implements Client

private const HANDLER_PROCESSING_INTERVAL = 0.01;

public const REFRESH_SLEEP_DELAY = 300.0;

private const CMD_STATE = 'state';

private const CMD_HEARTBEAT = 'heartbeat';
Expand Down Expand Up @@ -172,36 +176,50 @@ public function connect(): void
->getCloudWsConnection($this->connector);

$wsClient->onMessage[] = function (API\Messages\Message $message): void {
if ($message instanceof API\Messages\Response\ReportDeviceOnline) {
$this->queue->append(
$this->messageBuilder->create(
Queue\Messages\StoreDeviceConnectionState::class,
[
'connector' => $this->connector->getId(),
'identifier' => $message->getIdentifier(),
'state' => $message->isOnline()
? DevicesTypes\ConnectionState::CONNECTED
: DevicesTypes\ConnectionState::DISCONNECTED,
],
),
);
} elseif ($message instanceof API\Messages\Response\ReportDeviceState) {
$this->queue->append(
$this->messageBuilder->create(
Queue\Messages\StoreChannelPropertyState::class,
[
'connector' => $this->connector->getId(),
'identifier' => $message->getIdentifier(),
'data_points' => array_map(
static fn (API\Messages\Response\DataPointState $dps): array => [
'code' => $dps->getCode(),
'value' => $dps->getValue(),
],
$message->getDataPoints(),
),
],
),
if (
$message instanceof API\Messages\Response\ReportDeviceOnline
|| $message instanceof API\Messages\Response\ReportDeviceState
) {
$knowDevices = array_filter(
$this->devices,
static fn (Documents\Devices\Device $device): bool => $device->getIdentifier() === $message->getIdentifier(),
);

if ($knowDevices === []) {
return;
}

if ($message instanceof API\Messages\Response\ReportDeviceOnline) {
$this->queue->append(
$this->messageBuilder->create(
Queue\Messages\StoreDeviceConnectionState::class,
[
'connector' => $this->connector->getId(),
'identifier' => $message->getIdentifier(),
'state' => $message->isOnline()
? DevicesTypes\ConnectionState::CONNECTED
: DevicesTypes\ConnectionState::DISCONNECTED,
],
),
);
} else {
$this->queue->append(
$this->messageBuilder->create(
Queue\Messages\StoreChannelPropertyState::class,
[
'connector' => $this->connector->getId(),
'identifier' => $message->getIdentifier(),
'data_points' => array_map(
static fn (API\Messages\Response\DataPointState $dps): array => [
'code' => $dps->getCode(),
'value' => $dps->getValue(),
],
$message->getDataPoints(),
),
],
),
);
}
}
};

Expand Down Expand Up @@ -307,6 +325,17 @@ private function handleCommunication(): void
$this->connectionManager->getCloudApiConnection($this->connector)->connect(false);
}

if ($this->connectionManager->getCloudApiConnection($this->connector)->isRefreshFailed()) {
$refreshFailedAt = $this->connectionManager->getCloudApiConnection($this->connector)->getRefreshFailed();
assert($refreshFailedAt instanceof DateTimeInterface);

if ($this->clock->getNow()->getTimestamp() - $refreshFailedAt->getTimestamp() < self::REFRESH_SLEEP_DELAY) {
return;
}

$this->connectionManager->getCloudApiConnection($this->connector)->connect(false);
}

foreach ($this->devices as $device) {
if (!in_array($device->getId()->toString(), $this->processedDevices, true)) {
$this->processedDevices[] = $device->getId()->toString();
Expand Down Expand Up @@ -411,17 +440,7 @@ private function readDeviceInformation(Documents\Devices\Device $device): bool
);
})
->catch(function (Throwable $ex) use ($device): void {
$this->logger->error(
'Could not call cloud openapi',
[
'source' => MetadataTypes\Sources\Connector::TUYA->value,
'type' => 'cloud-client',
'exception' => ApplicationHelpers\Logger::buildException($ex),
'connector' => [
'id' => $this->connector->getId()->toString(),
],
],
);
$renderException = true;

if ($ex instanceof Exceptions\OpenApiError) {
$this->queue->append(
Expand All @@ -445,6 +464,8 @@ private function readDeviceInformation(Documents\Devices\Device $device): bool
],
),
);

$renderException = false;
} else {
$this->dispatcher?->dispatch(
new DevicesEvents\TerminateConnector(
Expand All @@ -453,6 +474,18 @@ private function readDeviceInformation(Documents\Devices\Device $device): bool
),
);
}

$this->logger->error(
'Could not call cloud openapi',
[
'source' => MetadataTypes\Sources\Connector::TUYA->value,
'type' => 'cloud-client',
'exception' => ApplicationHelpers\Logger::buildException($ex, $renderException),
'connector' => [
'id' => $this->connector->getId()->toString(),
],
],
);
});

return true;
Expand Down
6 changes: 4 additions & 2 deletions src/Commands/Execute.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ protected function execute(Input\InputInterface $input, Output\OutputInterface $

$io = new Style\SymfonyStyle($input, $output);

$io->title((string) $this->translator->translate('//tuya-connector.cmd.execute.title'));
if ($input->getOption('quiet') === false) {
$io->title((string) $this->translator->translate('//tuya-connector.cmd.execute.title'));

$io->note((string) $this->translator->translate('//tuya-connector.cmd.execute.subtitle'));
$io->note((string) $this->translator->translate('//tuya-connector.cmd.execute.subtitle'));
}

if ($input->getOption('no-interaction') === false) {
$question = new Console\Question\ConfirmationQuestion(
Expand Down
8 changes: 7 additions & 1 deletion src/DI/TuyaExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,18 @@ public function loadConfiguration(): void
$builder->addFactoryDefinition($this->prefix('writers.event'))
->setImplement(Writers\EventFactory::class)
->getResultDefinition()
->setType(Writers\Event::class);
->setType(Writers\Event::class)
->setArguments([
'logger' => $logger,
]);

$builder->addFactoryDefinition($this->prefix('writers.exchange'))
->setImplement(Writers\ExchangeFactory::class)
->getResultDefinition()
->setType(Writers\Exchange::class)
->setArguments([
'logger' => $logger,
])
->addTag(ExchangeDI\ExchangeExtension::CONSUMER_STATE, false);

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Entities/Devices/Device.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ class Device extends DevicesEntities\Devices\Device

public const TYPE = 'tuya-connector';

public const STATE_READING_DELAY = 5_000.0;
public const STATE_READING_DELAY = 300.0;

public const HEARTBEAT_DELAY = 2_500.0;
public const HEARTBEAT_DELAY = 150.0;

private self|null $gateway = null;

Expand Down
Loading

0 comments on commit 2727abf

Please sign in to comment.