Skip to content
Permalink
Browse files

Added MQTT capability and various management.

One sensor supported presently, should be easy to extend with more sensor
types.
Requires database changes notated in the cron/mqtt.py file.
  • Loading branch information...
paulcsf committed Feb 28, 2019
1 parent fab704f commit a8308ff96d4464fd78d970de4f00e5ec0a3cae41
Showing with 288 additions and 29 deletions.
  1. +162 −0 cron/mqtt.py
  2. +61 −0 db.php
  3. +23 −1 js/request.js
  4. +0 −23 model.php
  5. +17 −5 settingslist.php
  6. +25 −0 st_inc/functions.php
@@ -0,0 +1,162 @@
#!/usr/bin/python
import sys
import time, os, fnmatch, MySQLdb as mdb, logging
import paho.mqtt.client as mqtt
import json
from decimal import Decimal
class bc:
hed = '\033[0;36;40m'
dtm = '\033[0;36;40m'
ENDC = '\033[0m'
SUB = '\033[3;30;45m'
WARN = '\033[0;31;40m'
grn = '\033[0;32;40m'
wht = '\033[0;37;40m'
print bc.hed + " "
print " _____ _ _ _ "
print " | __ \ (_) | | | | "
print " | |__) | _ | |__| | ___ _ __ ___ ___ "
print " | ___/ | | | __ | / _ \ | |_ \_ \ / _ \ "
print " | | | | | | | | | (_) | | | | | | | | __/"
print " |_| |_| |_| |_| \___/ |_| |_| |_| \___|"
print " "
print " "+bc.SUB + "S M A R T H E A T I N G C O N T R O L "+ bc.ENDC
print bc.WARN +" "
print "***********************************************************"
print "* PiHome MQTT Temperature Sensors Data to MySQL DB *"
print "* Use this script if you have MQTT Temperature sensors *"
print "* available to Raspberry Pi. *"
print "* Build Date: 09/02/2019 *"
print "* Have Fun - PiHome.eu *"
print "***********************************************************"
print " " + bc.ENDC
logging.basicConfig(filename='/var/www/cron/logs/MQTT_error.log', level=logging.DEBUG, format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger=logging.getLogger(__name__)


#Database Settings Variables
dbhost = 'localhost'
dbuser = 'pihome'
dbpass = 'pihome2018'
dbname = 'pihome'
print bc.dtm + time.ctime() + bc.ENDC + ' - MQTT Temperature Sensors Script Started'
print "-" * 68

'''
CREATE TABLE `pihome`.`mqtt` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '' ,
`ip` VARCHAR(39) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '127.0.0.1' ,
`port` INT NOT NULL DEFAULT '1883' ,
`username` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '' ,
`password` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '' ,
`enabled` TINYINT NOT NULL DEFAULT '1' ,
`type` INT NOT NULL DEFAULT '0' ,
PRIMARY KEY (`id`)) ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_unicode_520_ci;
INSERT INTO `mqtt` (`id`, `name`, `ip`, `port`, `username`, `password`, `enabled`, `type`) VALUES (NULL, 'Demo', '127.0.0.1', '1883', 'mosquitto', 'mosquitto', '0', '0');
sudo pip install mysqlclient
'''

clients=[]


#Function for Storing DS18B20 Temperature Readings into MySQL
def insertDB(IDs, temperature):
try:
con = mdb.connect(dbhost, dbuser, dbpass, dbname);
cur = con.cursor()
for i in range(0,len(temperature)):
#Check if Sensors Already Exit in Nodes Table, if no then add Sensors into Nodes Table otherwise just update Temperature Readings.
#print "ID %s" % IDs[i]
cur.execute('SELECT COUNT(*) FROM `nodes` where node_id = (%s)', [IDs[i]])
row = cur.fetchone()
row = int(row[0])
if (row == 0):
print bc.dtm + time.ctime() + bc.ENDC + ' - New DS18B20 Sensors Discovered' + bc.grn, IDs[i], bc.ENDC
cur.execute('INSERT INTO nodes (node_id, child_id_1, name, last_seen, ms_version) VALUES(%s,%s,%s,%s,%s)', (IDs[i], '-1', 'Temperature Sensor', time.strftime("%Y-%m-%d %H:%M:%S"), '0'))
con.commit()
#If DS18B20 Sensor record exist: Update Nodes Table with Last seen status.
if (row == 1):
cur.execute('UPDATE `nodes` SET `last_seen`=now() WHERE node_id = %s', [IDs[i]])
con.commit()
cur.execute('INSERT INTO messages_in(node_id, child_id, sub_type, payload, datetime) VALUES(%s, %s, %s, %s, %s)', (IDs[i], '-1', '0', round(temperature[i],2), time.strftime("%Y-%m-%d %H:%M:%S")))
con.commit()
#print bc.dtm + time.ctime() + bc.ENDC + ' - Sensors ID' + bc.grn, IDs[i], bc.ENDC + 'Temperature' + bc.grn, temperature[i], bc.ENDC
print 'Sensor ID' + bc.grn, IDs[i], bc.ENDC + 'Temperature' + bc.grn, temperature[i], bc.ENDC
con.close()
except mdb.Error, e:
logger.error(e)
print bc.dtm + time.ctime() + bc.ENDC + ' - DB Connection Closed: %s' % e


# The callback for when the client receives a CONNACK response from the server.
def on_connect_def(client, userdata, flags, rc):
print("Connected with result code "+str(rc))

# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
# "#" is subscribe to all, just watch traffic.
client.subscribe("#")

# The callback for when a PUBLISH message is received from the server.
def on_message_def(client, userdata, msg):
# We are subscribed to all, just print it for viewing
print("Default: "+msg.topic+" "+str(msg.payload))




# The callback for when the client receives a CONNACK response from the server.
def on_connect_SonoffTasmota(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# "+" is a wildcard for that level, so tele/sonoff1/SENSOR and tele/sonoff2/SENSOR ... will all be received
client.subscribe("tele/+/SENSOR")

# The callback for when a PUBLISH message is received from the server.
def on_message_SonoffTasmota(client, userdata, msg):
temperature = []
IDs = []
jsonObject=json.loads(msg.payload)
for key in jsonObject:
value = jsonObject[key]
if(key[0:8]=="DS18B20-"):
#print("The key and value are ({}) = ({})".format(key, value))
temperature.append(jsonObject[key]['Temperature'])
IDs.append("28-"+jsonObject[key]['Id'])
if (len(temperature)>0):
insertDB(IDs, temperature)




con = mdb.connect(dbhost, dbuser, dbpass, dbname);
cur = con.cursor(mdb.cursors.DictCursor)
cur.execute('SELECT * FROM `mqtt` WHERE `enabled`=1;')
if(cur.rowcount<=0):
print("No enabled MQTT connections. We'll sleep for 30 seconds, quit, and let the system restart us.")
time.sleep(30)
sys.exit()

MQTTCons = cur.fetchall()


for Idx,MQTTCon in enumerate(MQTTCons):
print(MQTTCon)
clients.insert(Idx,mqtt.Client())
if(MQTTCon['type']==0):
clients[Idx].on_connect = on_connect_def
clients[Idx].on_message = on_message_def
elif(MQTTCon['type']==1):
clients[Idx].on_connect = on_connect_SonoffTasmota
clients[Idx].on_message = on_message_SonoffTasmota
else:
print("Unknown MQTT type. Skipping.")
continue
clients[Idx].username_pw_set(MQTTCon['username'], MQTTCon['password'])
clients[Idx].connect(MQTTCon['ip'], MQTTCon['port'], 60)
clients[Idx].loop_start() #start background thread to handle this connection

while True:
#loop forever, letting the background threads and call backs do their work.
time.sleep(1)
61 db.php
@@ -366,5 +366,66 @@
return;
}
}
if($what=="mqtt"){
if($opp=="delete"){
$query = "DELETE FROM `mqtt` WHERE `id`=" . $_GET['wid'] . ";";
if($conn->query($query)){
header('Content-type: application/json');
echo json_encode(array('Success'=>'Success','Query'=>$query));
return;
}else{
header('Content-type: application/json');
echo json_encode(array('Message'=>'Database query failed.\r\nQuery=' . $query));
return;
}
}
if($opp=="add"){
$query = "INSERT INTO `mqtt`
(`id`, `name`, `ip`, `port`, `username`, `password`, `enabled`, `type`) VALUES
(NULL,
'" . $_GET['inp_Name'] . "',
'" . $_GET['inp_IP'] . "',
" . $_GET['inp_Port'] . ",
'" . $_GET['inp_Username'] . "',
'" . $_GET['inp_Password'] . "',
" . $_GET['sel_Enabled'] . ",
" . $_GET['sel_Type'] . ");";
if($conn->query($query)){
header('Content-type: application/json');
echo json_encode(array('Success'=>'Success','Query'=>$query));
return;
}else{
header('Content-type: application/json');
echo json_encode(array('Message'=>'Database query failed.\r\nQuery=' . $query));
return;
}
}
if($opp=="edit"){
$query = "UPDATE `mqtt` SET
`name`='" . $_GET['inp_Name'] . "',
`ip`='" . $_GET['inp_IP'] . "',
`port`=" . $_GET['inp_Port'] . ",
`username`='" . $_GET['inp_Username'] . "',
`password`='" . $_GET['inp_Password'] . "',
`enabled`=" . $_GET['sel_Enabled'] . ",
`type`=" . $_GET['sel_Type'] . "
WHERE `id`=" . $_GET['inp_id'] . ";";
if($conn->query($query)){
header('Content-type: application/json');
echo json_encode(array('Success'=>'Success','Query'=>$query));
return;
}else{
header('Content-type: application/json');
echo json_encode(array('Message'=>'Database query failed.\r\nQuery=' . $query));
return;
}
}
}
?>
<?php if(isset($conn)) { $conn->close();} ?>
@@ -84,7 +84,7 @@ function active_away(){

//update frost temperate
function update_frost(){
var idata="w=frost&o=update";
var idata="w=frost&o=update";
idata+="&frost_temp="+document.getElementsByName("frost_temp")[0].value;
idata+="&wid=0";
$.get('db.php',idata)
@@ -213,3 +213,25 @@ var idata="w=setup_piconnect&o=update&status="+document.getElementById("checkbox
.always(function() {
});
}


function mqtt_delete(wid){
var result = confirm("Confirm delete MQTT server?");
if (!result) return;

var idata="w=mqtt&o=delete";
idata+="&wid="+wid;
$.get('db.php',idata)
.done(function(odata){
if(odata.Success)
$('#ajaxModal').modal('hide')
else
console.log(odata.Message);
})
.fail(function( jqXHR, textStatus, errorThrown ){
if(jqXHR==401 || jqXHR==403) return;
console.log("delete_mqtt: Error.\r\n\r\njqXHR: "+jqXHR+"\r\n\r\ntextStatus: "+textStatus+"\r\n\r\nerrorThrown:"+errorThrown);
})
.always(function() {
});
}
@@ -480,29 +480,6 @@
</div>
</div>';
// systems up time
echo '
<div class="modal fade" id="system_uptime" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h5 class="modal-title">System Uptime</h5>
</div>
<div class="modal-body">
<p class="text-muted"> Raspberry PI up time since last reboot. </p>
<i class="fa fa-clock-o fa-1x red"></i>
';
$uptime = (exec ("cat /proc/uptime"));
$uptime=substr($uptime, 0, strrpos($uptime, ' '));
echo secondsToWords($uptime);
echo '</div>
<div class="modal-footer">
<button type="button" class="btn btn-default login btn-sm" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>';
// backup_image
echo '
@@ -31,14 +31,14 @@
?>
<script language="javascript" type="text/javascript">
$("#ajaxModal").on("show.bs.modal", function(e) {
console.log($(e.relatedTarget).data('ajax'));
//console.log($(e.relatedTarget).data('ajax'));
$(this).find("#ajaxModalLabel").html("...");
$(this).find("#ajaxModalBody").html("Waiting ...");
$(this).find("#ajaxModalFooter").html("...");
$(this).find("#ajaxModalContent").load($(e.relatedTarget).data('ajax'));
});
</script>
<div class="panel panel-primary">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-cog fa-fw"></i> Settings
<div class="pull-right"> <div class="btn-group"><?php echo date("H:i"); ?></div> </div>
@@ -117,6 +117,18 @@
<h3 class="status"></small></h3>
</button>

<button class="btn btn-default btn-circle btn-xxl mainbtn animated fadeIn" data-toggle="modal" data-remote="false" data-target="#ajaxModal" data-ajax="ajax.php?Ajax=GetModal_Services">
<h3 class="buttontop"><small>Services</small></h3>
<h3 class="degre" ><i class="ionicons ion-ios-cog-outline"></i></h3>
<h3 class="status"></small></h3>
</button>

<button class="btn btn-default btn-circle btn-xxl mainbtn animated fadeIn" data-toggle="modal" data-remote="false" data-target="#ajaxModal" data-ajax="ajax.php?Ajax=GetModal_MQTT">
<h3 class="buttontop"><small>MQTT</small></h3>
<h3 class="degre" >MQTT</h3>
<h3 class="status"></small></h3>
</button>

<button class="btn btn-default btn-circle btn-xxl mainbtn animated fadeIn" data-toggle="modal" data-remote="false" data-target="#ajaxModal" data-ajax="ajax.php?Ajax=GetModal_OpenWeather">
<h3 class="buttontop"><small>OpenWeather</small></h3>
<h3 class="degre" ><i class="fa fa-sun-o"></i></h3>
@@ -151,8 +163,8 @@
<h3 class="status"></small></h3>
</button>

<button type="button" class="btn btn-default btn-circle btn-xxl mainbtn animated fadeIn" data-toggle="modal" data-target="#system_uptime">
<h3 class="buttontop"><small>Uptime</small></h3>
<button type="button" class="btn btn-default btn-circle btn-xxl mainbtn animated fadeIn" data-toggle="modal" data-remote="false" data-target="#ajaxModal" data-ajax="ajax.php?Ajax=GetModal_Uptime">
<h3 class="buttontop"><small>Uptime, etc</small></h3>
<h3 class="degre" ><i class="ionicons ion-clock red"></i></h3>
<h3 class="status"></small></h3>
</button>
@@ -215,7 +227,7 @@
</div>
</div>
</div>
</div>
</div>
<?php include("model.php"); ?>
</div>
<!-- /.panel-body -->
@@ -311,4 +311,29 @@ function TempToDB($conn,$T){
}
return round($T,1);
}
function my_exec($cmd, $input='')
{
$proc=proc_open($cmd, array(0=>array('pipe', 'r'), 1=>array('pipe', 'w'), 2=>array('pipe', 'w')), $pipes);
fwrite($pipes[0], $input);fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]);fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]);fclose($pipes[2]);
$rtn=proc_close($proc);
return array('stdout'=>$stdout,
'stderr'=>$stderr,
'return'=>$rtn
);
}
function Convert_CRLF($string, $line_break=PHP_EOL)
{
$patterns = array( "/(\r\n|\r|\n)/" );
$replacements = array( $line_break );
$string = preg_replace($patterns, $replacements, $string);
return $string;
}
?>

0 comments on commit a8308ff

Please sign in to comment.
You can’t perform that action at this time.