add initial project files.

This commit is contained in:
2020-06-09 16:02:25 +09:00
commit b692151385
22 changed files with 2851 additions and 0 deletions

131
lib/DigiTemp.php Normal file
View File

@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Orrisroot;
class DigiTemp
{
/**
* @var string digitemp command path
*/
private string $commandPath;
/**
* @var string config file path
*/
private string $configPath;
/**
* @var array config data
*/
private array $config = [
'TTY' => '',
'READ_TIME' => 0,
'LOG_TYPE' => 0,
'LOG_FORMAT' => '',
'CNT_FORMAT' => '',
'HUM_FORMAT' => '',
'SENSORS' => 0,
'ROM' => [],
];
/**
* constructor.
*
* @param string $commandPath digitemp command path
* @param string $configPath config file path
*/
public function __construct(string $commandPath, string $configPath)
{
$this->commandPath = $commandPath;
$this->configPath = $configPath;
$this->parseConfig();
}
/**
* get number of sensors.
*/
public function getNumSensors(): int
{
return $this->config['SENSORS'];
}
/**
* get sensor id.
*
* @param int $num sensor number
*
* @return ?string
*/
public function getSensorId(int $num): ?string
{
return isset($this->config['ROM'][$num]) ? $this->config['ROM'][$num] : null;
}
/**
* read sensor.
*
* @param int $num sensor number
*
* @return ?float
*/
public function readSensor(int $num): ?float
{
$cmd = escapeshellcmd($this->commandPath.' -c '.$this->configPath.' -q -t '.$num.' -o"%.2C" 2>/dev/null');
exec($cmd, $output, $ret);
if (0 !== $ret) {
return null;
}
return (float) $output[0];
}
/**
* parse config file.
*
* @return false if failure
*/
private function parseConfig(): bool
{
foreach (file($this->configPath) as $line) {
preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\S+/', trim($line), $matches);
if (empty($matches[0])) {
continue;
}
$cols = $matches[0];
$key = array_shift($cols);
$value = array_shift($cols);
switch ($key) {
case 'TTY':
$this->config[$key] = $value;
break;
case 'READ_TIME':
$this->config[$key] = (int) $value;
break;
case 'LOG_TYPE':
$this->config[$key] = (int) $value;
break;
case 'LOG_FORMAT':
$this->config[$key] = '"' === substr($value, 0, 1) ? stripslashes(substr($value, 1, -1)) : $value;
break;
case 'CNT_FORMAT':
$this->config[$key] = '"' === substr($value, 0, 1) ? stripslashes(substr($value, 1, -1)) : $value;
break;
case 'HUM_FORMAT':
$this->config[$key] = '"' === substr($value, 0, 1) ? stripslashes(substr($value, 1, -1)) : $value;
break;
case 'SENSORS':
$this->config[$key] = (int) $value;
break;
case 'ROM':
$this->config[$key][(int) $value] = str_replace('0x', '', implode('', array_reverse($cols)));
break;
default:
return false;
}
}
return true;
}
}

69
lib/Mail/Address.php Normal file
View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Orrisroot\Mail;
class Address
{
const EMAIL_REGEX = '[a-zA-Z0-9]+(?:[_\\.\\-][a-zA-Z0-9]+)*@(?:[a-zA-Z0-9]+(?:[\\.\\-][a-zA-Z0-9]+)*)+\\.[a-zA-Z]{2,}';
/**
* @var string name
*/
private string $name;
/**
* @var string email address
*/
private string $email;
/**
* constructor.
*
* @param string $name name
* @param string $email email address
*
* @throws Exception
*/
public function __construct(string $name, string $email)
{
$this->name = $name;
if (!$this->validateEmail($email)) {
throw new \Exception('Invalid email address found: '.$email);
}
$this->email = $email;
}
/**
* get name.
*
* @return string name
*/
public function getName(): string
{
return $this->name;
}
/**
* get email.
*
* @return string email
*/
public function getEmail(): string
{
return $this->email;
}
/**
* check wheter string is email.
*
* @param string $text email
*
* @return bool false if not email string
*/
public function validateEmail(string $email): bool
{
return false !== preg_match('/^'.self::EMAIL_REGEX.'$/', $email);
}
}

32
lib/Mail/UTF8_Mailer.php Normal file
View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Orrisroot\Mail;
class UTF8_Mailer
{
/**
* send mail.
*
* @param Address $form from email address
* @param array $tos to email addresses
* @param string $subject subject
* @param string $body mail body
*
* @return bool false if failure
*/
public static function sendMail(Address $from, array $tos, string $subject, string $body): bool
{
$mailer = new \PHPMailer\PHPMailer\PHPMailer();
$mailer->CharSet = 'UTF-8';
$mailer->setFrom($from->getEmail(), $from->getName());
$mailer->Subject = $subject;
$mailer->Body = $body;
foreach ($tos as $to) {
$mailer->AddAddress($to->getEmail(), $to->getName());
}
return $mailer->Send();
}
}

188
lib/Rrd/Temperature.php Normal file
View File

@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace Orrisroot\Rrd;
class Temperature
{
/**
* @var string database directory path
*/
private string $databasePath;
/**
* @var array sensor ids
*/
private array $sensorIds;
/**
* constructor.
*
* @param string $fpath database directory path
*/
public function __construct(string $fpath)
{
$this->databasePath = $fpath;
$this->sensorIds = [];
}
/**
* add sensor.
*
* @param string $id sensor id
*/
public function addSensor(string $id)
{
$this->sensorIds[] = $id;
}
/**
* update database.
*
* @param string $id sensor id
* @param int $time timestamp
* @param float $value temperature value
*
* @return bool false if failure
*/
public function update(string $id, int $time, float $value): bool
{
$fpath = $this->getFilePath($id);
if (!file_exists($fpath)) {
if (!$this->_createDatabase($fpath)) {
return false;
}
}
$options = [sprintf('%u:%lf', $time, $value)];
return rrd_update($fpath, $options);
}
/**
* read last data.
*
* @param string $id sensor id
*
* @return ?array last data
*/
public function readLastData(string $id): ?array
{
static $options = [
'LAST',
];
$fpath = $this->getFilePath($id);
$res = rrd_fetch($fpath, $options);
if (false === $res) {
return null;
}
$latest = 0;
$start = $res['start'];
$step = $res['step'];
$data = [];
foreach ($res['data'][$id] as $key => $datum) {
if (!is_nan($datum) && 0 != $datum) {
$data[] = $datum;
$latest = $start + ($key + 1) * $step;
}
}
if (empty($data)) {
return null;
}
$ret['id'] = $id;
$ret['min'] = min($data);
$ret['max'] = max($data);
$ret['average'] = array_sum($data) / count($data);
$ret['latest'] = $data[count($data) - 1];
$ret['timestamp'] = $latest;
return $ret;
}
/**
* create database.
*
* @param string $id sensor id
*
* @return bool false if failure
*/
private function create(string $id): boolean
{
$fpath = $this->getFilePath($id);
$options = [
'--step', '60', // 1min step
sprintf('DS:%s:GAUGE:600:0:100', $this->id), // 10min hartbeat
'RRA:LAST:0.5:1:1440', // last/m - 1day(1440min)
'RRA:LAST:0.5:30:52560', // last/30m - 3years(365day=30min*17520)*3
];
return rrd_create($fpath, $options);
}
/**
* get rrd file path.
*
* @param string $id sensor id
*
* @return string rrd file path
*/
private function getFilePath(string $id): string
{
return $this->databasePath.'/'.$id.'.rrd';
}
/**
* output graph.
*
* @param string $type output graph type
* @param string $fpath output image file path
*
* @return bool false if failure
*/
public function outputGraph(string $type, string $fpath): bool
{
static $colors = [
'#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF',
'#FF9999', '#99FF99', '#9999FF', '#FFFF99', '#99FFFF', '#FF99FF',
];
static $types = ['hour', 'day', 'week', 'month', 'year', '3year'];
if (!in_array($type, $types)) {
return false;
}
$start = '3year' == $type ? '-3year' : '-1'.$type;
$options = [
'--imgformat', 'PNG',
'--lower-limit', '10',
'--upper-limit', '40',
'--start', $start,
'--end', 'now',
'--width', '400',
'--height', '200',
'--units-exponent', '0',
'--vertical-label', "Temperature [\xc2\xb0C]",
'--title', 'Server Room Temperature - by '.$type,
];
$idlen = 0;
foreach ($this->sensorIds as $key => $id) {
if (strlen($id) > $idlen) {
$idlen = strlen($id);
}
$options[] = sprintf('DEF:B%d=%s:%s:LAST', $key, $this->getFilePath($id), $id);
}
$options[] = sprintf('COMMENT: %s Cur\: Min\: Avg\: Max\:', str_repeat(' ', $idlen));
$options[] = sprintf('COMMENT:\l');
foreach ($this->sensorIds as $key => $id) {
$color = $colors[$key % count($colors)];
$options[] = sprintf('LINE2:B%d%s:%s%s', $key, $color, $id, str_repeat(' ', $idlen - strlen($id)));
$options[] = sprintf('GPRINT:B%d:LAST: %%-6.2lf', $key);
$options[] = sprintf('GPRINT:B%d:MIN: %%-6.2lf', $key);
$options[] = sprintf('GPRINT:B%d:AVERAGE: %%-6.2lf', $key);
$options[] = sprintf('GPRINT:B%d:MAX: %%-6.2lf\l', $key);
}
$options[] = sprintf('COMMENT:Last update\: %s\r', str_replace(':', '\:', date('Y-m-d H:i:s T')));
$res = rrd_graph($fpath, $options);
return false !== $res;
}
}