Tag Archives: php

Album player using old CD Cover site.

In the past, I made an overview of CDs I own, and which CDs I was missing from my top artists.

Today I wanted to have a little test using above code and some additional script to get a little website which enables me to quick start playing an album using LMS.

Exporting the album database in Linux:

sqlite3 /var/lib/squeezeboxserver/cache/library.db ".dump albums" > /mnt/www/albums-test.dump

grep -i culture /mnt/private/albums
INSERT INTO "albums" VALUES(16872,'Bothy Culture','BOTHY CULTURE','BOTHY CULTURE',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,18957,NULL);

So, 16872 is the needed album ID.

To start playing this (adhoc), I used the below command.

curl "http://IP-OF-MY-LMS-SERVER:9000/anyurl?p0=playlistcontrol&p1=album_id:16872&p2=cmd:load&player=12:23:34:45:56:67"

Player=12:23:34:45:56:67 is the mac address from a squeezebox player.

I wrote some additional PHP code in my original CD Cover site, and got this working.

I think I will rewrite the code using python to get a more flexible generator.

Left the LMS player, right the cover website. (Clicking 3 covers to change albums)

Realtime draw maze using php gd lib

UPDATE FROM: https://www.henriaanstoot.nl/2023/05/08/wss-websocket-to-mqtt-updating-website/

Mqtt controlled

for f in 1 2 3 4 5 6 7 0 ; do mosquitto_pub -h mqttserver -t web/mapviewer -m $f ;sleep 2 ;done

Below can be used in yesterdays script.
And can be called as image source.

<img src="draw.php?direction=0">

CODE

<?php

function imagelinethick($image, $x1, $y1, $x2, $y2, $color, $thick = 3)
{
    /* this way it works well only for orthogonal lines
    imagesetthickness($image, $thick);
    return imageline($image, $x1, $y1, $x2, $y2, $color);
    */
    if ($thick == 1) {
        return imageline($image, $x1, $y1, $x2, $y2, $color);
    }
    $t = $thick / 2 - 0.5;
    if ($x1 == $x2 || $y1 == $y2) {
        return imagefilledrectangle($image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color);
    }
    $k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q
    $a = $t / sqrt(1 + pow($k, 2));
    $points = array(
        round($x1 - (1+$k)*$a), round($y1 + (1-$k)*$a),
        round($x1 - (1-$k)*$a), round($y1 - (1+$k)*$a),
        round($x2 + (1+$k)*$a), round($y2 - (1-$k)*$a),
        round($x2 + (1-$k)*$a), round($y2 + (1+$k)*$a),
    );
    imagefilledpolygon($image, $points, 4, $color);
    return imagepolygon($image, $points, 4, $color);
}
header("Content-Type: image/png");
$im = imagecreate(640, 640);
$background_color = imagecolorallocate($im, 0, 0, 0);
$text_color = imagecolorallocate($im, 255, 255, 255);
$text_color1 = imagecolorallocate($im, 128, 128, 128);

$max=640;
$depth1=80;
$depth2=160;
$depth3=190;
$depthcolor1 = imagecolorallocate($im, 255, 255, 255);
$depthcolor2 = imagecolorallocate($im, 128, 128, 128);
$depthcolor3 = imagecolorallocate($im, 32, 32, 32);

$direction = $_GET["direction"];


// draw always
imagelinethick($im,0,0,$depth1,$depth1,$depthcolor1);
imagelinethick($im,$max,0,$max-$depth1,$depth1,$depthcolor1);
imagelinethick($im,0,$max,$depth1,$max-$depth1,$depthcolor1);
imagelinethick($im,$max,$max,$max-$depth1,$max-$depth1,$depthcolor1);

if ( $direction == "1" ) {
imagelinethick($im,$max-$depth1,$depth1,$max-$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$max-$depth1,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$max-$depth2,$depth2,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth1,$max-$depth2,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth1,$depth2,$max-$depth2,$depth2,$depthcolor2);
// front
imagelinethick($im,$depth1,$depth1,$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "4" ) {
imagelinethick($im,$depth1,$max-$depth1,$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth1,$depth1,$depth2,$depth2,$depthcolor2);
imagelinethick($im,$depth2,$depth2,$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$max-$depth2,$max-$depth1,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$depth2,$max-$depth1,$depth2,$depthcolor2);
// front
imagelinethick($im,$max-$depth1,$depth1,$max-$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "5" ) {
imagelinethick($im,$depth1,$depth2,$max-$depth1,$depth2,$depthcolor2);
imagelinethick($im,$depth1,$max-$depth2,$max-$depth1,$max-$depth2,$depthcolor2);
// front
imagelinethick($im,$depth1,$depth1,$depth1,$max-$depth1,$depthcolor1);
imagelinethick($im,$max-$depth1,$depth1,$max-$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "2" ) {
imagelinethick($im,$depth1,$depth1,$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$depth1,$max-$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$max-$depth1,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth1,$max-$depth1,$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$depth2,$depth3,$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$depth2,$max-$depth3,$depth3,$depthcolor3);
imagelinethick($im,$depth2,$max-$depth2,$depth3,$max-$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth3,$max-$depth3,$depthcolor3);
}

if ( $direction == "3" ) {
imagelinethick($im,$max-$depth1,$depth1,$max-$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$max-$depth1,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$depth2,$depth1,$depth2,$depthcolor2);
imagelinethick($im,$depth2,$max-$depth2,$depth1,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$depth2,$depth2,$max-$depth2,$depthcolor2);

//depth3
imagelinethick($im,$depth2,$depth2,$depth3,$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$depth2,$max-$depth3,$depth3,$depthcolor3);
imagelinethick($im,$depth2,$max-$depth2,$depth3,$max-$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth3,$max-$depth3,$depthcolor3);
//front
imagelinethick($im,$depth1,$depth1,$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "6" ) {
imagelinethick($im,$depth1,$depth1,$depth2,$depth2,$depthcolor2);
imagelinethick($im,$depth1,$max-$depth1,$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$depth2,$max-$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$max-$depth2,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth2,$depth2,$depthcolor2);

//depth3
imagelinethick($im,$depth2,$depth2,$depth3,$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$depth2,$max-$depth3,$depth3,$depthcolor3);
imagelinethick($im,$depth2,$max-$depth2,$depth3,$max-$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth3,$max-$depth3,$depthcolor3);
//front
imagelinethick($im,$max-$depth1,$depth1,$max-$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "7" ) {
imagelinethick($im,$depth1,$depth2,$depth2,$depth2,$depthcolor2);
imagelinethick($im,$depth1,$max-$depth2,$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$depth2,$max-$depth2,$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$depth2,$max-$depth2,$depth2,$depthcolor2);
imagelinethick($im,$max-$depth1,$max-$depth2,$max-$depth2,$max-$depth2,$depthcolor2);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth2,$depth2,$depthcolor2);
//depth3
imagelinethick($im,$depth2,$depth2,$depth3,$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$depth2,$max-$depth3,$depth3,$depthcolor3);
imagelinethick($im,$depth2,$max-$depth2,$depth3,$max-$depth3,$depthcolor3);
imagelinethick($im,$max-$depth2,$max-$depth2,$max-$depth3,$max-$depth3,$depthcolor3);
//front
imagelinethick($im,$max-$depth1,$depth1,$max-$depth1,$max-$depth1,$depthcolor1);
imagelinethick($im,$depth1,$depth1,$depth1,$max-$depth1,$depthcolor1);
}

if ( $direction == "0" ) {
imagelinethick($im,$depth1,$depth1,$max-$depth1,$depth1,$depthcolor1);
imagelinethick($im,$max-$depth1,$depth1,$max-$depth1,$max-$depth1,$depthcolor1);
imagelinethick($im,$max-$depth1,$max-$depth1,$depth1,$max-$depth1,$depthcolor1);
imagelinethick($im,$depth1,$max-$depth1,$depth1,$depth1,$depthcolor1);
}

imagepng($im);
imagedestroy($im);
?>

Simple FreeOTP access for PHP

I’ve played with FreeOTP for my own websites in the past, but here is a cleaned up version.

apt-get install qrencode oathtool

Using a webserver with php you can use below simple sniplet to restrict access.

<?PHP
$output = shell_exec('oathtool --totp=sha256 --base32 ################PLACE-SECRET-OUTPUT-HERE-############');
$otp=$_POST["otp"];
$stripped = preg_replace('/\D/', '', $output);
$strippedotp = preg_replace('/\D/', '', $otp);
?>
<form action="" method="post">
OTP: <input type="text" name="otp"><br>
<input type="submit">
</form>
<?PHP
if(strcmp("$strippedotp", "$stripped") == 0)
{ echo "Access"; }
else
{ echo "No Access"; }
?>

bash script to generate secret and qrcode

#!/bin/bash
secret=$(echo 1234567812345678 | base32)
echo "Secret : (place in PHP file)"
echo $secret
qrencode -t ANSI256 -o - $(echo otpauth://totp/Mine:myname?secret=${secret}\&issuer=Mine\&algorithm=SHA256\&digits=6\&period=30) -o qrcode.png -t png -s 5

Above generates a QRcode you can import in FreeOTP

Nextcloud Notes

(Old and new notes, i will keep adding info)

A install step stopped .. find a .step file and remove this

[nginx@nextcloud nextcloud]$ rm /mnt/fileserver/updater-ocv0dxmv6vpg/.step

Login page loop: directory rights wrong

This server was running nginx
drwxrwx---.  2 root apache     6 Jan  3 18:19 wsdlcache
drwxrwx---.  2 root apache 16384 Jan  3 18:19 session
drwxrwx---.  2 root apache     6 Jan  3 18:19 opcache

fixed with:
chown root:nginx /var/lib/php/session 
chown root:nginx /var/lib/php/opcache
chown root:nginx /var/lib/php/wsdlcache

Other things to check (Generic)

  • Really slow storage can cause problems, check with iotop
  • Check selinux! (temporary test with : setenforce 0 )
  • Behind a reverse proxy? remove these options in your config.php and check a local connection. This is a test for: reverseproxy, firewall and proxy settings in config.php. Need to test a localhost connection with your browser?
    ssh -L8080:localhost:80 nextcloudserver (and connect to http://localhost:8080 with your browser
  • Check your services! .. memcache, nginx, apache, mysql/mariadb
  • Check logs: journalctl -f, tail -f /var/log/nginx/*log /x/y/z/netcloud.log
  • Storage on a remote server? (Using NFS for example) Check your rights
    • change into your webserver account and test access to files.storage
      ( sudo su – nginx -s /bin/bash )

Migrating users to a complete new nextcloud instance keeping IDs

Get a list of users

su - apache -s /bin/bash  -c " source /opt/rh/rh-php72/enable; cd /var/www/html/nextcloud ;./occ user:list 2>/dev/null | grep -v admin ; exit" > listofusers

Sync and check users
Rsync data and use occ command to scan files into database

#!/bin/bash
cd /root
#set -x
templist=templist.$$
./getlistusers > userlistsvr1
cat userlistsvr1 > /tmp/$templist
cat userlistsvr2.new >> /tmp/$templist
cat /tmp/$templist | cut -f2 -d: | sort  | uniq -c | grep " 2 " | cut -c 10- > /tmp/deze.$$
cat /tmp/deze.$$ | while read ; do
	echo "User $REPLY"
	oldtest=0
	newtest=0
	old=$(cat userlistsvr2 | grep "$REPLY" | awk '{ print $2 }' | sed s/://g)
	new=$(cat userlistsvr1 | grep "$REPLY" | awk '{ print $2 }' | sed s/://g)
	test -d /var/data/owncloud/data/$old/files/ && oldtest=1
	test -d /var/data/owncloud/nextcloud/$new/files/ && newtest=1
	if [ $oldtest -eq 1 ] ; then echo " found old dir - $old" ; fi
	if [ $newtest -eq 1 ] ; then echo " found new dir - $new" ; fi
	if [ $oldtest -eq 1 ] && [ $newtest -eq 1 ] ; then echo "  both found - ready to sync" 
		rsync -va /var/data/owncloud/data/$old/files/ /var/data/owncloud/nextcloud/$new/files/
		echo " RUN occ files:scan $new ############################"
		su - apache -s /bin/bash  -c " source /opt/rh/rh-php72/enable; cd /var/www/html/nextcloud ;./occ files:scan $new"
        fi
	echo " "
 
done
rsync -va  /var/data/owncloud/data/root/files/xyz/ /var/data/owncloud/nextcloud/38842e24-5d0d-11e9-dbdb-0001a4ab950f/files/
su - apache -s /bin/bash  -c " source /opt/rh/rh-php72/enable; cd /var/www/html/nextcloud ;./occ files:scan 38842e24-5d0d-11e9-dbdb-0001a4ab950f"

Escape game world generator

A world map generator in php.
This php script selects randomly 3 cities from a CSV file and draws these on a worldmap.
No cities wil be choosen which have could cause a drawing overlap.
Every player can see the same generated worldmap with a countdown timer.

CSV example with places and coordinates (cities.csv)

London,905,412
Amsterdam,929,414
Wellington,1722,867
Costa Rica,524,640
New Delhi,1270,514
New York,567,477
Tokio,1548,500

generate.html

<html><body bgcolor=black>
<header>
  <div class="menu_area"></div>
  <center>
<p id="demo" style="text:white;"></p>
  </center>
</header>
<style>
html, body, header {
    overflow: hidden; /* Hide scrollbars */
    height: 100%;
    text: white;
}
header {
    background-image: url('world.php');
    background-size: cover;
}
  p {
  color: white;
  font-size: large;
  font-family: Verdana, Arial, sans-serif;
  font-size: 42px;
 }
</style>
<script>
	Date.prototype.addHours= function(h){
    this.setHours(this.getHours()+h);
    return this;
}

var countDownDate = new Date().addHours(1).getTime();
  var x = setInterval(function() {
  var now = new Date().getTime();
  var distance = countDownDate - now;
  var days = Math.floor(distance / (1000 * 60 * 60 * 24));
  var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
  var seconds = Math.floor((distance % (1000 * 60)) / 1000);

  document.getElementById("demo").innerHTML = days + "d " + hours + "h "
  + minutes + "m " + seconds + "s ";

  if (distance < 0) {
    clearInterval(x);
    document.getElementById("demo").innerHTML = "EXPIRED";
  }
}, 1000);
</script>
</body></html>

Used world image to draw on.

The squares and city names are dynamically drawn using php GD lib.

apt-get install apache2 php php-gd

php example:

// create image from source
$src = imagecreatefromjpeg('world.jpg');
$dest = imagecreatetruecolor(1920, 1080);
// 1920x1080
imagecopy($dest, $src, 0, 0, 0,0, 1920, 1080);
// assign color
$white = imagecolorallocate($dest, 255, 255, 255);
// rectangle example
imagerectangle($dest, $x, $y, $x2, $y2, $white);
// add text
Imagettftext($dest, 24, 0, $x, $y, $white, $font, "Text");
// set header output as image
header('Content-Type: image/jpg');
// output to browser
imagejpeg($dest);
// output to file
imagejpeg($dest, 'generatedworld.jpg');
// Free memory
imagedestroy($dest);
imagedestroy($src);

world.php is included as image, it dynamically generates the map

<?php

putenv('GDFONTPATH=' . realpath('.'));

// Name the font to be used (note the lack of the .ttf extension)
$font = 'tahoma.ttf';
$city1="";
$city2="";
$city3="";

// Create image instances
$src = imagecreatefromjpeg('world.jpg');
$dest = imagecreatetruecolor(1920, 1080);

// Copy
imagecopy($dest, $src, 0, 0, 0,0, 1920, 1080);

$green = imagecolorallocate($dest, 255, 255, 255);



$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $city1="$data[0]";
    $x1=$data[1];
    $y1=$data[2];
}

imagerectangle($dest, $x1-50, $y1-50, $x1+50, $y1+50, $green);
imagerectangle($dest, $x1-49, $y1-49, $x1+49, $y1+49, $green);
imagerectangle($dest, $x1-10, $y1, $x1+10, $y1-1, $green);
imagerectangle($dest, $x1, $y1-10, $x1-1, $y1+10, $green);
Imagettftext($dest, 24, 0, $x1-50, $y1-60, $green, $font, "$city1");

while($city2 == "") {

$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $x2=$data[1];
    $y2=$data[2];
    $deltax=abs($x2-$x1);
    $deltay=abs($y2-$y1);
}
if($data[0] != $city1 && $deltax > 100 && $deltay > 100 ){
	$city2=$data[0];
}

}


imagerectangle($dest, $x2-50, $y2-50, $x2+50, $y2+50, $green);
imagerectangle($dest, $x2-49, $y2-49, $x2+49, $y2+49, $green);
imagerectangle($dest, $x2-10, $y2, $x2+10, $y2-1, $green);
imagerectangle($dest, $x2, $y2-10, $x2-1, $y2+10, $green);
Imagettftext($dest, 24, 0, $x2-50, $y2-60, $green, $font, "$city2");


while($city3 == "") {

$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $x3=$data[1];
    $y3=$data[2];
    $deltax=abs($x3-$x1);
    $deltay=abs($y3-$y1);
    $deltax1=abs($x3-$x2);
    $deltay1=abs($y3-$y2);
}
if($data[0] != $city1 && $data[0] != $city2 && $deltax > 100 && $deltay > 100 && $deltax1 > 100 && $deltay1 > 100 ){
	$city3=$data[0];
}
}
imagerectangle($dest, $x3-50, $y3-50, $x3+50, $y3+50, $green);
imagerectangle($dest, $x3-49, $y3-49, $x3+49, $y3+49, $green);
imagerectangle($dest, $x3-10, $y3, $x3+10, $y3-1, $green);
imagerectangle($dest, $x3, $y3-10, $x3-1, $y3+10, $green);
Imagettftext($dest, 24, 0, $x3-50, $y3-60, $green, $font, "$city3");


// Output and free from memory
header('Content-Type: image/jpg');
imagejpeg($dest);
imagejpeg($dest, 'generatedworld.jpg');


imagedestroy($dest);
imagedestroy($src);
?>

Finding files on my fileserver

I use several tools to find files on my server.

Loads of stuff on my main fileserver.
(Graph is a great tool called DUC) https://duc.zevv.nl/

Besides a search engine, i have a file finder.
Due to the massive amount of data, i like to find things by other means than knowing the directory structure.

I can find files by filename, but also by contents.

I’ll talk about find by contents first.

I’ve got loads of documents in Pdf, HTML, txt, doc, sheets , wordperfect etcetera.
Those documents i can find using a tool named Namazu.
This is quite a old tool, but i’m using it for a long time and it still works great.
I didn’t find a better replacement yet.
(But i’ve been looking into : elasticsearch, Solr, Lucene)

http://www.namazu.org/ is easy to install, but if you want the tool to scrape different kinds of documents you have to add some additional software.

My multipurpose printer can scan pages in pdf.
Those are only embedded jpg’s in a pdf container.
I will talk about how i handle these later.

My current start page :
This index contains 267,763 documents and 14,036,762 keywords.
Search example of namazu

Some things to consider when implementing namazu:

  • tweak the file types to scrape, it makes no sense to scrape binaries
  • tweak the directories to scrape (example below)
    • 0 1 * * 1 fash /usr/bin/mknmz -f /etc/namazu/mknmzrc –output-dir=/namazu/ /mnt/private/paperwork/ /mnt/private/information/ /mnt/private/Art\ en\ hobby\ Projects/ /mnt/private/Music\ Projects/ /mnt/private/bagpipe-music-writer/ –exclude=XXX –exclude=/mnt/binaries > /tmp/namazu.log 2>&1
  • you can set a parameter in the config for search only, this disables downloading the found link in the results!

Before Namazu i used HtDig.

Screenshot htdig

HtDIg also can scrape remote websites, Namazu can’t.

Preparing PDF for indexing:

I’ve written some scripts to make PDFs containing scanned text scrape-able.
( https://gitlab.com/fash/inotify-scanner-parser )
What it does:

  • My scanner puts a scanned pdf on my fileserver in a certain directory
  • Inotify detects a written file
  • it will copy the file, run OCR on it (tesseract) and writes a txt file (scapeable)
  • After that the text will be embedded (overlay) on the PDF, so now it becomes searchable/scrapeable
  • When certain keywords are found, it will sort documents in subdirs
Example from a scanned jpg, i can find OCR words!
(note .. the overlay is exact on the found words)

Finding files by name:

For finding files a made a little webpage like this:

It is a simple webpage grabbing through a list of files.
It takes the first keyword and does a grep, it takes a second keyword to match also.
I can select different file databases to search. (This case is private)
Between search and private i can give the number of entries to print.
So i can do
Search “ansible” NOT “tower” 50 entries from the public fileset

Crontab:

20 5 * * * /usr/bin/find /mnt/shark*  > /var/www/html/findfiles/sharkoon
10 4 * * * /usr/bin/find /tank/populair > /var/www/html/findfiles/populair
20 4 * * * /usr/bin/find /tank/celtic > /var/www/html/findfiles/celtic
etc

And a php script (dirty fast hack, never came around it to make it a final version)

<html><head><title></title></body>
<font face="Tahoma"><small>|keyword|(keyword)|search|(nr results)|NOT SECOND KEYWORD|share|</small><BR>
Search: <form method="post" action="/findfiles/?"><input type="Text" name="words" size=10 value=""><input type="Text" name="words2" size=10 value=""><input type="Submit" name="submit" value="search"><input type="Text" name="nrlines" size=3 value=""><input type="checkbox" name="not" unchecked>
<SELECT NAME=findfile>
<OPTION VALUE=private>private
<OPTION VALUE=celtic>celtic
<OPTION VALUE=populair>populair
<OPTION VALUE=dump>public
<OPTION VALUE=sharkoon>sharkoon
</SELECT>
</form>
<P><PRE>
<?php
$words2=$_POST['words2'];
$words=$_POST['words'];
$filefile=$_POST['filefile'];
$findfile=$_POST['findfile'];
$nrlines=$_POST['nrlines'];
$not=$_POST['not'];


if ($words2=="xsearch") { $findfile="other"; $words2=""; }
if ($nrlines) {  } else { $nrlines=100; }
if ($words && $words2=="") {
$words = preg_replace("(\r\n|\n|\r)", "", $words);
$words = preg_replace("/[^0-9a-z]/i",'', $words);
$command = "/bin/cat $findfile |/bin/grep -i $words |head -$nrlines";
$blah=shell_exec($command);
$blah=str_replace($words, "<b><font color=red>$words</font></b>",$blah);
print $blah;
}
if (($words) and ($words2)) {
$words = preg_replace("(\r\n|\n|\r)", "", $words);
$words = preg_replace("/[^0-9a-z.]/i",'', $words);
$words2 = preg_replace("(\r\n|\n|\r)", "", $words2);
$words2 = preg_replace("/[^0-9a-z.]/i",'', $words2);
if ($not=="on") {
$command = "/bin/cat $findfile |/bin/grep -i $words | /bin/grep -iv $words2 |head -$nrlines";
} else {
$command = "/bin/cat $findfile |/bin/grep -i $words | /bin/grep -i $words2 |head -$nrlines";
}
$blah=shell_exec($command);
$blah=str_replace($words, "<b><font color=red>$words</font></b>",$blah);
$blah=str_replace($words2, "<b><font color=red>$words2</font></b>",$blah);
print $blah;
}
?>
</PRE>
</body></html>

Domotemp – domoticz temperature floorplan

For my home automation i’m using Home Assistant and Domoticz.
All 433Mhz Temperature/Humidity are connected to a RFXcom device on two domoticx instances. (Master slave construction)

I’ve made a php script and a bash script to draw all sensors on a floorplan in realtime.

There is also a cron running which takes a snapshot of the generated image every 5 minutes.
These images are being converted to MP4 and animated GIF to have a timelapse with all temperatures displayed on a floorplan.

An obfuscated view of the floorplan

The circles are where sensors are placed.
Colors are from blue till red, representing the heat.
In the center is the measured temperature value.

The (shortened) PHP script: (index.php)

<?php
header('Content-type: image/png');
// This is the floorplan empty ..
$png_image = imagecreatefrompng('plattegrondenmerge.png');
$white = ImageColorAllocate($png_image, 0, 0, 0);

$max = 40;
$min = -10;

// living
// getstate is a bash script (see below which gets the values from domoticz using curl) 
// 840 is the domoticz idx
$temp840 = shell_exec('./getstate 840');
// A gray circle will be drawn if the temperature age is > 500 seconds
$age = shell_exec('./new.sh 18 840 500 >/dev/null || echo gray');
// location of circle
$start_x = 950;
$start_y = 760;
$line = $temp840 + 10;
// get x-th line from colors
$colorfromlist = shell_exec("tail --lines=$line ./colors2 | head -1");
if(strpos($age, "gray") !== false){
 $colorfromlist = "128,128,128";
};
$colors = explode(",", $colorfromlist);
$color = imagecolorallocatealpha($png_image, $colors[0], $colors[1], $colors[2], 50);
// draw circle
imagefilledellipse ($png_image, $start_x, $start_y, 175, 175, $color);
$start_x = $start_x - 70;
$start_y = $start_y + 15;
// add text
imagettftext($png_image, 24, 0, $start_x, $start_y, $white, './verdana.ttf', $temp840);

// winecellar
$temp840 = shell_exec('./getstate 839');
$age = shell_exec('./new.sh 18 839 700 >/dev/null || echo gray');
$start_x = 560;
$start_y = 840;
$line = $temp840 + 10;
$colorfromlist = shell_exec("tail --lines=$line ./colors2 | head -1");
if(strpos($age, "gray") !== false){
 $colorfromlist = "128,128,128";
};
$colors = explode(",", $colorfromlist);
$color = imagecolorallocatealpha($png_image, $colors[0], $colors[1], $colors[2], 50);
imagefilledellipse ($png_image, $start_x, $start_y, 175, 175, $color);
$start_x = $start_x - 70;
$start_y = $start_y + 15;
imagettftext($png_image, 24, 0, $start_x, $start_y, $white, './verdana.ttf', $temp840);

// ETC ETC

imagesavealpha($png_image, TRUE); 

  imagepng($png_image);
  imagedestroy($png_image);
?>


getstate bash script
(gets the temperature from domoticz instance1 given an idx)

#!/bin/bash
curl -s --connect-timeout 2 --max-time 5 "http://ip-domoticz1:8080/json.htm?type=devices&rid=$1" | egrep "Temp|Humid" | awk '{print $3 }' | cut -f1 -d\. | grep -v \" | tr -d "\n\r" | sed s/,/%\ /g | awk '{ print $2"° "$1 }'

new.sh script gets the age of the reading from
domoticz1 or domoticz1
Usage: ./new.sh <domoticz-last-numer-ip> <idx> <maxageinseconds>

#!/bin/bash
## server idx time
now=$(date +%s)
lastupdate=$(curl -s -i -H "Accept: application/json" "http://192.168.1.$1:8080/json.htm?type=devices&rid=$2" |  grep LastUpdate | cut -f4 -d\" )
#echo $lastupdate
seen=$(date -d "$lastupdate" +%s)
#echo $seen
#echo "$(( $now - $seen))"
difftime="$(( $now - $seen))"
if [ $difftime -gt $3 ] ; then
  echo "WARN : too old - $difftime seconds"
  exit 1
else
  echo "OK : $difftime seconds"
  exit 0
fi

colors2 – a list of colors representing the temperature
red -> green -> blue

255,0,0
255,10,0
255,20,0
255,30,0
255,40,0
255,60,0
255,70,0
255,80,0
255,90,0
255,100,0
255,120,0
255,130,0
255,140,0
255,150,0
255,160,0
255,180,0
255,190,0
255,200,0
255,210,0
255,220,0
255,240,0
255,250,0
253,255,0
215,255,0
176,255,0
101,255,0
62,255,0
23,255,0
0,255,16
0,255,54
0,255,131
0,255,168
0,255,208
0,255,244
0,228,255
0,196,255
0,180,255
0,164,255
0,148,255
0,132,255
0,100,255
0,84,255
0,68,255
0,50,255
0,34,255
0,2,255
0,0,255
1,0,255
2,0,255
3,0,255
5,0,255

Crontab and gif/mp4 generators

# crontab
5 * * * * root /scripts/domotemp/crontemp.sh

# crontemp.sh
# stores an image with a date.
cd /www/webdir/domotemp
wget https://mydomoticzweb/domotemp/ -O $(date +%Y%m%d%H).png >/dev/null 2>/dev/null 1>/dev/null


# rest scripts
mkdir -p embed
# below adds the time to the image
ls 2020*png | sort -n -k1 | while read ; do hour=$(echo $REPLY | cut -c9,10) ; convert -pointsize 80 -fill black -draw 'text 1650 100 '\"$hour:00\"'' -resize 960x540 $REPLY  embed/$REPLY ;done
# convert to gif
convert $(ls embed/2020*png | sort -n -k1) animation.gif
# convert to mp4
ffmpeg -f image2 -r 24 -pattern_type glob -i '*.png'   -vcodec libx264 -profile:v high444 -refs 16 -crf 0 -preset ultrafast -vf scale=1920:1080 domotemp.mp4

Nikon Lenses

Created a graph for my Nikon lenses. It shows me what lens i can use at what Aperture / Range.

( https://media.henriaanstoot.nl/websites/lenses/ )

It uses canvas to draw lines and text, but thats only interesting for static stuff. ( https://www.w3schools.com/graphics/canvas_drawing.asp )
My example uses a php script to load information from a csv file. And loops though those entries and draw lens info.

Piece of canvas script to draw 17-70mm lens info.

ctx.fillStyle = 'rgba(0, 200, 100, 0.9)';
ctx.lineWidth=2;
ctx.beginPath();
ctx.moveTo(189.98560128675,163.25813588993);
ctx.lineTo(359.81942904592,202.49237972319);
ctx.lineTo(359.81942904592,250);
ctx.lineTo(189.98560128675,250);
ctx.strokeStyle="rgba(0,0,0,0.9)";
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.fillStyle = 'rgba(0,0,0,0.9';
ctx.save();
ctx.translate( 189.98560128675,153.25813588993);
ctx.rotate(-Math.PI / 4);
ctx.fillText("17-70mm", 0,0);
ctx.restore();

CSV lenses info

80;200;2.8;2.8;FX;80-200mm f/2.8
10;24;3.5;4.5;DX;10-24mm wide
28;100;3.5;5.6;;28-100mm leen
17;70;2.8;4;DX;17-70mm
35;35;1.8;1.8;DX;35mm f/1.8
60;60;2.8;2.8;FX;60mm f/2.8
28;300;3.5;5.6;FX;28-300mm

PHP Code to generate


<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>Lens</title>
  <style type='text/css'>
    body{ background-color: ivory; }
#canvas{border:1px solid white;}

  </style>
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
<?PHP
$log=yes;

print "ctx.strokeStyle = 'rgba(200, 0, 0, 0.1)';";
print "ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';";
$apes=array(1.4,2,2.8,4,5.6);
$count=0;
foreach ($apes as &$value) {
$height=log($value)*110;
$count=$count+1;
$height=$height+50;
print "ctx.font = '20px Arial';";
print "ctx.fillText(\"$value\", 20, $height);";
print "ctx.lineWidth=2;";
print "ctx.moveTo(50, $height);";
print "ctx.lineTo(900, $height);";
}
print "ctx.stroke();";
$count=0;
$apes=array(8,16,32,64,128);
foreach ($apes as &$value) {
$width=log($value)*120;
$count=$count+1;
$width=$width-150;
print "ctx.fillText(\"$value\", $width, 270);";
}
print "ctx.font = '15px Arial';";
?>
ctx.beginPath();
ctx.moveTo(50,250);
ctx.lineTo(900,250);
ctx.moveTo(50,50);
ctx.lineTo(50,250);
ctx.lineWidth=1;
ctx.strokeStyle="rgba(0, 0, 0, 0.9)";
ctx.stroke();
<?PHP

$configfile="lenses.csv";

$config=get2DArrayFromCsv($configfile,";");

        foreach ($config as $value) {
$fp1=$value[0];
$fp2=$value[1];
$ap1=$value[2];
$ap2=$value[3];
$x1=(log($fp1)*120)-150;
$x2=(log($fp2)*120)-150;
$y1=(log($ap1)*110)+50;
$y2=(log($ap2)*110)+50;
if ( $x1 == $x2 ) { $color="200,0,0"; } else { $color="0,0,0"; }
if ( $x1 == $x2 ) { $x2 = $x2 + 6; }
$green=200;
$xx1=(log($fp1*2)*120)-150;
$xxx1=(log($fp1*2)*120)-160;
$xx2=(log($fp2*2)*120)-150;
$yy1=$y1-10;
$yyy1=$y1+10;

if ( $xx1 == $xx2 ) { $xx2 = $xx2 + 6; }
if ( $value[4] == "FX" ) { $col="200"; $trans=0.2; } else { $col="0";$trans=0.9;  }
?>

ctx.fillStyle = 'rgba(<?PHP print $col; ?>, <?PHP print $green; ?>, 100, <?PHP print $trans; ?>)';
ctx.lineWidth=2;
ctx.beginPath();
ctx.moveTo(<?PHP print "$x1"; ?>,<?PHP print "$y1"; ?>);
ctx.lineTo(<?PHP print "$x2"; ?>,<?PHP print "$y2"; ?>);
ctx.lineTo(<?PHP print "$x2"; ?>,<?PHP print "250"; ?>);
ctx.lineTo(<?PHP print "$x1"; ?>,<?PHP print "250"; ?>);
ctx.strokeStyle="rgba(<?PHP print "$color"; ?>,<?PHP print $trans;  ?>)";
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.fillStyle = 'rgba(<?PHP print "$color"; ?>,<?PHP print $trans;  ?>';
ctx.save();
ctx.translate( <?PHP print "$x1"; ?>,<?PHP print "$yy1"; ?>);
ctx.rotate(-Math.PI / 4);
ctx.fillText("<?PHP print "$value[5]"; ?>", 0,0);
ctx.restore();
<?PHP

if ( $value[4] == "FX" ) {
?>
ctx.fillStyle = 'rgba(0, <?PHP print "200"; ?>, 0, 0.9)';
ctx.beginPath();
ctx.moveTo(<?PHP print "$xx1"; ?>,<?PHP print "$y1"; ?>);
ctx.lineTo(<?PHP print "$xx2"; ?>,<?PHP print "$y2"; ?>);
ctx.lineTo(<?PHP print "$xx2"; ?>,<?PHP print "250"; ?>);
ctx.lineTo(<?PHP print "$xx1"; ?>,<?PHP print "250"; ?>);
ctx.strokeStyle="rgba(<?PHP print "$color"; ?>,0.9";
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';
ctx.save();
ctx.translate( <?PHP print "$xx1"; ?>,<?PHP print "$yy1"; ?>);
ctx.rotate(-Math.PI / 4);
ctx.fillText("<?PHP print "$value[5]"; ?> crop", <?PHP print "0"; ?>, <?PHP print "0"; ?>);
ctx.restore();
<?PHP
}
}
?>

ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.font = 'bold 10pt Calibri';

}//]]>

</script>

</head>
<body><small>
  <canvas id="canvas" width=700 height=300></canvas>
<P>
<?PHP
$configfile="lenses.csv";

   function get2DArrayFromCsv($file,$delimiter) {
       if (($handle = fopen($file, "r")) !== FALSE) {
           $i = 0;
           while (($lineArray = fgetcsv($handle, 4000, $delimiter)) !== FALSE) {
               for ($j=0; $j<count($lineArray); $j++) {
                   $data2DArray[$i][$j] = $lineArray[$j];
               }
               $i++;
           }
           fclose($handle);
       }
       return $data2DArray;
   }

$config=get2DArrayFromCsv($configfile,";");

        foreach ($config as $value) {
            print "<B>Lens = $value[5] </B><BR>";
            print "Focal min = $value[0] <BR>";
            print "Focal max = $value[1] <BR>";
            print "Arp min = $value[2] <BR>";
            print "Arp max = $value[3] <BR>";
            print "Crop (FX/DX) = $value[4] <P>";
        }


?>

</body>

</html>

Kanban effectiveness graph generator

While attending a Kanban workshop, i needed to keep myself awake.
I’d hardly slept that night.

So i wrote something useful using PHP JPgraph and Sqlite, while attending the workshop.
It shows the effectiveness of the team using kanban.

index.php

<html>
<head>
</head>
<body>
<?PHP
if ($_SERVER['REQUEST_METHOD'] == 'POST'){
include ("insert.php");
}
include ("getlast.php");
?>
<img src="graph.php"><P>
<form action="" method="POST">
Tickets from board: <input size="4" name="klaar" value="<?PHP print $klaar; ?>"/> <input type="checkbox" name="useklaar" value="useklaar" checked> Use fromboard <P>
    <table>
        <tr>
            <td>inlane:</td><td><input size="4" name="inlane" value="<?PHP print $inlanelast; ?>"/></td>
            <td>analyse:</td><td><input size="4" name="analyze" value="<?PHP print $analyzelast; ?>" /></td>
            <td>implement:</td><td><input size="4" name="implement" value="<?PHP print $implementlast; ?>" /></td>
            <td>documentation:</td><td><input size="4" name="documentation" value="<?PHP print $documentationlast; ?>"/></td>
            <td>peer:</td><td><input size="4" name="peer" value="<?PHP print $peerlast; ?>"/></td>
            <td>test:</td><td><input size="4" name="test" value="<?PHP print $testlast; ?>"/></td>
            <td>done:</td><td><input size="4"  name="done" value="<?PHP print $donelast; ?>"/></td>
        </tr>
    </table>
            <input type="submit" value="Submit"/>
</form>

</body>
</html>

graph.php

<?php
include ("jpgraph/jpgraph.php");
include ("jpgraph/jpgraph_line.php");
$db = new SQLite3('mysqlitedb.db');
$index=0;
$results = $db->query('SELECT inlane FROM ENTRIES');
while ($row = $results->fetchArray()) {
$inlane[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT analyze FROM ENTRIES');
while ($row = $results->fetchArray()) {
$analyze[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT implement FROM ENTRIES');
while ($row = $results->fetchArray()) {
$implement[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT documentation FROM ENTRIES');
while ($row = $results->fetchArray()) {
$documentation[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT peer FROM ENTRIES');
while ($row = $results->fetchArray()) {
$peer[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT test FROM ENTRIES');
while ($row = $results->fetchArray()) {
$test[$index] = $row[0];
     $index++;
}
$index=0;
$results = $db->query('SELECT done FROM ENTRIES');
while ($row = $results->fetchArray()) {
$done[$index] = $row[0];
     $index++;
}
//$datay = array(1,2,4);
$datay1 = array(2,4,4,6,7,4);
$datay2 = array(2,3,4);
$graph = new Graph(600,600,"auto");
$graph->img->SetMargin(40,40,40,40);	
$graph->img->SetAntiAliasing();
$graph->SetScale("textlin");
$graph->SetShadow();
$graph->title->Set("Kanban grapher - A pruts by FASH");
$graph->title->SetFont(FF_FONT1,FS_BOLD);

// Add 10% grace to top and bottom of plot
$graph->yscale->SetGrace(10,10);

//ID|inlane|implement|documentation|peer|test|done

$p1 = new LinePlot($inlane);
$p1->mark->SetType(MARK_FILLEDCIRCLE);
$p1->mark->SetFillColor("red");
$p1->mark->SetWidth(4);
$p1->SetColor("red");
$p1->SetCenter();
$graph->Add($p1);

$p2 = new LinePlot($analyze);
$p2->mark->SetType(MARK_FILLEDCIRCLE);
$p2->mark->SetFillColor("orange");
$p2->mark->SetWidth(4);
$p2->SetColor("orange");
$p2->SetCenter();
$graph->Add($p2);

$p3 = new LinePlot($implement);
$p3->mark->SetType(MARK_FILLEDCIRCLE);
$p3->mark->SetFillColor("yellow");
$p3->mark->SetWidth(4);
$p3->SetColor("yellow");
$p3->SetCenter();
$graph->Add($p3);

$p4 = new LinePlot($documentation);
$p4->mark->SetType(MARK_FILLEDCIRCLE);
$p4->mark->SetFillColor("green");
$p4->mark->SetWidth(4);
$p4->SetColor("green");
$p4->SetCenter();
$graph->Add($p4);

$p5 = new LinePlot($peer);
$p5->mark->SetType(MARK_FILLEDCIRCLE);
$p5->mark->SetFillColor("blue");
$p5->mark->SetWidth(4);
$p5->SetColor("blue");
$p5->SetCenter();
$graph->Add($p5);

$p6 = new LinePlot($test);
$p6->mark->SetType(MARK_FILLEDCIRCLE);
$p6->mark->SetFillColor("purple");
$p6->mark->SetWidth(4);
$p6->SetColor("purple");
$p6->SetCenter();
$graph->Add($p6);

$p7 = new LinePlot($done);
$p7->mark->SetType(MARK_FILLEDCIRCLE);
$p7->mark->SetFillColor("black");
$p7->mark->SetWidth(4);
$p7->SetColor("black");
$p7->SetCenter();
$graph->Add($p7);

$graph->legend->SetLineSpacing(5);

$p1->SetLegend ("inlane"); 
$p2->SetLegend ("analyse"); 
$p3->SetLegend ("implementation"); 
$p4->SetLegend ("documentation"); 
$p5->SetLegend ("peer"); 
$p6->SetLegend ("test"); 
$p7->SetLegend ("done"); 
$graph ->legend->Pos( 0.09,0.09,"left" ,"top");

$graph->Stroke();
?>

insert.php

<?php
$db = new SQLite3('mysqlitedb.db');
//ID|inlane|implement|documentation|peer|test|done
$klaar=$_POST["klaar"];
$inlane=$_POST["inlane"];
$analyze=$_POST["analyze"];
$implement=$_POST["implement"];
$documentation=$_POST["documentation"];
$peer=$_POST["peer"];
$test=$_POST["test"];
$done=$_POST["done"];

$db->exec("DELETE FROM BOARD");
$db->exec("INSERT INTO BOARD  VALUES ($klaar,$inlane,$analyze,$implement,$documentation,$peer,$test,$done)");

$done=$_POST["done"]+$klaar;
$test=$_POST["test"]+$done;
$peer=$_POST["peer"]+$test;
$documentation=$_POST["documentation"]+$peer;
$implement=$_POST["implement"]+$documentation;
$analyze=$_POST["analyze"]+$implement;
$inlane=$_POST["inlane"]+$analyze;

$db->exec("INSERT INTO ENTRIES  VALUES (NULL,$inlane,$analyze,$implement,$documentation,$peer,$test,$done)");
?>
getlast.php
<?php
$db = new SQLite3('mysqlitedb.db');

$results = $db->query('SELECT inlane FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$inlanelast = $row[0];

$results = $db->query('SELECT implement FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$implementlast = $row[0];

$results = $db->query('SELECT documentation FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$documentationlast = $row[0];

$results = $db->query('SELECT peer FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$peerlast = $row[0];

$results = $db->query('SELECT test FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$testlast = $row[0];

$results = $db->query('SELECT done FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$donelast = $row[0];

$results = $db->query('SELECT vanbord FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$klaar = $row[0];

$results = $db->query('SELECT analyze FROM BOARD LIMIT 1;');
$row = $results->fetchArray();
$analyzelast = $row[0];
?>

Jumanji Screen

Vincent showed me a very beautiful “pruts” he made.
It was the Boardgame from Jumanji.
He asked someone from work to write software for the center display.

I really liked the idea, so i made my own version.

I’ve cleaned-up the code and my version is here.
https://media.henriaanstoot.nl/websites/jumanji/index.html

The second page should play a mp3 sound sample, but there is a autoplay issue with some browsers. (I have fixed this on other projects, i will fix this later.