Random quote camera

Here a post of a side project of mine. I’m working on my audio system. and i have a camera showing the current record playing. Because not always something is playing i have a blank screen.

I decided to make a random quote on it.

quote sensor

sensor:
  - platform: rest
    name: "Daily quote"
    unique_id: "rest_001"
    resource: https://zenquotes.io/api/random
    value_template: '{{ value_json.q }}'
    scan_interval: 3600
    json_attributes:
      - q
      - a

php image generating code.

<?php
$homeAssistantUrl = 'http://192.168.1.1:8123';  // Replace with your Home Assistant URL
$token = '12345678';
$imageCacheFolder = 'image_cache';



function imagettfstroketext(&$image, $size, $angle, $x, $y, &$textcolor, &$strokecolor, $fontfile, $text, $px) {
    for($c1 = ($x-abs($px)); $c1 <= ($x+abs($px)); $c1++)
        for($c2 = ($y-abs($px)); $c2 <= ($y+abs($px)); $c2++)
            $bg = imagettftext($image, $size, $angle, $c1, $c2, $strokecolor, $fontfile, $text);
   return imagettftext($image, $size, $angle, $x, $y, $textcolor, $fontfile, $text);
}


function genTextToImage($imagePath, $text) {
    $image = imagecreatefromjpeg($imagePath); // Assuming the image is in JPEG format

// Get the dimensions of the original image
$originalWidth = imagesx($image);
$originalHeight = imagesy($image);

// Calculate the maximum dimension (either width or height)
$maxDimension = max($originalWidth, $originalHeight);

// Create a new square canvas with the maximum dimension
$newImage = imagecreatetruecolor($maxDimension, $maxDimension);

// Fill the canvas with a black background
$blackColor = imagecolorallocate($newImage, 0, 0, 0);
imagefill($newImage, 0, 0, $blackColor);

// Calculate the position to paste the original image onto the canvas
$x = ($maxDimension - $originalWidth) / 2;
$y = ($maxDimension - $originalHeight) / 2;

// Paste the original image onto the canvas
imagecopy($newImage, $image, $x, $y, 0, 0, $originalWidth, $originalHeight);





    $font = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'; // Replace with the path to a TrueType font file
    $textColor = imagecolorallocate($image, 255, 255, 255); // White color
    $stroke_color = imagecolorallocate($image, 0, 0, 0);

    // Get the dimensions of the image
    $imageWidth = imagesx($newImage);
    $imageHeight = imagesy($newImage);
    $fontSize = $imageWidth / 30;

    // Calculate the position for the text (20% from the bottom)
    $textHeight = count(explode("\n", $text)) * 20;
    // Break the text into lines to fit the image width
    $textLines = explode("\n", wordwrap($text, 35, "\n"));

    $textY = $imageHeight - ((count($textLines)- 1) * $fontSize +  ((count($textLines)-2) * $fontSize * 1.55));
    #$textY =  ($imageHeight * 0.20);

    foreach ($textLines as $line) {
       // imagettftext($newImage, $fontSize, 0, 10, $textY, $textColor, $font, $line);
         imagettfstroketext($newImage,$fontSize,0, 10, $textY, $textColor,$stroke_color, $font, $line,2);
        $textY += ($fontSize * 1.55) ; // Adjust the Y position for the next line
    }
    return $newImage;
}


// Fetch the quote from Home Assistant
$quoteUrl = $homeAssistantUrl . '/api/states/sensor.daily_quote';
$quoteHeader = array(
    'Authorization: Bearer ' . $token,
);

$quoteResponse = file_get_contents($quoteUrl, false, stream_context_create(['http' => ['header' => $quoteHeader]]));

if ($quoteResponse) {
    $quoteData = json_decode($quoteResponse, true);
    $quote = $quoteData['attributes']['q'] . "\n- " . $quoteData['attributes']['a'];
    $finalImagePath = $imageCacheFolder . '/final/' . preg_replace('/[\/:*?"<>| \n]/ ', '',$quote) . '.jpg'; 
    if (!file_exists( $finalImagePath )) {
      // Specify the Wikipedia API URL for Dutch Wikipedia
      $url = 'https://en.wikipedia.org/w/api.php';

      // Retrieve data from Wikipedia
      $title = str_replace(' ', '_', $quoteData['attributes']['a']); // No lowercase conversion

      if (!file_exists($imageCacheFolder)) {
        mkdir($imageCacheFolder, 0755, true); // Create the image_cache folder if it doesn't exist
      }

      $outputImage = $imageCacheFolder . '/' . $title . '.jpg'; // Output image path

      // Check if the image already exists in the cache
      if (!file_exists($outputImage)) {

          $searchUrl = 'https://en.wikipedia.org/w/api.php?action=query&format=json&list=search&srsearch='.$title;
          $searchOptions = array(
              CURLOPT_URL => $searchUrl,
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_HTTPHEADER => array('User-Agent:DaftBot/0.1 (Daft dutch quote generator)'),
          );
	  $ch = curl_init();
          curl_setopt_array($ch, $searchOptions);
          $searchResponse = curl_exec($ch);
          $searchData = json_decode($searchResponse, true);


          if (isset($searchData['query']['search'][0]['title'])) {
              $wikititle = $searchData['query']['search'][0]['title'];
          } else {
	      $wikititle = false;
          }


          // If not, download the image using cURL
          if ($wikititle ) { 
            $imageDownloadUrl = 'https://en.wikipedia.org/w/api.php?action=query&format=json&titles=' . str_replace(' ', '_',$wikititle ). '&prop=pageimages&piprop=original&pilicense=any';
          } else {
            $imageDownloadUrl = 'https://en.wikipedia.org/w/api.php?action=query&format=json&titles=' . $title . '&prop=pageimages&piprop=original&pilicense=any';
          }  
        $imageDownloadOptions = array(
              CURLOPT_URL => $imageDownloadUrl,
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_HTTPHEADER => array('User-Agent:DaftBot/0.1 (Daft dutch quote generator)'),
          );

          $ch = curl_init();
          curl_setopt_array($ch, $imageDownloadOptions);
          $imageResponse = curl_exec($ch);

          if ($imageResponse !== false) {
              $imageData = json_decode($imageResponse, true);
              $page_id = key($imageData['query']['pages']);
              $page = $imageData['query']['pages'][$page_id];

              if (isset($page['original'])) {
                  $image_url = $page['original']['source'];
#die( $image_url);
 
                  // Save the image to the cache
                  $chImage = curl_init();
                  $outputImageFile = fopen($outputImage, 'w');
                  curl_setopt($chImage, CURLOPT_URL, $image_url);
                  curl_setopt($chImage, CURLOPT_FILE, $outputImageFile);
                  curl_setopt($chImage, CURLOPT_HTTPHEADER, array('User-Agent:DaftBot/0.1 (Daft dutch quote generator)'));
                  curl_exec($chImage);
                  fclose($outputImageFile);
                  curl_close($chImage);
                } else { 
 #         die("no image1");
                }
          } else { 
 #          die("no image2");
         }
        curl_close($ch);
    }

      // gen the image
  if (!file_exists($outputImage)) {
  $random = imagecreatefromjpeg('https://random.imagecdn.app/800/800');
    imagejpeg($random,$outputImage);
  }



 
  $finalImage = genTextToImage($outputImage, $quote);
  header('Content-Type: image/jpeg');
  imagejpeg($finalImage,$finalImagePath);
  imagejpeg($finalImage);
  } else {
    header('Content-Type: image/jpeg');
    imagejpeg(imagecreatefromjpeg($finalImagePath));
  }
}
?>


make the url dynamic on a changing url. that you can check the only update on url update checkbox.

- platform: template
    sensors:
      qoute_image:
       friendly_name: "qoute image"
       value_template: >
            http://192.168.5.1/local/quote_image.jpg.php?foo={{ state_attr('sensor.daily_quote','a')|replace(' ','_') }}
          
1 Like

Some small spelling errors that trigger my OCD - you have both quote and qoute

Love it though.

A right. I have problems with spelling. English is a strange language.

1 Like

There is always a ‘u’ after a ‘q’. :slight_smile:

I updated the script.
Better alignment of the text. and some black borders around the white text.

and made it search wikipedia and return the 1st hit.

Nice project!
How did you visualize the quote/image in a lovelace card? can you share the code as an example?

I’ve made a small improvement to avoid the error State max length is 255 characters:

sensor:
- platform: rest
  scan_interval: 3600
  resource: "https://zenquotes.io/api/random"
  method: GET
  name: "Daily quote"
  unique_id: rest_001
  json_attributes:
    - q
    - a
  value_template: 'Active'

And where did you place the php file? In the www folder of HA but can it process php?
@Daft

you need a separate server with php.

and i use the image entity card.

Hi Bram
I’m running the php script in Docker now but it doesn’t seems to have the authentication:

  • Login attempt or request with invalid authentication from 10.0.3.2 (10.0.3.2). Requested URL: ‘/api/states/sensor.daily_quote_api’. (None)

you need to create a long term client token and cut past it in the code.

click your username scroll down for long term tokens. and create one there.
it will be shown only once.

oke if wikipedia has no image now one using online AI is generated. code in testing will post soonish.