diff --git a/Layout/default/RaspberryDisplay/Index.php b/Layout/default/RaspberryDisplay/Index.php
new file mode 100644
index 000000000..d59ee43f2
--- /dev/null
+++ b/Layout/default/RaspberryDisplay/Index.php
@@ -0,0 +1,273 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
8322 Studenzen NOC Displays
+
+
+
+
+
+
+
+
+
+
+
+ {{ display.display_url | cleanupURL }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
|
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ display.display_label }}
+
+
+
+
+
+
+
diff --git a/Layout/default/menu.php b/Layout/default/menu.php
index 08026235e..1fd173f5c 100644
--- a/Layout/default/menu.php
+++ b/Layout/default/menu.php
@@ -26,6 +26,9 @@
"> Dashboard
diff --git a/application/RaspberryDisplay/RaspberryDisplay.php b/application/RaspberryDisplay/RaspberryDisplay.php
new file mode 100644
index 000000000..02acaa680
--- /dev/null
+++ b/application/RaspberryDisplay/RaspberryDisplay.php
@@ -0,0 +1,6 @@
+loadMe();
+ $this->me = $me;
+ $this->layout()->set("me", $me);
+ }
+
+ protected function restartRaspberryPi($id) {
+ $display = RaspberryDisplayModel::get($id);
+
+ $ssh = new SSH2($display->ip_address, $this->port);
+ $ssh->login($this->username, $this->password);
+ $ssh->exec('sudo reboot now');
+ return true;
+ }
+
+ protected function getDisplaysApi(): array
+ {
+ $displays = RaspberryDisplayModel::getAll();
+ $result = [];
+ foreach ($displays as $display) {
+ $result[] = [
+ "display_label" => $display->display_label,
+ "hostname" => $display->hostname,
+ "ip" => $display->ip_address,
+ "display_url" => $display->display_url,
+ "auto_refresh_enabled" => $display->auto_refresh_enabled === "1",
+ "margin_hot_fix_enabled" => $display->margin_hot_fix_enabled === "1",
+ "custom_style" => $display->custom_style,
+ "id" => $display->id,
+ ];
+ }
+ return $result;
+ }
+
+ protected function change()
+ {
+ $displayID = $this->request->displayID;
+ $field = $this->request->field;
+ $value = $this->request->value;
+ $value = $value === "true" ? 1 : ($value === "false" ? 0 : $value);
+ $display = RaspberryDisplayModel::get($displayID);
+ if ($display === null) {
+ return false;
+ }
+ $display->$field = $value;
+ $display->save();
+ return true;
+ }
+
+ protected function getConfig() {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $hostname = $this->request->hostname;
+
+ $displays = RaspberryDisplayModel::getByHostnameAndIp($hostname, $ip);
+
+ if ($displays === null) {
+ die("No display found for this hostname and ip:" . $hostname . " X " . $ip);
+ }
+
+ return array_map(function ($display) {
+ return [
+ "display_url" => $display->data->display_url,
+ "auto_refresh_enabled" => $display->data->auto_refresh_enabled === "1",
+ "margin_hot_fix_enabled" => $display->data->margin_hot_fix_enabled === "1",
+ "id" => $display->id,
+ ];
+ }
+ , $displays);
+ }
+ protected function apiAction() {
+ $do = $this->request->do;
+
+ if (!$this->me->is("employee") && !in_array($do, ["getDisplays", "change", "reboot"])) {
+ $this->redirect("dashboard");
+ }
+
+ $return = match ($do) {
+ "getDisplays" => $this->getDisplaysApi(),
+ "change" => $this->change(),
+ "reboot" => $this->restartRaspberryPi($this->request->displayID),
+ "getConfig" => $this->getConfig(),
+ default => false,
+ };
+
+ $data = [];
+
+ if ($return === true) {
+ $data = ["status" => "success"];
+ $this->returnJson($data);
+ }
+
+ if(!is_array($return) || !count($return)) {
+ $data = ["status" => "error"];
+ $this->returnJson($data);
+ }
+ $data['status'] = "OK";
+ $data['result'] = $return;
+ $this->returnJson($data);
+ }
+
+ protected function indexAction(): void
+ {
+ $this->layout()->setTemplate("RaspberryDisplay/Index");
+ }
+
+
+}
\ No newline at end of file
diff --git a/application/RaspberryDisplay/RaspberryDisplayModel.php b/application/RaspberryDisplay/RaspberryDisplayModel.php
new file mode 100644
index 000000000..2ec6a4ee9
--- /dev/null
+++ b/application/RaspberryDisplay/RaspberryDisplayModel.php
@@ -0,0 +1,103 @@
+ $value) {
+ if (property_exists(get_called_class(), $field)) {
+ $this->$field = $value;
+ }
+ }
+ }
+
+ public static function get($id)
+ {
+ $db = FronkDB::singleton();
+
+ $res = $db->select("RaspberryDisplay", "*", "id = $id");
+ if ($db->num_rows($res)) {
+ return new RaspberryDisplay($db->fetch_object($res));
+ }
+ return null;
+ }
+
+ public static function getByHostnameAndIp($hostname, $ip)
+ {
+ $db = FronkDB::singleton();
+
+ $res = $db->select("RaspberryDisplay", "*", "hostname = '$hostname' AND ip_address = '$ip'");
+ //fetch 2 rows
+
+ if ($db->num_rows($res)) {
+ while ($data = $db->fetch_object($res)) {
+ $items[] = new RaspberryDisplay($data);
+ }
+ return $items;
+ }
+
+ }
+
+ public static function create(array $data)
+ {
+ $model = new RaspberryDisplay();
+
+ foreach ($data as $field => $value) {
+ if (property_exists(get_called_class(), $field)) {
+ $model->$field = $value;
+ }
+ }
+
+ $me = new User();
+ $me->loadMe();
+
+ if ($model->create_by === null) {
+ $model->create_by = $me->id;
+ }
+ if ($model->edit_by === null) {
+ $model->edit_by = $me->id;
+ }
+
+ return $model;
+ }
+
+ public static function getAll()
+ {
+ $items = [];
+
+ $db = FronkDB::singleton();
+
+ $res = $db->select("RaspberryDisplay", "id, display_label, hostname, ip_address,custom_style, display_url, auto_refresh_enabled, margin_hot_fix_enabled");
+ if ($db->num_rows($res)) {
+ while ($data = $db->fetch_object($res)) {
+ $items[] = new RaspberryDisplay($data);
+ }
+ }
+ return $items;
+
+ }
+
+ public static function save(RaspberryDisplay $model)
+ {
+ $db = FronkDB::singleton();
+
+ $data = $model->data;
+
+ if ($model->id) {
+ $db->update("RaspberryDisplay", $data, "id=" . $model->id);
+ } else {
+ $model->create = date("U");
+ $model->edit = date("U");
+ $model->id = $db->insert("RaspberryDisplay", $data);
+ }
+
+ return $model;
+ }
+}
\ No newline at end of file
diff --git a/composer.json b/composer.json
index e4bb66219..3c82036fe 100644
--- a/composer.json
+++ b/composer.json
@@ -1,8 +1,9 @@
{
- "require": {
- "phpoffice/phpspreadsheet": "^1.23",
- "robmorgan/phinx": "^0.13.4",
- "textalk/websocket": "^1.6",
- "chillerlan/php-qrcode": "dev-main"
- }
+ "require": {
+ "phpoffice/phpspreadsheet": "^1.23",
+ "robmorgan/phinx": "^0.13.4",
+ "textalk/websocket": "^1.6",
+ "chillerlan/php-qrcode": "dev-main",
+ "phpseclib/phpseclib": "^3.0"
+ }
}
diff --git a/config/config.sample.php b/config/config.sample.php
index 6c68ba51b..ac1d60149 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -686,4 +686,8 @@ define("PDFTOTEXT_BIN_PATH", "/usr/bin/pdftotext");
define("TT_MBI_API_ENABLE", true); //Enable API Calls
define("TT_MBI_API_URL", "https://x.x.x.x/api/");
define("TT_MBI_API_VERSION", "v01");
-define("TT_MBI_API_KEY", "");
\ No newline at end of file
+define("TT_MBI_API_KEY", "");
+
+//Raspberry Display Configuration
+define("XINON_RASPBERRY_DISPLAY_SSH_USER", "");
+define("XINON_RASPBERRY_DISPLAY_SSH_PASS", "");
\ No newline at end of file
diff --git a/db/migrations/20240213083717_add_raspberry_display_table.php b/db/migrations/20240213083717_add_raspberry_display_table.php
new file mode 100644
index 000000000..f13890033
--- /dev/null
+++ b/db/migrations/20240213083717_add_raspberry_display_table.php
@@ -0,0 +1,40 @@
+getEnvironment() == "thetool") {
+ $table = $this->table('RaspberryDisplay');
+ $table->addColumn('display_label', 'string', ['limit' => 255])
+ ->addColumn('hostname', 'string', ['limit' => 255])
+ ->addColumn('ip_address', 'string', ['limit' => 15])
+ ->addColumn('display_url', 'string', ['limit' => 255])
+ ->addColumn('auto_refresh_enabled', 'boolean', ['default' => false])
+ ->addColumn('margin_hot_fix_enabled', 'boolean', ['default' => false])
+ ->addColumn('custom_style', 'string', ['limit' => 255, 'null' => true])
+ ->addColumn('created_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
+ ->addColumn('updated_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP'])
+ ->create();
+
+ }
+
+ if($this->getEnvironment() == "addressdb") {
+
+ }
+ }
+
+ public function down(): void
+ {
+ if($this->getEnvironment() == "thetool") {
+ $this->table('RaspberryDisplay')->drop();
+ }
+
+ if($this->getEnvironment() == "addressdb") {
+
+ }
+ }
+}