feat(#36): add Mail notification on Low Battery

This commit is contained in:
Benjamin Nussbaum 2025-06-18 00:10:29 +02:00
parent 1122764333
commit 2f8d989147
11 changed files with 375 additions and 0 deletions

View file

@ -0,0 +1,71 @@
<?php
namespace App\Notifications;
use App\Models\Device;
use App\Notifications\Channels\WebhookChannel;
use App\Notifications\Messages\WebhookMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class BatteryLow extends Notification
{
use Queueable;
private Device $device;
/**
* Create a new notification instance.
*/
public function __construct(Device $device)
{
$this->device = $device;
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail', WebhookChannel::class];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->markdown('mail.battery-low', ['device' => $this->device]);
}
public function toWebhook(object $notifiable)
{
return WebhookMessage::create()
->data([
'topic' => config('services.webhook.notifications.topic', 'battery.low'),
'message' => "Battery below {$this->device->battery_percent}% on device: {$this->device->name}",
'device_id' => $this->device->id,
'device_name' => $this->device->name,
'battery_percent' => $this->device->battery_percent,
])
->userAgent(config('app.name'))
->header('X-TrmnlByos-Event', 'battery.low');
}
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
'device_name' => $this->device->name,
'battery_percent' => $this->device->battery_percent,
];
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Notifications\Channels;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Response;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Arr;
class WebhookChannel
{
/** @var Client */
protected $client;
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Send the given notification.
*
* @param mixed $notifiable
*
* @throws Exception
* @throws GuzzleException
*/
public function send($notifiable, Notification $notification): ?Response
{
$url = $notifiable->routeNotificationFor('webhook', $notification);
if (! $url) {
return null;
}
if (! method_exists($notification, 'toWebhook')) {
throw new Exception('Notification does not implement toWebhook method.');
}
$webhookData = $notification->toWebhook($notifiable)->toArray();
$response = $this->client->post($url, [
'query' => Arr::get($webhookData, 'query'),
'body' => json_encode(Arr::get($webhookData, 'data')),
'verify' => Arr::get($webhookData, 'verify'),
'headers' => Arr::get($webhookData, 'headers'),
]);
if (! $response instanceof Response) {
throw new Exception('Webhook request did not return a valid GuzzleHttp\Psr7\Response.');
}
if ($response->getStatusCode() >= 300 || $response->getStatusCode() < 200) {
throw new Exception('Webhook request failed with status code: '.$response->getStatusCode());
}
return $response;
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace App\Notifications\Messages;
final class WebhookMessage
{
/**
* The GET parameters of the request.
*
* @var array|string|null
*/
private $query;
/**
* The POST data of the Webhook request.
*
* @var mixed
*/
private $data;
/**
* The headers to send with the request.
*
* @var array|null
*/
private $headers;
/**
* The Guzzle verify option.
*
* @var bool|string
*/
private $verify = false;
/**
* @param mixed $data
* @return static
*/
public static function create($data = '')
{
return new self($data);
}
/**
* @param mixed $data
*/
public function __construct($data = '')
{
$this->data = $data;
}
/**
* Set the Webhook parameters to be URL encoded.
*
* @param mixed $query
* @return $this
*/
public function query($query)
{
$this->query = $query;
return $this;
}
/**
* Set the Webhook data to be JSON encoded.
*
* @param mixed $data
* @return $this
*/
public function data($data)
{
$this->data = $data;
return $this;
}
/**
* Add a Webhook request custom header.
*
* @param string $name
* @param string $value
* @return $this
*/
public function header($name, $value)
{
$this->headers[$name] = $value;
return $this;
}
/**
* Set the Webhook request UserAgent.
*
* @param string $userAgent
* @return $this
*/
public function userAgent($userAgent)
{
$this->headers['User-Agent'] = $userAgent;
return $this;
}
/**
* Indicate that the request should be verified.
*
* @return $this
*/
public function verify($value = true)
{
$this->verify = $value;
return $this;
}
/**
* @return array
*/
public function toArray()
{
return [
'query' => $this->query,
'data' => $this->data,
'headers' => $this->headers,
'verify' => $this->verify,
];
}
}