<?php
namespace SHServ\Controllers;
use SHServ\Models\Devices;
use SHServ\Tools\FirmwareCatalog;
class FirmwareRESTAPIController extends \SHServ\Middleware\Controller {
public function firmwares_list() {
$catalog = new FirmwareCatalog();
$all = $catalog->getAll();
$firmwares = array_values(array_map(function ($entry) {
return $entry['manifest'];
}, $all));
return $this->utils()->response_success([
'firmwares' => $firmwares,
'total' => count($firmwares),
]);
}
public function firmware_detail($firmware_id) {
$catalog = new FirmwareCatalog();
$entry = $catalog->getById($firmware_id);
if (!$entry) {
return $this->utils()->response_error('firmware_not_found');
}
return $this->utils()->response_success([
'firmware' => $entry['manifest'],
]);
}
public function firmware_download($firmware_id) {
$catalog = new FirmwareCatalog();
$binPath = $catalog->getBinPath($firmware_id);
if (!$binPath) {
return $this->utils()->response_error('firmware_not_found');
}
$entry = $catalog->getById($firmware_id);
$filename = $entry['manifest']['bin_filename'] ?? 'firmware.bin';
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . filesize($binPath));
readfile($binPath);
// cleanup
$tempDir = dirname($binPath);
if (strpos($tempDir, sys_get_temp_dir()) === 0) {
@unlink($binPath);
@rmdir($tempDir);
}
exit;
}
public function firmware_refresh() {
$catalog = new FirmwareCatalog();
$catalog->scan();
return $this->utils()->response_success();
}
public function device_firmware_compatibility($device_id) {
$device_id = intval($device_id);
if ($device_id < 1) {
return $this->utils()->response_error('invalid_id', ['device_id']);
}
$devices_model = new Devices();
$device = $devices_model->by_id($device_id);
if (!$device) {
return $this->utils()->response_error('device_not_found');
}
$about = $device->device_api()->get_about();
if (($about['http_code'] ?? 0) !== 200 || !is_array($about['data'] ?? null)) {
return $this->utils()->response_error('device_request_fail');
}
$catalog = new FirmwareCatalog();
$compatible = $catalog->findCompatible($about['data']);
return $this->utils()->response_success([
'compatible' => $compatible,
'current_version' => $about['data']['firmware_version'] ?? 'unknown',
'current_platform' => $about['data']['platform'] ?? null,
'current_channels' => $about['data']['channels'] ?? null,
]);
}
public function device_update_firmware($device_id, $firmware_id) {
$device_id = intval($device_id);
if ($device_id < 1) {
return $this->utils()->response_error('invalid_id', ['device_id']);
}
$devices_model = new Devices();
$device = $devices_model->by_id($device_id);
if (!$device) {
return $this->utils()->response_error('device_not_found');
}
$catalog = new FirmwareCatalog();
$binPath = $catalog->getBinPath($firmware_id);
if (!$binPath) {
return $this->utils()->response_error('firmware_not_found');
}
// Проверка совместимости
$about = $device->device_api()->get_about();
if (($about['http_code'] ?? 0) !== 200 || !is_array($about['data'] ?? null)) {
$this->cleanupBin($binPath);
return $this->utils()->response_error('device_request_fail');
}
$compatible = $catalog->findCompatible($about['data']);
$ids = array_column($compatible, 'id');
if (!in_array($firmware_id, $ids, true)) {
$this->cleanupBin($binPath);
return $this->utils()->response_error('firmware_not_compatible');
}
// Push OTA
$result = $device->device_api()->updateFirmware($binPath);
$this->cleanupBin($binPath);
if (($result['http_code'] ?? 0) !== 200) {
return $this->utils()->response_error('ota_failed', [], [
'device_msg' => $result['error'] ?? '',
]);
}
return $this->utils()->response_success([
'device_msg' => $result['raw'] ?? 'Update OK',
]);
}
// ------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------
private function cleanupBin(string $binPath): void {
$tempDir = dirname($binPath);
if (strpos($tempDir, sys_get_temp_dir()) === 0) {
@unlink($binPath);
@rmdir($tempDir);
}
}
}