Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MQTT support #67

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"ext-ctype": "*",
"ext-iconv": "*",
"ext-json": "*",
"bluerhinos/phpmqtt": "dev-master",
"nelmio/cors-bundle": "^1.5",
"symfony/console": "^4.3",
"symfony/dotenv": "^4.3",
Expand Down
6 changes: 6 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@ services:
resource: '../src/Controller'
tags: ['controller.service_arguments']


App\EventListener\TransactionCreatedListener:
tags:
- { name: kernel.event_listener, event: transaction.created }


# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
13 changes: 13 additions & 0 deletions config/strichliste.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,16 @@ parameters:
- 200
- 500
- 1000

_mqtt:
enabled: false
host: localhost
clientId: strichliste
port: 1883
authentication: false
username: user
password: secret
flattenTransactionInfo: true
topics:
transactionValue: 'strichliste/transactionValue'
transactionInfo: 'strichliste/transactionInfo'
41 changes: 41 additions & 0 deletions src/Event/TransactionCreatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
/**
* Created by PhpStorm.
* User: flo
* Date: 01.03.19
* Time: 23:39
*/

namespace App\Event;


use App\Entity\Transaction;
use Symfony\Component\EventDispatcher\Event;

class TransactionCreatedEvent extends Event
{
public const NAME = 'transaction.created';

/**
* @var Transaction
*/
protected $transaction;

/**
* TransactionCreatedEvent constructor.
*
* @param \App\Entity\Transaction $transaction
*/
public function __construct(Transaction $transaction)
{
$this->transaction = $transaction;
}

/**
* @return Transaction
*/
public function getTransaction(): Transaction
{
return $this->transaction;
}
}
35 changes: 35 additions & 0 deletions src/EventListener/TransactionCreatedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Created by PhpStorm.
* User: flo
* Date: 01.03.19
* Time: 23:59
*/

namespace App\EventListener;


use App\Event\TransactionCreatedEvent;
use App\Service\MqttService;

class TransactionCreatedListener
{
/**
* @var MqttService
*/
private $mqttService;

public function __construct(MqttService $mqttService)
{
$this->mqttService = $mqttService;
}

public function onTransactionCreated(TransactionCreatedEvent $event)
{
if($this->mqttService->isEnabled()) {
$this->mqttService->notify(
$event->getTransaction()
);
}
}
}
183 changes: 183 additions & 0 deletions src/Service/MqttService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php
/**
* Created by PhpStorm.
* User: flo
* Date: 02.03.19
* Time: 00:06
*/

namespace App\Service;


use App\Entity\Transaction;
use App\Serializer\TransactionSerializer;
use Bluerhinos\phpMQTT;

class MqttService
{

/**
* @var SettingsService
*/
private $settingsService;

/**
* @var TransactionSerializer
*/
private $transactionSerializer;

/**
* @var bool
*/
private $enabled = false;

/**
* @var bool
*/
private $connected = false;

/**
* @var phpMQTT
*/
private $client = null;

public function __construct(
SettingsService $settingsService,
TransactionSerializer $transactionSerializer
) {
$this->settingsService = $settingsService;
$this->transactionSerializer = $transactionSerializer;

$this->enabled = $this->settingsService->getOrDefault('mqtt.enabled');
}

/**
* @param Transaction $transaction
*/
public function notify(Transaction $transaction)
{
if (!$this->enabled) {
return;
}

$this->connect();

$mqttClient = $this->getClient();

$transactionValueTopic = $this->settingsService->getOrDefault('mqtt.topics.transactionValue');
if (!empty($transactionValueTopic)) {
$mqttClient->publish(
$transactionValueTopic,
$transaction->getAmount()
);
}

$transactionInfoTopic = $this->settingsService->getOrDefault('mqtt.topics.transactionInfo');
if (!empty($transactionInfoTopic)) {
$mqttClient->publish(
$transactionInfoTopic,
$this->prepareTransactionInfo($transaction)
);
}

$this->disconnect();
}

/**
* @param \App\Entity\Transaction $transaction
*
* @return string
*/
private function prepareTransactionInfo(Transaction $transaction)
{
$info = $this->transactionSerializer->serialize($transaction);

if ($this->settingsService->getOrDefault('mqtt.flattenTransactionInfo')) {
$info = $this->flattenArray($info);
}

return json_encode($info);
}

/**
* @param $array
*
* @return array
*/
private function flattenArray($array)
{
$out = [];
foreach ($array as $key => $item) {
if (is_array($item)) {
foreach ($item as $subKey => $subItem) {
$out[$key . '_' . $subKey] = $subItem;
}
} else {
$out[$key] = $item;
}
}

return $out;
}

/**
* @return \Bluerhinos\phpMQTT
*/
public function getClient()
{
if (empty($this->client)) {
$mqttClient = new phpMQTT(
$this->settingsService->getOrDefault('mqtt.host'),
$this->settingsService->getOrDefault('mqtt.port'),
$this->settingsService->getOrDefault('mqtt.clientId')
);

$this->client = $mqttClient;
}
return $this->client;
}

public function connect()
{
if (!$this->connected) {
$mqttClient = $this->getClient();

$username = null;
$password = null;

if ($this->settingsService->getOrDefault('mqtt.authentication')) {
$username = $this->settingsService->getOrDefault('mqtt.username');
$password = $this->settingsService->getOrDefault('mqtt.password');
}

$connected = $mqttClient->connect(
true,
null,
$username,
$password
);

$this->connected = $connected;
}
return $this->connected;
}

public function disconnect()
{
if ($this->connected) {
$mqttClient = $this->getClient();
$mqttClient->disconnect();
$this->connected = false;
}

return $this->connected;
}

/**
* @return bool
*/
public function isEnabled(): bool
{
return $this->enabled;
}
}
15 changes: 13 additions & 2 deletions src/Service/SettingsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ function __construct(ContainerInterface $container) {
}

/**
* @param bool $includePrivateSettings
* @return array
*/
function getAll() {
return $this->settings;
function getAll(bool $includePrivateSettings = false) {
if ($includePrivateSettings) {
return $this->settings;
} else {
return array_filter($this->settings, function($key) {
return $key[0] !== '_';
}, ARRAY_FILTER_USE_KEY);
}
}

/**
Expand All @@ -46,6 +53,10 @@ function get(string $path) {

$settings = $this->settings;
foreach($parts as $part) {
if (!isset($settings[$part])) {
$part = '_' . $part;
}

if (!isset($settings[$part])) {
throw new ParameterNotFoundException($path);
}
Expand Down
19 changes: 16 additions & 3 deletions src/Service/TransactionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Entity\Article;
use App\Entity\Transaction;
use App\Entity\User;
use App\Event\TransactionCreatedEvent;
use App\Exception\AccountBalanceBoundaryException;
use App\Exception\ArticleInactiveException;
use App\Exception\ArticleNotFoundException;
Expand All @@ -15,6 +16,7 @@
use App\Exception\UserNotFoundException;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class TransactionService {

Expand All @@ -28,9 +30,15 @@ class TransactionService {
*/
private $settingsService;

function __construct(SettingsService $settingsService, EntityManagerInterface $entityManager) {
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;

function __construct(SettingsService $settingsService, EntityManagerInterface $entityManager, EventDispatcherInterface $eventDispatcher) {
$this->entityManager = $entityManager;
$this->settingsService = $settingsService;
$this->eventDispatcher = $eventDispatcher;
}


Expand Down Expand Up @@ -75,7 +83,7 @@ function doTransaction(int $userId, ?int $amount, string $comment = null, ?int $
throw new TransactionInvalidException('Amount can\'t be positive when sending money or buying an article');
}

return $this->entityManager->transactional(function () use ($userId, $amount, $comment, $quantity, $articleId, $recipientId) {
$e = $this->entityManager->transactional(function () use ($userId, $amount, $comment, $quantity, $articleId, $recipientId) {
$transaction = new Transaction();

$user = $this->entityManager->getRepository(User::class)->find($userId, LockMode::PESSIMISTIC_WRITE);
Expand Down Expand Up @@ -143,6 +151,11 @@ function doTransaction(int $userId, ?int $amount, string $comment = null, ?int $

return $transaction;
});

$transactionCreatedEvent = new TransactionCreatedEvent($e);
$this->eventDispatcher->dispatch('transaction.created', $transactionCreatedEvent);

return $e;
}

/**
Expand Down Expand Up @@ -247,4 +260,4 @@ private function checkAccountBalanceBoundary(User $user) {
throw new AccountBalanceBoundaryException($user, $balance, $lower);
}
}
}
}