diff --git a/app/Commands/Autowire.php b/app/Commands/Autowire.php new file mode 100644 index 0000000..8a102fe --- /dev/null +++ b/app/Commands/Autowire.php @@ -0,0 +1,90 @@ +info('Autowiring the application...'); + + $printers = $manager->getList()->map(fn(Printer $printer) => [ + 'name' => $printer->getName(), + 'status' => $printer->getStatus(), + 'attributes' => $printer->getAttributes(), + 'uri' => $printer->getUri(), + ]); + + try { + if ($this->option('init') && !file_exists($this->getYamlFilename())) { + $response = Http::acceptJson()->post('https://events.anikeen.com/api/printers/autoinit', [ + 'hostname' => gethostname(), + ]); + + if ($response->successful()) { + $this->setConfig($response->json()); + } else { + $this->warn('Unable to auto initialize configuration! Please use `print-cli init` to create a configuration file.'); + } + } + + $contig = $this->getConfig(expectedVersion: 2); + $response = Http::acceptJson()->patch(sprintf('%s/api/printers/autowire', $contig->getBaseUrl()), [ + 'id' => $contig->getId(), + 'hostname' => gethostname(), + 'printers' => $printers->toArray(), + ]); + + if ($response->successful()) { + $newPrinters = $response->json('printers', []); + if (empty($newPrinters)) { + $this->info('No new printers found to autowire.'); + } else { + foreach ($newPrinters as $printer) { + $this->info(sprintf('Printer: %s', $printer['uri'])); + } + } + $this->setConfig([ + ...$contig->toArray(), + 'printers' => $newPrinters, + ]); + } else { + throw new Exception('Failed to autowire printers: ' . $response->body()); + } + } catch (Throwable $exception) { + $this->error('Failed to autowire printers: ' . $exception->getMessage()); + return self::FAILURE; + } + + $this->info('Autowiring completed successfully.'); + + return self::SUCCESS; + } +} diff --git a/app/Commands/InitCommand.php b/app/Commands/Init.php similarity index 51% rename from app/Commands/InitCommand.php rename to app/Commands/Init.php index f91f65c..2a17890 100644 --- a/app/Commands/InitCommand.php +++ b/app/Commands/Init.php @@ -2,17 +2,18 @@ namespace App\Commands; +use App\Traits\HasConfig; use Illuminate\Support\Collection; use LaravelZero\Framework\Commands\Command; use Smalot\Cups\Builder\Builder; use Smalot\Cups\Manager\PrinterManager; -use Smalot\Cups\Model\Printer; use Smalot\Cups\Transport\Client; use Smalot\Cups\Transport\ResponseParser; -use Symfony\Component\Yaml\Yaml; -class InitCommand extends Command +class Init extends Command { + use HasConfig; + /** * The name and signature of the console command. * @@ -33,7 +34,6 @@ class InitCommand extends Command public function handle(): void { $username = getenv('USER') ?: get_current_user(); - $home = getenv('HOME'); $client = new Client(); $builder = new Builder(); @@ -48,8 +48,10 @@ class InitCommand extends Command return; } - $this->info('Please register printers first at:'); - $this->info('https://events.anikeen.com/console/resources/printers'); + $this->info('Please register the print-server first at:'); + $this->info('https://events.anikeen.com/console/resources/print-servers'); + + $id = $this->ask('What is your print-server ID?'); $password = $this->ask('What is your password?', match ($username) { 'print-cli' => 'print-cli', @@ -57,36 +59,14 @@ class InitCommand extends Command default => null, }); - $filename = $home . '/print-cli.yml'; - - $yaml = Yaml::dump( - input: [ - 'base_url' => 'https://events.anikeen.com', - 'printers' => $printers->map(function (Printer $printer) use ($username, $password) { - $attributes = $printer->getAttributes(); - - $id = $this->ask(sprintf('What is your ID for %s?', $printer->getName())); - - return [ - 'id' => $id, - 'name' => $printer->getName(), - 'driver' => 'cups', - 'address' => $attributes['printer-uri-supported'][0], - 'username' => $username, - 'password' => $password, - ]; - })->toArray(), + $this->setConfig([ + 'version' => 2, + 'id' => $id, + 'base_url' => 'https://events.anikeen.com', + 'default_credentials' => [ + 'username' => $username, + 'password' => $password, ], - inline: 100, - indent: 2, - flags: Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK - ); - - file_put_contents( - filename: $filename, - data: preg_replace('/-\n\s+/', '- ', $yaml) - ); - - $this->info(sprintf('Created configuration at %s', $filename)); + ]); } } diff --git a/app/Commands/InitSupervisorCommand.php b/app/Commands/InitSupervisor.php similarity index 85% rename from app/Commands/InitSupervisorCommand.php rename to app/Commands/InitSupervisor.php index 6e91a68..2dc9e60 100644 --- a/app/Commands/InitSupervisorCommand.php +++ b/app/Commands/InitSupervisor.php @@ -4,7 +4,7 @@ namespace App\Commands; use LaravelZero\Framework\Commands\Command; -class InitSupervisorCommand extends Command +class InitSupervisor extends Command { /** * The name and signature of the console command. @@ -34,8 +34,8 @@ directory = $home command = /usr/bin/php $home/.config/composer/vendor/bin/print-cli serve autostart = true autorestart = true -stderr_logfile = /var/log/print-cli.err.log -stdout_logfile = /var/log/print-cli.out.log +redirect_stderr = true +stdout_logfile = /var/log/print-cli.log stopwaitsecs = 3600 user = $username INI; diff --git a/app/Commands/InitWireguard.php b/app/Commands/InitWireguard.php new file mode 100644 index 0000000..3140070 --- /dev/null +++ b/app/Commands/InitWireguard.php @@ -0,0 +1,32 @@ +error('Log file not found. Please ensure the print server is running.'); + return; + } + $cmd = "tail -f $logFile"; + $descriptorspec = [ + 1 => ['pipe', 'w'], // stdout + 2 => ['pipe', 'w'], // stderr + ]; + $process = proc_open($cmd, $descriptorspec, $pipes); + + if (is_resource($process)) { + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + + while (true) { + $out = stream_get_contents($pipes[1]); + $err = stream_get_contents($pipes[2]); + + if ($out) $this->info(trim($out)); + if ($err) $this->error(trim($err)); + + usleep(200000); // 200ms delay to avoid CPU overuse + } + + // Never reached, but if you need to stop manually: + // fclose($pipes[1]); + // fclose($pipes[2]); + // proc_close($process); + } + } +} diff --git a/app/Commands/ListCommand.php b/app/Commands/Printers.php similarity index 50% rename from app/Commands/ListCommand.php rename to app/Commands/Printers.php index c188a4c..95aabe1 100644 --- a/app/Commands/ListCommand.php +++ b/app/Commands/Printers.php @@ -2,13 +2,10 @@ namespace App\Commands; +use App\Support\PrinterManager; use LaravelZero\Framework\Commands\Command; -use Smalot\Cups\Builder\Builder; -use Smalot\Cups\Manager\PrinterManager; -use Smalot\Cups\Transport\Client; -use Smalot\Cups\Transport\ResponseParser; -class ListCommand extends Command +class Printers extends Command { /** * The name and signature of the console command. @@ -26,20 +23,17 @@ class ListCommand extends Command /** * Execute the console command. - * - * @return int */ - public function handle(): int + public function handle(PrinterManager $printers): int { - $client = new Client(); - $builder = new Builder(); - $responseParser = new ResponseParser(); + $printers = $printers->getList(); - $printerManager = new PrinterManager($builder, $client, $responseParser); + if ($printers->isEmpty()) { + $this->error('We could not find any printers! Please register them first in CUPS.'); + return 1; + } - $printers = $printerManager->getList(); - - $this->info('Printers:'); + $this->info('Printer:'); foreach ($printers as $printer) { $this->info($printer->getName()); diff --git a/app/Commands/ServeCommand.php b/app/Commands/Serve.php similarity index 50% rename from app/Commands/ServeCommand.php rename to app/Commands/Serve.php index 6bbf0ea..eba42e6 100644 --- a/app/Commands/ServeCommand.php +++ b/app/Commands/Serve.php @@ -2,9 +2,10 @@ namespace App\Commands; +use App\Support\Config; +use App\Traits\HasConfig; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Client\RequestException; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; @@ -15,11 +16,12 @@ use Smalot\Cups\Manager\PrinterManager; use Smalot\Cups\Model\Job; use Smalot\Cups\Transport\Client; use Smalot\Cups\Transport\ResponseParser; -use Symfony\Component\Yaml\Yaml; use Throwable; -class ServeCommand extends Command +class Serve extends Command { + use HasConfig; + /** * The name and signature of the console command. * @@ -34,18 +36,80 @@ class ServeCommand extends Command */ protected $description = 'Command description'; + private $counter = 0; + /** * Execute the console command. */ public function handle(): void { - try { - $this->serve(); - } catch (Throwable $e) { - if ($e instanceof RequestException && $e->response->status() === 422) { - $this->error('Please check your configuration and try again.'); - $this->error($e->response->json()['message']); - } else { + $this->info('Starting service...'); + $this->monitor(); + } + + /** + * Monitor the service and handle jobs. + */ + private function monitor(): void + { + while (true) { + // autowire every 10 iterations + if ($this->counter % 10 === 0) { + try { + $this->call('autowire', [ + '--init' => $this->counter === 0, // Initialize only on the first run + ]); + } catch (Throwable $e) { + $this->error('Autowire failed: ' . $e->getMessage()); + } + } + + try { + $config = $this->getConfig(expectedVersion: 2); + $this->fetchJobs($config); + sleep(2); + } catch (ConnectionException $e) { + $this->error('Connection error: ' . $e->getMessage()); + sleep(2); + } catch (RequestException $e) { + $this->error('Request error: ' . $e->getMessage()); + sleep(2); + } catch (Throwable $e) { + $this->error('An unexpected error occurred: ' . $e->getMessage()); + sleep(2); + } + + $this->counter++; + } + } + + /** + * @throws RequestException + * @throws ConnectionException + */ + private function fetchJobs(Config $config): void + { + $printerIds = $config->getPrinters()->pluck('id')->toArray(); + $response = Http::acceptJson()->get(sprintf('%s/api/printers/jobs', $config->getBaseUrl()), [ + 'printer_ids' => $printerIds, + ]); + + if ($response->failed()) { + throw new RequestException($response); + } + + $jobs = $response->json(); + + if (empty($jobs)) { + return; + } + + $this->info('Printing jobs...'); + + foreach ($jobs as $job) { + try { + $this->handleJob($config, $job); + } catch (Throwable $e) { $this->error($e->getMessage()); } } @@ -55,80 +119,18 @@ class ServeCommand extends Command * @throws RequestException * @throws ConnectionException */ - private function serve(): void + private function handleJob(Config $config, array $job): void { - $this->info('Starting service...'); + $printer = $config->getPrinters()->firstWhere('id', $job['printer_id']); + [$username, $password] = $this->getConfig()->getPrinterCredentials($printer); - $yaml = $this->getConfiguration(); - $printerIds = array_map(fn($printer) => $printer['id'], $yaml['printers']); - - $response = Http::acceptJson()->patch(sprintf('%s/api/printers/register', $yaml['base_url']), [ - 'printers' => $yaml['printers'], - ]); - - $response->throw(); - - $this->info('Service started!'); - - do { - try { - $response = Http::acceptJson()->get(sprintf('%s/api/printers/jobs', $yaml['base_url']), [ - 'printer_ids' => $printerIds, - ]); - - if ($response->failed()) { - $this->error('Failed to fetch jobs, error: ' . $response->status()); - sleep(2); - continue; - } - - $jobs = $response->json(); - - if (empty($jobs)) { - sleep(2); - continue; - } - - $this->info('Printing jobs...'); - - foreach ($jobs as $job) { - try { - $this->handleJob($job, $yaml); - } catch (Throwable $e) { - $this->error($e->getMessage()); - } - } - - sleep(2); - } catch (Throwable $e) { - $this->error($e->getMessage()); - sleep(2); - } - - } while (true); - } - - private function getConfiguration(): array - { - $this->info('Reading configuration...'); - $yaml = file_get_contents(getcwd() . '/print-cli.yml'); - return Yaml::parse($yaml); - } - - /** - * @throws RequestException - * @throws ConnectionException - */ - private function handleJob(array $job, mixed $yaml): void - { - $printer = Collection::make($yaml['printers'])->firstWhere('id', $job['printer_id']); if (!empty($job['data']['preview'])) { $this->info(sprintf('Job %s is a preview', $job['id'])); - $this->markCompleted($job, $yaml, 0); + $this->markCompleted($config, $job, 0); return; } elseif (empty($job['file_url'])) { $this->info(sprintf('Job %s has no file', $job['id'])); - $this->markFailed($job, $yaml, 'No file provided'); + $this->markFailed($config, $job, 'No file provided'); return; } @@ -138,12 +140,12 @@ class ServeCommand extends Command $pointWidth = round($paperWidth * 2.83465, 2); $pointHeight = round($paperHeight * 2.83465, 2); - $client = new Client($printer['username'], $printer['password']); + $client = new Client($username, $password); $builder = new Builder(); $responseParser = new ResponseParser(); $printerManager = new PrinterManager($builder, $client, $responseParser); - $printer = $printerManager->findByUri($printer['address']); + $printer = $printerManager->findByUri($printer['uri']); $jobManager = new JobManager($builder, $client, $responseParser); $content = file_get_contents($job['file_url']); @@ -158,12 +160,12 @@ class ServeCommand extends Command $printerJob->addAttribute('fit-to-page', true); if (!$jobManager->send($printer, $printerJob)) { - $this->markFailed($job, $yaml, 'Failed to print job'); + $this->markFailed($config, $job, 'Failed to print job'); $this->error(sprintf('Failed to print job %s', $job['id'])); return; } - $this->markCompleted($job, $yaml, $printerJob->getId()); + $this->markCompleted($config, $job, $printerJob->getId()); $this->info(sprintf('Job %s completed as %s', $job['id'], $printerJob->getId())); } @@ -172,11 +174,12 @@ class ServeCommand extends Command * @throws RequestException * @throws ConnectionException */ - private function markCompleted(array $job, mixed $yaml, int $jobId): void + private function markCompleted(Config $config, array $job, int $jobId): void { - $response = Http::acceptJson()->patch(sprintf('%s/api/printers/jobs/%s/complete', $yaml['base_url'], $job['id']), [ - 'job_id' => $jobId, - ]); + $response = Http::acceptJson() + ->patch(sprintf('%s/api/printers/jobs/%s/complete', $config->getBaseUrl(), $job['id']), [ + 'job_id' => $jobId, + ]); $response->throw(); } @@ -184,11 +187,12 @@ class ServeCommand extends Command * @throws RequestException * @throws ConnectionException */ - private function markFailed(array $job, mixed $yaml, string $reason): void + private function markFailed(Config $config, array $job, string $reason): void { - $response = Http::acceptJson()->patch(sprintf('%s/api/printers/jobs/%s/fail', $yaml['base_url'], $job['id']), [ - 'reason' => $reason, - ]); - $response->throw(); + Http::acceptJson() + ->patch(sprintf('%s/api/printers/jobs/%s/fail', $config->getBaseUrl(), $job['id']), [ + 'reason' => $reason, + ]) + ->throw(); } } diff --git a/app/Printer.php b/app/Printer.php deleted file mode 100644 index 8a0f825..0000000 --- a/app/Printer.php +++ /dev/null @@ -1,8 +0,0 @@ -config[$key] ?? $default; + } + + public function set(string $key, mixed $value): void + { + $this->config[$key] = $value; + } + + public function all(): array + { + return $this->config; + } + + public function has(string $key): bool + { + return array_key_exists($key, $this->config); + } + + public function remove(string $key): void + { + unset($this->config[$key]); + } + + public function toArray(): array + { + return $this->config; + } + + public function getId(): string + { + return $this->get('id'); + } + + public function getBaseUrl(): string + { + return $this->get('base_url'); + } + + public function getPrinters(): Collection + { + return Collection::make($this->get('printers', [])); + } + + public function getPrinterCredentials(array $printer): array + { + $defaultCredentials = $this->get('default_credentials'); + + return [ + $printer['username'] ?? $defaultCredentials['username'] ?? null, + $printer['password'] ?? $defaultCredentials['password'] ?? null, + ]; + } +} diff --git a/app/Support/PrinterManager.php b/app/Support/PrinterManager.php new file mode 100644 index 0000000..09857db --- /dev/null +++ b/app/Support/PrinterManager.php @@ -0,0 +1,27 @@ +printerManager = new CupsPrinterManager($builder, $client, $responseParser); + } + + public function getList(): Collection + { + return Collection::make($this->printerManager->getList()); + } +} diff --git a/app/Traits/HasConfig.php b/app/Traits/HasConfig.php new file mode 100644 index 0000000..03ba4a3 --- /dev/null +++ b/app/Traits/HasConfig.php @@ -0,0 +1,78 @@ +getYamlFilename()); + } catch (Throwable) { + throw new RuntimeException(sprintf( + 'Unable to read configuration file %s', + $this->getYamlFilename(), + )); + } + + $config = Yaml::parse($yaml); + + if ($expectedVersion !== null) { + if (!isset($config['version'])) { + throw new RuntimeException('Configuration file does not have a version.'); + } + + if ($config['version'] !== $expectedVersion) { + throw new RuntimeException(sprintf( + 'Configuration file version %s does not match expected version %s.', + $config['version'], + $expectedVersion + )); + } + } + + if (!isset($config['base_url'])) { + throw new RuntimeException('Configuration file does not have a base_url.'); + } + + return new Config($config); + } + + protected function setConfig(array $config): void + { + $yaml = Yaml::dump( + input: $config, + inline: 100, + indent: 2, + flags: Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK + ); + + file_put_contents( + filename: $this->getYamlFilename(), + data: preg_replace('/-\n\s+/', '- ', $yaml) + ); + } +} diff --git a/bin/install b/bin/install new file mode 100644 index 0000000..a433ca7 --- /dev/null +++ b/bin/install @@ -0,0 +1,40 @@ +#!/bin/sh + +# Prevent running as root +if [ "$EUID" -eq 0 ]; then + echo "This script must NOT be run as root or with sudo. Please run as a normal user." + exit 1 +fi + +# Stage 1: Install dependencies +sudo apt-get update +sudo apt-get upgrade -y +sudo add-apt-repository -y ppa:ondrej/php +sudo apt-get install -y git cups zip unzip supervisor \ + php-cli php-zip php-curl php-xml php-mbstring \ + printer-driver-gutenprint + +# Stage 2: Install Composer +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +php composer-setup.php +php -r "unlink('composer-setup.php');" +sudo mv composer.phar /usr/local/bin/composer + +# Stage 3: Setup Composer global vendor bin directory +COMPOSER_BIN_DIR='export PATH="$PATH:$HOME/.config/composer/vendor/bin"' +grep -qxF "$COMPOSER_BIN_DIR" ~/.bashrc || echo "$COMPOSER_BIN_DIR" >> ~/.bashrc + +# Stage 4: Configure CUPS server +sudo cupsctl --remote-admin --remote-any +sudo usermod -aG lpadmin "$USER" +sudo /etc/init.d/cups restart + +# Stage 5: Install Print CLI application +composer global require anikeen/print-cli +print-cli init --no-interaction +print-cli init:supervisor | sudo tee /etc/supervisor/conf.d/print-cli.conf > /dev/null + +# Stage 6: Start Supervisor service +sudo supervisorctl reread +sudo supervisorctl update +sudo supervisorctl restart print-cli diff --git a/composer.json b/composer.json index 2f2474c..6334ab1 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "illuminate/http": "^11.5", "laravel-zero/framework": "^11.0.0", "ghostzero/cups-ipp": "^1.0", - "symfony/yaml": "^7.1" + "symfony/yaml": "^7.1", + "ext-posix": "*" }, "require-dev": { "laravel/pint": "^1.15.2", diff --git a/docs/orangepi5plus.md b/docs/orangepi5plus.md new file mode 100644 index 0000000..0bc4450 --- /dev/null +++ b/docs/orangepi5plus.md @@ -0,0 +1,76 @@ +# Installation + +Installing Print-CLI on a Orange Pi 5 Plus is a straightforward process but requires some time to install the necessary +packages. This guide will help you to install Print-CLI on a Orange Pi 5 Plus. + +Estimated time: 30-60 minutes + +## Step 1: Install the Orange Pi OS + +Before we start, make sure you have created a bootable SD card with the **Orange Pi 1.2.0 Jammy** image. The +simplest way is to use the Balena Etcher which helps you flash the Orange Pi image onto your SD card. + +Recommended configuration: + +- **OS**: Orange Pi 1.2.0 Jammy with Linux 5.10.160-rockchip-rk3588 +- **Username**: orangepi + +## Step 2: Install Required Packages + +**Tested Printers:** + +- EPSON ET-2750 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) +- EPSON ET-2860 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) + +With the Orange Pi OS installed, we need to install the required packages for Print-CLI to work. Run the following +command and grab a coffee while the packages are being installed (it may take a while): + +> **Note:** It may ask you for your password during the installation process. + +```bash +curl -sfL https://print-cli.b-cdn.net/install | bash - +``` + +## Step 3: Configuration + +After the installation is complete, you can configure your printer and other settings. Just run the following command: + +```bash +print-cli init +``` + +# Troubleshooting + +## WLAN + +https://www.makeuseof.com/connect-to-wifi-with-nmcli/ + +# Printer Offsets + +### EPSON ET-2750 Series (with Gutenprint @ OrangePI) + +> Some other OS and drivers need other offsets. + +```json +{ + "badge": { + "offset": { + "y": 0, + "x": 0 + } + } +} +``` + +### EPSON ET-2860 Series (with Gutenprint @ OrangePI) + +```json +{ + "badge": { + "offset": { + "y": -80, + "x": 0 + } + } +} +``` diff --git a/docs/raspberry.md b/docs/raspberry.md new file mode 100644 index 0000000..b04a471 --- /dev/null +++ b/docs/raspberry.md @@ -0,0 +1,46 @@ +# Installation + +Installing Print-CLI on a Raspberry Pi is a quite simple process but requires some time installing the required +packages. This guide will help you to install Print-CLI on a Raspberry Pi. + +Estimated time: 30-60 minutes + +## Step 1: Install the Raspberry Pi OS + +Before we start, make sure you have created a bootable SD card with the **Ubuntu Server 22.04 LTS 64-bit** image. The +simplest way is to use the Raspberry Pi Imager which enables you to select an Ubuntu image when flashing your SD card. + +Recommended configuration: + +- **OS**: Ubuntu Server 22.04 LTS 64-bit +- **Username**: print-cli + +## Step 2: Install Required Packages + +**Tested Printers:** + +- EPSON ET-2750 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) +- EPSON ET-2860 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) + +With the Raspberry Pi OS installed, we need to install the required packages for Print-CLI to work. Run the following +command and grab a coffee while the packages are being installed (it may take a while): + +> **Note:** It may ask you for your password during the installation process. + +```bash +curl -sfL https://print-cli.b-cdn.net/install | bash - +``` + +## Step 3: Configuration + +After the installation is complete, you can configure your printer and other settings. Just run the following command: + +```bash +print-cli init +``` + +# Troubleshooting + +## WLAN + +https://www.makeuseof.com/connect-to-wifi-with-nmcli/ diff --git a/orangepi5plus.md b/orangepi5plus.md deleted file mode 100644 index 5ea817c..0000000 --- a/orangepi5plus.md +++ /dev/null @@ -1,179 +0,0 @@ -# Installation - -Installing Print-CLI on a Raspberry Pi is a quite simple process but requires some time installing the required -packages. This guide will help you to install Print-CLI on a Raspberry Pi. - -Estimated time: 30-60 minutes - -## Step 1: Install the Raspberry Pi OS - -Before we start, make sure you have created a bootable SD card with the **Ubuntu Server 22.04 LTS 64-bit** image. The -simplest way is to use the Raspberry Pi Imager which enables you to select an Ubuntu image when flashing your SD card. - -Recommended configuration: - -- **OS**: Orange Pi 1.2.0 Jammy with Linux 5.10.160-rockchip-rk3588 -- **Username**: orangepi - -## Step 2: Install Required Packages - -**Tested Printers:** - -- EPSON ET-2750 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) -- EPSON ET-2860 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) - -Next, we need to install the required packages for Print-CLI to work. Run the following commands and grab a coffee while -the packages are being installed (it may take a while): - -```bash -sudo apt-get update -sudo add-apt-repository ppa:ondrej/php -sudo apt-get install -y git cups zip unzip supervisor \ - php-zip php-curl php-xml php-mbstring \ - printer-driver-gutenprint -``` - -Install Composer as usual: - -``` -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" -sudo mv composer.phar /usr/local/bin/composer -``` - -Finally, add the Composer bin directory to your PATH, so you can run the `print-cli` command from anywhere: - -```bash -echo 'export PATH="$PATH:$HOME/.config/composer/vendor/bin"' >> ~/.bashrc -``` - -## Step 3: Ensure CUPS is Running - -Now we need to ensure that the CUPS service is running. CUPS is the printing system used by Print-CLI to send print jobs -to the printer. This is the most important step, so make sure you follow it carefully, otherwise, Print-CLI won't work -as expected, and you won't be able to print anything. - -```bash -sudo cupsctl --remote-admin --remote-any -sudo usermod -aG lpadmin orangepi -sudo /etc/init.d/cups restart -``` - -Make sure the CUPS service is running by visiting the following URL in your browser: - -```text -https://10.20.0.195:631/printers/ -``` - -You should see a page with a list of printers. If you don't see any printers, you may need to add one manually. - -Ensure that you can print a test page by clicking on the printer name and selecting "Print Test Page". - -### Find the Printer Address - -To find the printer address, visit your Printers page in CUPS, and click on the printer name. The address in the browser -should look like this: - -```text -https://10.20.0.195:631/printers/EPSON_ET_2720_Series -``` - -It contains the Name of the printer, which is the `printer-name` part. You can use this address in the configuration -file for Print-CLI. In most cases, just replace the `https` with `ipp` and replace the ip to `127.0.0.1`: - -```text -ipp://127.0.0.1:631/printers/EPSON_ET_2720_Series -``` - -## Step 4: Install Print-CLI - -So far, we have installed all the required packages and ensured that CUPS is running. Now we can install Print-CLI using -Composer, the PHP package manager. Run the following command to install Print-CLI globally: - -```bash -composer global require anikeen/print-cli -``` - -You can also use the same command to update Print-CLI to the latest version: - -```bash -composer global require anikeen/print-cli -``` - -## Step 5: Configure Print-CLI - -With the latest version, you can automatically create your `/home/orangepi/print-cli.yml` with: - -```bash -print-cli init -``` - -To test the configuration, run the following command: - -```bash -print-cli serve -``` - -If everything is configured correctly, you should see the following output: - -```text -Starting service... -Reading configuration... -Service started! -``` - -You can exit the service by pressing `Ctrl+C`. - -## Step 6: Supervisor Configuration - -To run Print-CLI as a service, we can use Supervisor. Supervisor is a process control system that allows you to monitor -and control a number of processes on UNIX-like operating systems. - -Create a new configuration file `/etc/supervisor/conf.d/print-cli.conf`: - -```bash -print-cli init:supervisor | sudo tee /etc/supervisor/conf.d/print-cli.conf > /dev/null -``` - -Now, update Supervisor to read the new configuration file and start the Print-CLI service: - -```bash -sudo supervisorctl reread -sudo supervisorctl update -sudo supervisorctl start print-cli -``` - -## WLAN - -https://www.makeuseof.com/connect-to-wifi-with-nmcli/ - -## Printer Offsets - -### EPSON ET-2750 Series (with Gutenprint @ OrangePI) - -> Some other OS and drivers need other offsets. - -```json -{ - "badge": { - "offset": { - "y": 0, - "x": 0 - } - } -} -``` - -### EPSON ET-2860 Series (with Gutenprint @ OrangePI) - -```json -{ - "badge": { - "offset": { - "y": -80, - "x": 0 - } - } -} -``` diff --git a/raspberry.md b/raspberry.md deleted file mode 100644 index 5660dc6..0000000 --- a/raspberry.md +++ /dev/null @@ -1,152 +0,0 @@ -# Installation - -Installing Print-CLI on a Raspberry Pi is a quite simple process but requires some time installing the required -packages. This guide will help you to install Print-CLI on a Raspberry Pi. - -Estimated time: 30-60 minutes - -## Step 1: Install the Raspberry Pi OS - -Before we start, make sure you have created a bootable SD card with the **Ubuntu Server 22.04 LTS 64-bit** image. The -simplest way is to use the Raspberry Pi Imager which enables you to select an Ubuntu image when flashing your SD card. - -Recommended configuration: - -- **OS**: Ubuntu Server 22.04 LTS 64-bit -- **Username**: print-cli - -## Step 2: Install Required Packages - -**Tested Printers:** - -- EPSON ET-2750 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) -- EPSON ET-2860 Series with driver: Epson Expression ET-2750 EcoTank - CUPS+Gutenprint v5.3.3 (color) - -Next, we need to install the required packages for Print-CLI to work. Run the following commands and grab a coffee while -the packages are being installed (it may take a while): - -```bash -sudo apt-get update -sudo apt-get upgrade -y -sudo add-apt-repository ppa:ondrej/php -sudo apt-get install -y git cups zip unzip supervisor \ - php-cli php-zip php-curl php-xml php-mbstring \ - printer-driver-gutenprint -``` - -Install Composer as usual: - -``` -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" -sudo mv composer.phar /usr/local/bin/composer -``` - -Finally, add the Composer bin directory to your PATH, so you can run the `print-cli` command from anywhere: - -```bash -echo 'export PATH="$PATH:$HOME/.config/composer/vendor/bin"' >> ~/.bashrc -``` - -## Step 3: Ensure CUPS is Running - -Now we need to ensure that the CUPS service is running. CUPS is the printing system used by Print-CLI to send print jobs -to the printer. This is the most important step, so make sure you follow it carefully, otherwise, Print-CLI won't work -as expected, and you won't be able to print anything. - -```bash -sudo cupsctl --remote-admin --remote-any -sudo usermod -aG lpadmin print-cli -sudo /etc/init.d/cups restart -``` - -Make sure the CUPS service is running by visiting the following URL in your browser: - -```text -https://10.20.0.195:631/printers/ -``` - -You should see a page with a list of printers. If you don't see any printers, you may need to add one manually. - -Ensure that you can print a test page by clicking on the printer name and selecting "Print Test Page". - -### Find the Printer Address - -To find the printer address, visit your Printers page in CUPS, and click on the printer name. The address in the browser -should look like this: - -```text -https://10.20.0.195:631/printers/EPSON_ET_2720_Series -``` - -It contains the Name of the printer, which is the `printer-name` part. You can use this address in the configuration -file for Print-CLI. In most cases, just replace the `https` with `ipp` and replace the ip to `127.0.0.1`: - -```text -ipp://127.0.0.1:631/printers/EPSON_ET_2720_Series -``` - -## Step 4: Install Print-CLI - -So far, we have installed all the required packages and ensured that CUPS is running. Now we can install Print-CLI using -Composer, the PHP package manager. Run the following command to install Print-CLI globally: - -```bash -composer global require anikeen/print-cli -``` - -You can also use the same command to update Print-CLI to the latest version: - -```bash -composer global require anikeen/print-cli -``` - -## Step 5: Configure Print-CLI - -With the latest version, you can automatically create your `/home/print-cli/print-cli.yml` with: - -```bash -print-cli init -``` - -To test the configuration, run the following command: - -```bash -print-cli serve -``` - -If everything is configured correctly, you should see the following output: - -```text -Starting service... -Reading configuration... -Service started! -``` - -You can exit the service by pressing `Ctrl+C`. - -## Step 6: Supervisor Configuration - -To run Print-CLI as a service, we can use Supervisor. Supervisor is a process control system that allows you to monitor -and control a number of processes on UNIX-like operating systems. - -Create a new configuration file `/etc/supervisor/conf.d/print-cli.conf`: - -```bash -print-cli init:supervisor | sudo tee /etc/supervisor/conf.d/print-cli.conf > /dev/null -``` - -Now, update Supervisor to read the new configuration file and start the Print-CLI service: - -```bash -sudo supervisorctl reread -sudo supervisorctl update -sudo supervisorctl start print-cli -``` - -# Troubeshooting - -## WLAN - -https://www.makeuseof.com/connect-to-wifi-with-nmcli/