In het kader van de
home automation had ik de keukenlamp al via een
Shelly1 gekoppeld. Daar kun je leuke, maar ook gewoon handige dingen mee doen. Zo zit de schakelaar van de keukenlamp hier aan de wooonkamerzijde, dus als je door de buitendeur binnen komt (met je vieze schoenen; aan de andere kant van de keuken) moet je eerst in het donker je schoenen uittrekken, voordat je de lamp aan kunt doen. Via de Shelly heb ik dit ondervangen door de keukenlamp ook automatisch aan te doen als de bewegingsmelder in de achtertuin aan gaat (en het donker is).
Dat lost het probleem echter "van buiten naar binnen" op. Als je naar buiten gaat, met de lamp aan, en smerige schoenen aan je voeten, kun je de lamp niet meer uit doen. Hiervoor moet je dan een thuisblijver aanwijzen, of de Shelly even "via de app" schakelen (gedoe als je al handschoenen aan hebt). Enorm first world problem dus. Gelukkig zitten er bij de keukendeur een viertal schakelaars voor de buitenlampen, waarvan er een niet gebruikt werd. Deze laatste heb ik nu aan
nog een Shelly gehangen, en weer teruggekoppeld naar de eerste Shelly. Nu kunnen we de keukenlamp dus vanaf twee plekken bedienen - hoezee! Eigenlijk net zoiets als de gemiddelde
wisselschakeling voor het trappengat, maar dan stukken nodeloos complexer.
Alhoewel: het biedt natuurlijk ook wel weer meer mogelijkheden. Zo kun je nu met "dubbel klik" (snel aan/uit) de "flood light" onder de dakgoot bedienen, en zullen er binnenkort vast nog wel meer "handigheidjes" volgen. Tijd om de handleiding voor het huis weer eens bij te werken ...
Rob, zaterdag 27 april 2024, 17:52
Werktechnisch zit ik met bakken data (denk terabyte) te kijken die we bijna nooit nodig hebben - alleen om bij "problemen" dingen uit te zoeken. Daarnaast is het ook nog niet in een vast formaat (geen vaste veldindeling). Teveel en te onhandig dus om een database mee lastig te vallen, maar alles zomaar in een file dumpen maakt het gestructureerd doorzoeken er ook niet handiger op. Enter
JSON lines: een text based format met op elke regel een JSON object. Een beetje een soort CSV zonder vaste kolomindeling. En daar moet je dan wat mee:
<?php
namespace Rsi\Object;
/**
* Manipulate a JSON lines file.
*/
class JsonLinesFile{
public $delimiter = "\n"; //!< Line delimiter.
public $assoc = true; //!< Return data as assoc.array.
protected $_filename = null;
public function __construct($filename){
$this->_filename = $filename;
}
/**
* Append data to the file.
* @param mixed $data
* @return bool True on success.
*/
public function append($data){
return file_put_contents($this->_filename,json_encode($data) . $this->delimiter,LOCK_EX | FILE_APPEND) !== false;
}
/**
* Clear (empty) the file.
* @return bool True on success.
*/
public function clear(){
return file_put_contents($this->_filename,null) !== false;
}
/**
* Walk through all lines in the file.
* @param callable $callback Function to call for each line. The line is in the first parameter, and the index in the file
* (base-0) in the second.
*/
protected function lines($callback){
if(is_file($this->_filename)){
$f = fopen($this->_filename,'r');
$index = 0;
while(!feof($f)) if(strlen($line = fgets($f)) && (call_user_func($callback,$line,$index++) === false)) break;
fclose($f);
}
}
/**
* Number of records in the file.
* @return int
*/
public function count(){
$count = 0;
$this->lines(function() use (&$count){
$count++;
});
return $count;
}
/**
* Decode a line.
* @param string $line Encoded JSON line.
* @return mixed Decoded data.
*/
protected function decode($line){
if((($data = json_decode($line,$this->assoc)) === null) && (json_last_error() != JSON_ERROR_NONE))
throw new \Exception(json_last_error_msg());
return $data;
}
/**
* Walk through all records in the file.
* @param callable $callback Function to call for each record. The data is in the first parameter, and the index in the file
* (base-0) in the second. Return false to break.
*/
public function each($callback){
$this->lines(function($line,$index) use ($callback){
return call_user_func($callback,$this->decode($line),$index);
});
}
/**
* Retrieve a slice of the records in the file.
* @param $offset int Record to start from.
* @param $length int Number of records to return (when available).
* @return array The selected records (key = index, value = data).
*/
public function slice($offset,$length){
$length += $offset;
$result = [];
$this->lines(function($line,$index) use ($offset,$length,&$result){
if($index >= $offset){
if($index < $length) $result[$index] = $this->decode($line);
else return false; //break
}
});
return $result;
}
/**
* Filter the records in the file.
* @param callable $callback If this function returns true, the record is preserved. The data is in the first parameter, and
* the index in the file (base-0) in the second.
* @return bool True on success.
*/
public function filter($callback){
$f = fopen($temp = $this->_filename . '-' . bin2hex(random_bytes(16)),'w');
try{
$this->lines(function($line,$index) use ($callback,$f){
if(call_user_func($callback,$this->decode($line),$index)) fputs($f,$line);
});
fclose($f);
return rename($temp,$this->_filename);
}
catch(\Exception $e){
unlink($temp);
throw $e;
}
}
/**
* Edit the records in the file.
* @param callable $callback Function that returns the modified record. The original data is in the first parameter, and the
* index in the file (base-0) in the second. Return false to remove the record, or true to keep the original record.
* @return bool True on success.
*/
public function edit($callback){
$f = fopen($temp = $this->_filename . '-' . bin2hex(random_bytes(16)),'w');
try{
$this->lines(function($line,$index) use ($callback,$f){
if(($data = call_user_func($callback,$this->decode($line),$index)) !== false)
fputs($f,$data === true ? $line : json_encode($data) . $this->delimiter);
});
fclose($f);
return rename($temp,$this->_filename);
}
catch(\Exception $e){
unlink($temp);
throw $e;
}
}
}
Rob, zaterdag 27 april 2024, 22:18