From 5162731a16ab98fad6daf098e38812e56d3c201c Mon Sep 17 00:00:00 2001 From: daniel Tartavel Date: Thu, 30 Dec 2021 16:18:32 +0100 Subject: [PATCH] first commit --- db_functions.php | 123 ++++++++++++++ events.php | 94 +++++++++++ moha.kdev4 | 3 + moha.php | 253 +++++++++++++++++++++++++++++ mqtt_functions.php | 147 +++++++++++++++++ scripts/notificationfreemobile.php | 2 + scripts/panneau_salon.php | 29 ++++ utils.php | 14 ++ 8 files changed, 665 insertions(+) create mode 100644 db_functions.php create mode 100644 events.php create mode 100644 moha.kdev4 create mode 100644 moha.php create mode 100644 mqtt_functions.php create mode 100644 scripts/notificationfreemobile.php create mode 100644 scripts/panneau_salon.php create mode 100644 utils.php diff --git a/db_functions.php b/db_functions.php new file mode 100644 index 0000000..4b281b6 --- /dev/null +++ b/db_functions.php @@ -0,0 +1,123 @@ +friendly_name; + $devices[$topic][$fn] = new device; + $devices[$topic][$fn]->topic = $topic; + $devices[$topic][$fn]->device = $device; + $devices[$topic][$fn]->type = $device->type; + $devices[$topic][$fn]->ieeeAddress = $device->ieee_address; + $devices[$topic][$fn]->friendlyName = $device->friendly_name; + if ( !empty($devices[$topic][$fn]->powerSource ) ) + { + $devices[$topic][$fn]->powerSource = $device->power_source; + } + if ($device->definition != null) + { + $devices[$topic][$fn]->description = $device->definition->description; + } + searchProperty($fn, $devices[$topic][$fn], $device, $listProperties); + $indexDevices[$device->ieee_address] = $device->friendly_name; + } + $dbInit = true; + fwrite($logFh, print_r($devices, true)); + echo "Devices DB made" .EOL; + //print_r($devices); +} + +function searchProperty($fn, $device, $object, $listProperties) +{ + $objectArray = (array)$object; + foreach($listProperties as $key => $value) + { + if (in_array($value, $objectArray)) + { + //echo "$value trouvé =>"; + $device->$key = $value; + //echo $device->$key . EOL; + } + } +} + +function changeDevice($topic, $fn, $device) +{ + //print_r($device); + iterateDevice($topic, $fn, $device, $device->json); +} + +function iterateDevice($topic, $fn, $device, $object) +{ + global $changed; + //echo "device =>";print_r($device);echo EOL; + foreach($object as $key => $value) + { + $oldValue = 0; + //echo "key =>"; print_r($key); echo EOL; + //echo "value =>"; print_r($value); echo EOL; + + //echo "type : " . gettype($value) .EOL; + if (gettype($value) == "object") + { + //echo " is Object" . EOL; + if (!property_exists($device, $key)) + { + $device->{$key} = null; + } + //echo "iterating" . EOL; + iterateDevice($topic, $fn, $device, $value); + }elseif (gettype($value) == "array") + { + //echo "is array" . EOL; + + iterateDevice($topic, $fn, $device, $value); + }else + { + if (empty($device->$key) or $value != null) + { + if (isset($device->$key)) $oldValue = $device->$key; + if ($oldValue != $value) + { + $device->{$key} = $value; + $changed[$fn]["key"] = $key; + $changed[$fn]["value"] = $value; + + //echo "oldvalue => " . print_r($oldValue, true) . EOL; + if (empty($oldValue)) + { + echo "Initializing " . $key; + }else + { + echo "changed " . $key . " value " . $oldValue;; + } + echo " to " . $value . EOL; + } + //print_r($device->functions); + if (!empty($device->functions)) + { + echo "executing notifications functions " . EOL; + foreach($device->functions as $function) + { + //print_r($function); + $function($topic, $fn, $key, $value); + } + } + } + } + } +} + + +?> diff --git a/events.php b/events.php new file mode 100644 index 0000000..d943a65 --- /dev/null +++ b/events.php @@ -0,0 +1,94 @@ + checking events" . EOL; + foreach ($events as $key => $event) + { + if ($event->dateTimeEvent <= now()) + { + echo "---->sending command" . EOL; + publish($devices[$event->topic][$indexDevices[$event->ieeeAddress]], array($event->param => $event->value), "set", $key); + //$event->published = now(); + unset($key); + } + } + //print_r($events); +} + +function setDelay(&$device, $delay, $unit="sec", $param, $value, $replace=false) +{ + global $events; + $datetime = new dateTime(); + //print_r($datetime); + switch($unit) + { + case "sec": + $unit = "S"; + break; + case "min": + $unit = "M"; + break; + case "hour": + $unit = "H"; + break; + case "day": + $unit = "D"; + break; + case "week": + $unit = "W"; + break; + case "month": + $unit = "M"; + break; + case "year": + $unit = "Y"; + break; + } + $datetime->add(new DateInterval('PT'. $delay . $unit)); + //print_r($device); + if ($replace) + { + $eventKey = searchEvent($device, $param, $value); + if ($eventKey !== false) deleteEvent($eventKey); + } + //$dt = $datetime->format("Y-m-d\TH:i:s\Z"); + $events[] = new event; + $key = key($events); + $events[$key]->dateTimeEvent = $datetime; + $events[$key]->ieeeAddress = $device->ieeeAddress; + $events[$key]->topic = $device->topic; + $events[$key]->param = $param; + $events[$key]->value = $value; + echo "new event"; +} + +function searchEvent($device, $param , $value) +{ + global $events; + echo "searching event" . EOL; + //$keys = array_keys($events, $device->ieeeAddress); + echo "ieee_address =>" . $device->ieeeAddress . EOL; + print_r($events); + foreach($events as $key => $event) + { + //echo "Event : $event => $value" . EOL; + echo "===>";print_r($event); echo EOL; + if($event->topic == $device->topic and $event->param == $param and $event->value == $value and $event->ieeeAddress == $device->ieeeAddress) + { + echo "==============================\nfound " . $key . "\n=================================" . EOL; + return $key; + } + } +} + +function deleteEvent($eventKey) +{ + global $events; + unset ($events[$eventKey]); + echo "delete event " . $eventKey . EOL; +} + + +?> diff --git a/moha.kdev4 b/moha.kdev4 new file mode 100644 index 0000000..b737c75 --- /dev/null +++ b/moha.kdev4 @@ -0,0 +1,3 @@ +[Project] +Name=moha +Manager=KDevGenericManager diff --git a/moha.php b/moha.php new file mode 100644 index 0000000..98192c4 --- /dev/null +++ b/moha.php @@ -0,0 +1,253 @@ + "batterie" ); + +class Message +{ + public $id; + public $state = false; + public $msg; +} + +class topic { + public $mid; + public $status; + public $info; + public $devices; + public $groups; + public $extensions; + public $config; +} + +class device +{ + public $topic; + public $device; + public $json; + public $ieeeAddress; + public $friendlyName; + public $type; + public $powerSource; + public $description; + public $functions; + public $payload; + + public function set($event) + { + publish($this, $this->payload, "set", $event); + } +} + +class event +{ + public $ieeeAddress; + public $topic; + public $param; + public $value; + public $published; + public $dateTimeEvent; + public $recurrenceDay; + public $recurrenceMonth; + public $recurrenceYear; + public $recurrenceWeek; + public $recurrenceHours; + public $recurrenceMinutes; + public $time; + public $day; + public $week; + public $month; + public $exceptionInterval; +} + +class interval +{ + public $startDate; + public $endDate; +} + +class notificationMethod +{ + public $url; + public $msg; + + function __construct($url) + { + $this->url = $url; + } + + public function send($text=null) + { + if (empty($text)) $text= $this->msg; + if (!empty($msg)) + { + $opts = array( + 'http'=>array( + 'method'=>"GET", + 'header'=>"Accept-language: fr\r\n" + ) + ); + $context = stream_context_create($opts); + + /* Envoi une requête HTTP vers $url + avec les en-têtes additionnels ci-dessus */ + $fp = fopen($url . "/" . $msg, 'r', false, $context); + $response = stream_get_contents($fp, -1, 0); + fclose($fp); + }else + { + // TODO log_error("notificationMethod : $msg is null"); + } + + } +} + +//global variables +$topics = array(); // list of topics +$mids = array(); // list of message IDs +$logFh = null; // filehandle of log file +$devices = array(); // array of device objetcs +$changed = array(); // list of changed devices +$notificationMethods = array(); // array of notification methods objects +$indexDevices = array(); // index devices by ieee_address +$dbInit = false; // flag to indicate that desvices db is initialized +$connected = false; // connected to MQTT server +$nSubscribed = 0; // Number of topics subsribed +$included = false; // flag indicate scripts are loaded +$events = array(); // list of event objects + +// topics definition +$topics["zigbee2mqtt"] = new topic; + +// gettext +bindtextdomain("moha", "./locale"); +textdomain("moha"); + +//signal handling +pcntl_signal(SIGTERM, 'signalHandler');// Termination ('kill' was called) +pcntl_signal(SIGHUP, 'signalHandler'); // Terminal log-out +pcntl_signal(SIGINT, 'signalHandler'); + + + +$client = new Mosquitto\Client(); +if (!init()) exit(1); +$client->onConnect('connectResponse'); +$client->onDisconnect('disconnectResponse'); +$client->onSubscribe('subscribeResponse'); +$client->onMessage('message'); +$client->onLog('logger'); +$client->onPublish('publishResponse'); + +$include 'mqtt_functions.php'; + +foreach($topics as $name => $topic) +{ + //echo $name; + $topic->mid = $client->subscribe($name . "/#", 2); + $mids[$topic->mid] = $name; + $topic->status = false; +} + +while (true) +{ + $client->loop(); + if ($dbInit and ! $included) + { + $hooks = loadHooks("./scripts"); + if (!empty($hooks)) + { + foreach ($hooks as $hook) + { + include $hook; + } + } + }elseif($dbInit and $included) + { + checkEvents(); + } +} + +endMoha(); + +function init() +{ + global $logFh, $client; + date_default_timezone_set('Europe/Paris'); + $client->connect("192.168.1.253", 1883, 5); + if (! $logFh = fopen("moha.log", "w") ) + { + echo _("error opening log file"); + return false; + } + return true; +} + +function loadHooks($dir) +{ + global $included; + $hookList = array(); + $files = scandir($dir); + //print_r($files); + foreach ($files as $file) + { + //echo " $file" . EOL; + if ($file != "." and $file != ".." and strpos($file, "~", -2) === false) + { + //echo "not . or .." . EOL; + if (is_dir($file)) + { + //echo "directory" . EOL; + listHooks($dir . '/' . $file); + }else + { + //echo "file" . EOL; + $hookList[] = $dir . "/" . $file; + } + } + } + //print_r($hookList); + $included = true; + return $hookList; +} + + + +function endMoha() +{ + global $topics, $nSubscribed ,$client; + $x = 0; + foreach($topics as $topic => $object) + { + if ($object->status) + { + $client->unsubscribe($topic); + } + } + + while ($nSubscribed != 0) + { + //echo $nSubscribed;0x00124b0022ebac5c + if ( $x++ > 30) exit (0); + $client->loop(); + } + $client->disconnect(); + fclose($logFh); +} + + + +?> diff --git a/mqtt_functions.php b/mqtt_functions.php new file mode 100644 index 0000000..c984cec --- /dev/null +++ b/mqtt_functions.php @@ -0,0 +1,147 @@ +topic); + if ($topic[1] == "bridge") + { + switch ($topic[2]) + { + case "info": + $topics[$topic[0]]->info = json_decode($message->payload); + break; + case "devices": + $topics[$topic[0]]->devices = json_decode($message->payload); + mkDevicesDB($topic[0], $topics[$topic[0]]->devices); + fwrite($logFh, print_r($topics[$topic[0]]->devices, true)); + break; + case "groups": + $topics[$topic[0]]->groups = json_decode($message->payload); + mkDevicesDB($topic[0], $topics[$topic[0]]->groups); + break; + case "extensions": + $topics[$topic[0]]->extensions = json_decode($message->payload); + break; + case "config": + $topics[$topic[0]]->config = json_decode($message->payload); + break; + case "logging": + //TODO + break; + case "state": + $topics[$topic[0]]->state = $message->payload; + break; + default: + break; + }; + }elseif (($topic[array_key_last($topic)]) != "get" and ($topic[array_key_last($topic)]) != "set") + { + $topic = explode ("/", $message->topic, 2); + + echo $topic[0] . " => " . $topic[1] . EOL; + $devices[$topic[0]][$topic[1]]->json = json_decode($message->payload); + changeDevice($topic[0], $topic[1], $devices[$topic[0]][$topic[1]]); + //fwrite($logFh, print_r($msg, true)); + } +} + +// payload is an array : +// key is parameter => value is value of parameter + +function publish($device, $payload, $commande="set", $eventKey) +{ + global $client, $mids, $logFh; + print_r($payload); + $string = $device->topic . "/" . $device->friendlyName . "/" . $commande; + $mid = $client->publish($string, json_encode($payload) , 2); + if ($mids[$mid]) + { + unset ($mids[$mid]); + }else + { + $mids[$mid] = true; + } + echo $string . " =>>>>>> " . json_encode($payload) . EOL; + logger(LOG_INFO, $logFh, "Publishing " . $string . " with payload => " . json_encode($payload)); +} + +function connectResponse($r, $message) +{ + global $connected; + echo _("I got code ") . $r . _(" and message : '") . $message . "'" . EOL; + switch ($r) + { + case 0: + echo _("Successfull connection") . EOL; + return true; + break; + case 1: + echo _("Connection refused : unacceptable protocol version") . EOL; + return false; + break; + case 2: + echo _("Connection refused : identifier rejected") . EOL; + return false; + break; + case 3: + echo _("Connection refused (broker unavailable )") . EOL; + return false; + break; + } +} + +function subscribeResponse($mid, $qosCount) +{ + global $topics, $mids, $nSubscribed; + $key = $mids[$mid]; + echo _("Subscribed to ") . $key . EOL; + $topics[$key]->status = true; + $nSubscribed += 1; +} + +function unsubscribeResponse($mid) +{ + global $topics, $mids, $nSubscribed; + $key = $mids[$mid]; + echo _("Unsubscribed from ") . $topics[$key]->name . EOL; + $topics[$key]->status = false; + $nSubscribed -= 1; +} + +function disconnectResponse($r) +{ + if (! $r) + { + echo _('Badly '); + }else + { + echo _('Cleanly '); + } + echo _("disconnected from server") . EOL; +} + +function publishResponse($mid) +{ + global $mids, $events; + logger(LOG_INFO, "Event with mid = " . $mid . " published by MQTT broker"); + if (isset($mids[$mid])) + { + unset ($mids[$mid]); + }else + { + $mids[$mid] = true; + } +} + +function logger($level, $log) +{ + global $logFh, $logLevel; + if ( $level >= $logLevel) + { + fwrite($logFh, "$level : $log" . EOL); + print ("$level : $log" . EOL); + } +} + +?> diff --git a/scripts/notificationfreemobile.php b/scripts/notificationfreemobile.php new file mode 100644 index 0000000..d676a3d --- /dev/null +++ b/scripts/notificationfreemobile.php @@ -0,0 +1,2 @@ + "ON"); + $device = $devices[Z2M][$indexDevices["0x588e81fffe343e8f"]]; + $device->set($msg); + setDelay($device, $delay, $timeUnit, $param, "OFF", true); + } + break; + } + echo _("notification received from MQTT") . EOL; + echo $param . "=> " . $value . EOL; + }; + + //assignation of the function to the devices + $devices[Z2M][$indexDevices["0x00124b0022ebac5c"]]->functions[] = $rdcPanneau; + $devices[Z2M][$indexDevices["0x588e81fffe2cf695"]]->functions[] = $rdcPanneau; +?> diff --git a/utils.php b/utils.php new file mode 100644 index 0000000..836ef4c --- /dev/null +++ b/utils.php @@ -0,0 +1,14 @@ +