Skip to content

Commit

Permalink
#36 retrieve weather based on location; updated namespace; removed co…
Browse files Browse the repository at this point in the history
…mmand for status update
  • Loading branch information
almostengr committed Oct 28, 2024
1 parent 11896b4 commit a3b81aa
Show file tree
Hide file tree
Showing 24 changed files with 195 additions and 355 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Open a terminal or command prompt window, and go to the plugin directory or othe
composer require almostengr/show-pulse-fpp
```

After installing the plugin, you should see "Light Show Pulse" commands on the "Run FPP Command"
dialog. If you do not see the commands, then restart FPPD or your show player.

## Setup

See the [User Guide](#) for how to configure the plugin.
Expand Down
91 changes: 54 additions & 37 deletions commands/BaseCommand.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace App\Commands;
namespace App;;

use Exception;

Expand All @@ -15,23 +15,24 @@ abstract class BaseCommand
protected const IMMEDIATE_RESTART = "IMMEDIATE RESTART";
protected const IMMEDIATE_SHUTDOWN = "IMMEDIATE SHUTDOWN";
protected const IMMEDIATE_STOP = "IMMEDIATE STOP";
protected const FPP_STATUS_IDLE_ID = 0;

public function __construct()
{
$this->configuration = $this->loadConfiguration();
$this->loadConfiguration();
}

/**
* @param ShowPulseSelectionResponseDto $selectionResponseDto
* @param mixed $fppStatus
*/
protected function postNextRequestedSelectionToFpp($selectionResponseDto, $fppStatus)
protected function postNextRequestedSelectionToFpp($data, $fppStatus)
{
if (is_null($selectionResponseDto)) {
if (is_null($data)) {
return false;
}

switch ($selectionResponseDto->getSequenceFilename()) {
switch ($data['playlist_name']) {
case self::IMMEDIATE_STOP:
$this->stopPlaylist();
$this->postStatusToWebsite($fppStatus, self::IMMEDIATE_STOP);
Expand Down Expand Up @@ -107,8 +108,7 @@ private function gracefulStopPlaylist()
for ($i = 0; $i < $maxLoops; $i++) {
$latestStatus = $this->getStatusFromFpp();

$fppStatusIdleId = 0;
if ($latestStatus->status === $fppStatusIdleId) {
if ($latestStatus->status === self::FPP_STATUS_IDLE_ID) {
break;
}

Expand All @@ -129,7 +129,13 @@ protected function webHttpRequest($route, $method = "GET", $data = null)
return $this->httpRequest($url, $method, $data, $this->configuration->getTokenAsHeader());
}

private function httpRequest($url, $method, $data, $headers = array())
protected function nwsHttpRequest($route)
{
$url = "https://api.weather.gov/" . $route;
return $this->httpRequest($url);
}

private function httpRequest($url, $method = "GET", $data = null, $headers = array())
{
array_push($headers, "Content-Type: application/json");
array_push($headers, "Accept: application/json");
Expand All @@ -149,24 +155,20 @@ private function httpRequest($url, $method, $data, $headers = array())

if ($response === false) {
$message = "cURL error: " . curl_error($ch);
$this->logError($message, true);
throw new Exception($message);
}

curl_close($ch);

return json_decode($response, true);
}

protected function logError($message, $throwException = false)
protected function logError($message)
{
$currentDateTime = date('Y-m-d h:i:s A');
error_log("$currentDateTime: $message");
echo $message;

if ($throwException) {
throw new Exception($message);
}

return false;
}

Expand All @@ -176,78 +178,93 @@ private function loadConfiguration()
$contents = file_get_contents($configFile);

if ($contents === false) {
$this->logError(
"Configuration file not found or unable to be loaded. Download configuration file contents from the Light Show Pulse website.",
true
);
throw new Exception("Configuration file not found or unable to be loaded. Download configuration file contents from the Light Show Pulse website.");
}

return new ShowPulseConfigurationResponse($contents);
$this->configuration = new ShowPulseConfigurationResponse($contents);
}

protected function getStatusFromFpp()
{
return $this->fppHttpRequest("fppd/status");
}

protected function postStatusToWebsite($fppStatus, $selectedSequence = null)
// protected function postStatusToWebsite($fppStatus, $selectedSequence = null, $latestWeather = null)
protected function postStatusToWebsite($statusRequestDto)
{
$statusDto = new ShowPulseStatusRequest($fppStatus, $this->configuration->getShowId(), $selectedSequence);

// $statusDto = new ShowPulseStatusRequestDto($fppStatus, $this->configuration->getShowId(), $selectedSequence);
return $this->webHttpRequest(
"api/show-statuses/add/" . $this->configuration->getShowId(),
"POST",
$statusDto,
$statusRequestDto, // $statusDto,
);
}

public function completed()
protected function completed()
{
echo "Done";
}

/**
* Summary of getNextRequestFromWebsite
* @var ShowPulseApiResponseDto $responseDto
* @var ShowPulseResponseDto $responseDto
* @return ShowPulseSelectionResponseDto|bool
*/
private function getNextRequestedSelectionFromWebsite()
{
$responseDto = $this->webHttpRequest(
"api/requested-selections/next/" . $this->configuration->getShowId()
"api/requested-selections/view-next/" . $this->configuration->getShowId()
);

return new ShowPulseSelectionResponseDto($responseDto);
return new ShowPulseResponseDto($responseDto);
}

protected function requestedSelectionGetNext($fppStatus)
protected function getNextRequestedSelection($fppStatus)
{
$selectionResponse = $this->getNextRequestedSelectionFromWebsite($this->configuration);
$selectionResponse = $this->getNextRequestedSelectionFromWebsite();

if ($selectionResponse === null) {
if ($selectionResponse->getData() === null) {
$selectionResponse = $this->getRandomSelection();
}

$this->postNextRequestedSelectionToFpp($selectionResponse, $fppStatus, $this->configuration);
$this->postNextRequestedSelectionToFpp($selectionResponse->getData(), $fppStatus, $this->configuration);

return $selectionResponse;
}

private function getRandomSelection()
{
$responseDto = $this->webHttpRequest(
"api/selection-options/random/" . $this->configuration->getShowId(),
'PUT'
"api/selection-options/view-random/" . $this->configuration->getShowId()
);

return new ShowPulseSelectionResponseDto($responseDto);
}

protected function rejectSelectionRequests()
protected function getShow()
{
$this->webHttpRequest(
"api/shows/reject-requests/" . $this->configuration->getShowId(),
'PUT'
$response = $this->webHttpRequest("api/shows/view/" . $this->configuration->getShowId());
return new ShowPulseResponseDto($response);
}

protected function updateShow($data)
{
$response = $this->webHttpRequest(
"api/shows/edit/" . $this->configuration->getShowId(),
'PUT',
$data
);
return new ShowPulseResponseDto($response);
}

protected function rejectSelectionRequests()
{
$response = $this->getShow();

$show = $response->getData();
$show['accepting_requests_id'] = 1;

$response = $this->updateShow($show);
return $response;
}
}
93 changes: 56 additions & 37 deletions commands/DaemonCommand.php
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
<?php

namespace App\Commands;
namespace App;;

use Exception;

require_once 'BaseCommand.php';

final class DaemonCommand extends BaseCommand implements ShowPulseCommandInterface
{
private const MAX_FAILURES_ALLOWED = 3;
private const MAX_FAILURES_ALLOWED = 5;
private const ONE_HOUR_IN_SECONDS = 3600;
private $lastWeatherUpdateTime;
private $latestWeather;
private $show;
private $lastSequence;
private $lastSecondsPlayed;
private $lastSong;

public function __construct()
{
parent::__construct();

$this->lastWeatherUpdateTime = time() - self::ONE_HOUR_IN_SECONDS;
}

public function execute()
{
$failureCount = 0;
$lastSequence = "";
$lastSong = "";
$lastSecondsPlayed = null;

if (file_exists(self::DAEMON_FILE)) {
$this->logError("Daemon is already running.", false);
$this->logError("Light Show Pulse Daemon is already running.");
return;
}

file_put_contents(self::DAEMON_FILE, "");
file_put_contents(self::DAEMON_FILE, "Running");
$daemonFileExists = true;

$failureCount = 0;
$this->show = $this->getShow();

do {
try {
Expand All @@ -31,27 +44,32 @@ public function execute()
}

$fppStatus = $this->getStatusFromFpp();
$hasStatusUpdate = $this->shouldPostStatus($fppStatus, $lastSequence, $lastSong);
$isPlayerFrozen = $this->checkPlayerFrozen($fppStatus, $lastSequence, $lastSecondsPlayed);
$hasStatusUpdate = $this->shouldPostStatus($fppStatus);
$isPlayerFrozen = $this->checkPlayerFrozen($fppStatus);

if ($hasStatusUpdate) {
$this->postStatusToWebsite($fppStatus);

$lastSequence = $fppStatus->current_sequence;
$lastSong = $fppStatus->current_song;
$lastSecondsPlayed = $fppStatus->seconds_played;

$this->requestedSelectionGetNext($fppStatus);
$this->getCurrentWeather();
// $this->postStatusToWebsite($fppStatus, null, $this->latestWeather);

$statusDto = new ShowPulseStatusRequestDto($fppStatus);
$statusDto->setWeatherObservation($this->latestWeather);
$this->postStatusToWebsite($statusDto);

$this->lastSequence = $fppStatus->current_sequence;
$this->lastSong = $fppStatus->current_song;
$this->lastSecondsPlayed = $fppStatus->seconds_played;

$this->getNextRequestedSelection($fppStatus);
} else if ($isPlayerFrozen) {
// $this->fppHttpRequest("apiv1/player-stuck", "POST");// todo future feature
$this->logError("Player appears to be frozen.", true);
throw new Exception("Player appears to be frozen.");
}

sleep(5);
$failureCount = 0;
} catch (Exception) {
} catch (Exception $exception) {
if ($failureCount < self::MAX_FAILURES_ALLOWED) {
$failureCount++;
$this->logError($exception->getMessage());
}

$delaySeconds = 2;
Expand All @@ -62,32 +80,33 @@ public function execute()
$daemonFileExists = file_exists(self::DAEMON_FILE);
} while ($daemonFileExists);

$this->logError("Daemon stopped.", false);
$this->completed();
}

private function shouldPostStatus($fppStatus, $lastSequence, $lastSong)
private function shouldPostStatus($fppStatus)
{
if (
return
!is_null($fppStatus) &&
$lastSequence === $fppStatus->current_sequence &&
$lastSong === $fppStatus->current_song
) {
return false;
}
$this->lastSequence === $fppStatus->current_sequence &&
$this->lastSong === $fppStatus->current_song;
}

return true;
private function checkPlayerFrozen($fppStatus)
{
return $this->lastSequence === $fppStatus->current_sequence && $this->lastSecondsPlayed === $fppStatus->seconds_played;
}

private function checkPlayerFrozen($fppStatus, $lastSequence, $lastSecondsPlayed)
private function getCurrentWeather()
{
if (
$lastSequence === $fppStatus->current_sequence &&
$lastSecondsPlayed === $fppStatus->seconds_played
) {
return true;
$timeDifference = time() - $this->lastWeatherUpdateTime;

if ($timeDifference < self::ONE_HOUR_IN_SECONDS || $this->show === null || $this->show->station_identifier === null) {
return;
}

return false;
$url = "/stations/" . $this->show->station_identifier . "/observations/latest";
$this->latestWeather = $this->nwsHttpRequest($url, "GET");
$this->lastWeatherUpdateTime = time();
}
}

Expand Down
12 changes: 9 additions & 3 deletions commands/DaemonStopCommand.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
<?php

namespace App\Commands;
namespace App;

use Exception;

final class DaemonStopCommand extends BaseCommand implements ShowPulseCommandInterface
{
public function execute()
{
unlink(self::DAEMON_FILE);
try {
unlink(self::DAEMON_FILE);
$this->completed();
} catch (Exception $exception) {
$this->logError($exception->getMessage());
}
}
}

$command = new DaemonStopCommand();
$command->execute();
$command->completed();
Loading

0 comments on commit a3b81aa

Please sign in to comment.