Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- # Keywords:
- # Raspberry Pi, w1thermsensor, temperature control and monitoring, multiple sensors in parallel, pulse width mo control
- # high current SCR (40amp)
- # ref web site www.smalle.uk
- # for use with Python 3
- # March 15th 2018 working byt sensor acquiring needs debugging- will probably sort later in the month.
- # As of today March 16th can be seen working on http://www.smalle.uk/temps
- # Copyright 2018 <djgtorrens@gmail.com>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- # MA 02110-1301, USA.
- print("Running combined02.py")
- #additional imports for local display and sauna
- import myzero01 as led
- import RPi.GPIO as GPIO
- # socket is for smartplug control
- import socket
- # all other imports for rest of program
- import time
- import subprocess
- import datetime
- import csv
- # For Python 3
- import configparser
- # For Python 2.x would be
- #import ConfigParser as configparser
- #but this prog no good on Python 2
- from ftplib import FTP
- from os import listdir
- from os import path
- from os import fsync
- import sys, getopt
- import webbrowser
- from shutil import copyfile
- #Use with
- #copyfile(src, dst)
- # Import w1thermsensor
- from w1thermsensor import W1ThermSensor
- # data class "config" is used to hold configuration information
- # the values set in initializing are the default values
- # If no configuration file is found these values are writen to a file "config.cfg"
- # put in same directory as the python file.
- # the line
- # " config.prog_path = path.dirname(path.realpath(__file__)) + "/" "
- # using "path" from "os" module.
- # is used to get where to look for and put this file.
- class class_config:
- def __init__(self):
- self.scan_delay = 1 # delay in seconds between each scan (not incl sensor responce times)
- self.max_scans = 3 # number of scans to do, set to zero to scan for ever (until type "ctrl C")
- self.log_directory = "/log/" # where to send log files
- self.ftp_credentials_filename = 'ftp_credentials.csv' #
- self.ftp_credentials_log_filename = 'ftp_credentials_log.csv'
- self.ftp_credentials_status_filename = 'ftp_credentials_status.csv'
- self.ftp_credentials_log_html_filename = 'ftp_credentials_log_html.csv'
- self.mount_point = "/rtr/" # the location to mount the share (must exist) e.g. /home/pi/rtr
- self.test_file = "test.txt" # a file that should be present at the share if its already mounted e.g. test.txt
- self.mount_arg1 = "sudo" # first part of mount command (usually sudo)
- self.mount_arg2 = "/etc/mount_log.sh" # second part of mount command (usualy name of script to run)
- self.delay_limit = 2 # Number of Seconds delay between temperature scans
- self.delay_increment = 2 # Number of scans to do, Zero for only stopped by Ctrl C on Keyboard
- self.ftplog = 2 # Number of Value Changes before Log File is Saved to remote website
- self.heaterIPa = '192.168.111.100' # IP for First Heater
- self.heaterIPb = '192.168.111.108' # IP for Second Heater
- self.sensor4readings = '28-0315a80584ff' #The code for the sensor to be used to measure room temperature
- self.change4log = 0.6 # change in temperature required before logging and displaying etc
- self.control_hysteresis = 1
- self.default_target = 70 # Initial Default temperature target e.g for Sauna
- self.precision = 12 # default precision is 12 bit
- # These parameters are not saved to the config.cfg file
- # First three use the program pathname
- self.prog_path = ""
- self.config_filename = ""
- self.sensor_info_filename = ""
- self.logging_filename_save_as = ""
- self.logging_filename = ""
- self.html_filename = ""
- self.status_filename = ""
- self.log_html_filename = ""
- self.local_www_html_filename = ""
- self.local_www_log_html_filename = ""
- self.local_www_status_htlm_filename = ""
- self.local_www_log_csv = ""
- self.logging_on = False
- self.sensor_present = False
- self.logging_outfile = ""
- self.scan_count = 0
- self.ftplog_count = 0
- self.ref_sensor_index = 0
- self.heater1_on = 0
- self.heater2_on = 0
- self.one_first = 1
- self.last_target = 0
- self.number_seen = 0
- # SENSORS
- class class_my_sensors:
- def __init__(self):
- self.number = [] # number designation of sensor to use for display
- self.code = [] # the code that the sensor is internally programmed with
- self.connected = [] # true/false flag indicating if his sensor is connected obtained by scanning for its file
- self.reading = [] # the last temperature reading read from the sensor in degrees Centigrade,
- # wil be a negative value < -100 if there is an error in reading
- self.last_logged = [] # the value last logged for that sensor
- self.code_seen = [] # a trie/false flag indicating that this senso code has been seen during this run
- self.code_seen_but_disconnected = [] # Flag for when we have seen a sensor then its disconnected
- self.location = [] # text read in from the sensor data file for the sensors location
- self.stype = [] # text read in from the sensor data file for the sensors type
- self.comment = [] # text read in from the sensor data file for a comment
- self.delay = [] # if the sesor is not responding, maybe has become disconnected
- self.error_number = [] # then its file will still be present for a while and this number is
- self.last_logged_error_number = [] # Last logged error
- self.status_text = [] # used to count down before retrying, initially set to the observed delay
- # then 0.5 subtracted each scan until value less than 0.5.
- # delay is usually around 2 secosnds so it will be about 3 scans before another attempt is made.
- class class_schedule:
- def __init__(self):
- self.index = [] # Index of the array holding the Temperature Schedule
- self.year = [] # Year
- self.month = [] # Month
- self.day = [] # Day
- self.hour = [] # Hour
- self.minute = [] # Minute
- self.target_temp = [] # Target Temperature
- class class_smartplug():
- def __init__(self,number_of_plugs):
- # NOTE Set for 2 plugs,
- # must introduce way to set number if need more
- self.state = [1.234]*number_of_plugs # state on is "1" off "0"
- self.ip = ["text"]*number_of_plugs # ip address
- self.current = [1.234]*number_of_plugs # current
- self.voltage = [1.234]*number_of_plugs # voltage
- self.power = [1.234]*number_of_plugs # power now
- self.total = [1.234]*number_of_plugs # total power (today ?)
- self.error = [1.234]*number_of_plugs # error code
- self.sent = ["command"]*number_of_plugs # last command sent
- self.received = ["reply"]*number_of_plugs # last reply received
- class textbffr(object):
- # Rotating Buffer Class
- # Initiate with just the size required Parameter
- # Get data with just a position in buffer Parameter
- def __init__(self, size_max):
- #initialization
- self.size_max = size_max
- self._data = [""]*(size_max)
- self.posn = self.size_max-1
- def replace(self, value):
- #replace current element
- self._data[self.posn] = value
- def append(self, value):
- #append an element
- if self.posn == self.size_max-1:
- self.posn = 0
- self._data[self.posn] = value
- else:
- self.posn += 1
- self._data[self.posn] = value
- def __getitem__(self, key):
- #return stored element
- if (key + self.posn+1) > self.size_max-1:
- return(self._data[key - (self.size_max-self.posn-1)])
- else:
- return(self._data[key + self.posn+1])
- # **********************************
- # Start of small service functions *
- # **********************************
- def in_GUI_mode():
- mode = 1
- try:
- if sys.stdin.isatty():
- mode = 0
- except AttributeError: # stdin is NoneType if not in terminal mode
- pass
- if mode == 0:
- #in terminal mode
- return(False)
- else:
- #in gui mode ...
- return(True)
- def list_files(path,exclude):
- # List all files in path "path" bu exclude items matching "exclude"
- # Result is returned as a list
- here = "list_files"
- emptylist= []
- files = []
- try:
- seen_files = listdir(path)
- pr(here,"Number of file found : ", str(len(seen_files)))
- for ind in range(0,len(seen_files)):
- if (seen_files[ind] == exclude) or (seen_files[ind][:2] == "00"):
- pr(here,"seen_file[" + str(ind)+"] is not OK : ",seen_files[ind])
- else:
- pr(here,"seen_file[" + str(ind)+"] is OK : ",seen_files[ind])
- files.append(seen_files[ind])
- return(files)
- except:
- pr_status(True,0,"Error in subroutine list_files")
- return(emptylist)
- def pt(where, message):
- global debug
- # routine for use debugging time taken each stage of program
- if debug:
- print("debug(pt) in: " , where , " : ", message, " at : " , str(datetime.datetime.now()), "\n")
- return
- def pr(where,message,var_val):
- global debug
- # routine for debugging that prints message then a variables value
- if debug:
- print("debug(pr) in : ", where , " : ", message, str(var_val))
- return
- def pr_f(where,message,var_val):
- global debug_ftp
- # routine for debugging ftp that prints message then a variables value
- if debug_ftp:
- print("debug_ftp(pr) in : ", where , " : ", message, str(var_val))
- def pr_w1(where,message,var_val):
- global debug_w1
- # routine for debugging when want to check operation of w1thermsensor only
- if debug_w1:
- print("debugW1 in : ", where , " : ", message, str(var_val))
- return
- def pr_status(appnd,ref,message):
- global status_bffr
- global last_ref
- here = "pr_status"
- # print to screen and to status log and update html file
- print(str(config.scan_count), " : ", message)
- if appnd:
- status_bffr.append(str(config.scan_count) + " : " + make_time_text(datetime.datetime.now()) + " : " + message)
- else :
- if ref == last_ref:
- status_bffr.replace(str(config.scan_count) + " : " + make_time_text(datetime.datetime.now()) + " : " + message)
- else:
- status_bffr.append(str(config.scan_count) + " : " + make_time_text(datetime.datetime.now()) + " : " + message)
- write_html(config.status_filename,status_bffr)
- try:
- copyfile(config.status_filename, config.local_www_status_htlm_filename)
- except:
- pr_log (True,"Fail with send html file to " + config.local_www_status_htlm_filename)
- pr_f(here, "FTP for Status file File names : ", config.ftp_credentials_status_filename + " : " + config.status_filename + " : " + "use_cred")
- ftp_result = send_by_ftp(config.ftp_credentials_status_filename, config.status_filename, "use_cred")
- # next lines in effect when "f" option selected
- for pres_ind in range(0, len(ftp_result)):
- pr_f(here, str(pres_ind) + " : ", ftp_result[pres_ind])
- last_ref = ref
- return
- def pr_log(appnd,message):
- global log_bffr
- here = "pr_log"
- # print to screen and to status log and update html file
- print(str(config.scan_count) + " : ",message)
- if appnd :
- log_bffr.append(str(config.scan_count) + " : " + message)
- else:
- log_bffr.replace(str(config.scan_count) + " : " + message)
- write_html(config.log_html_filename,log_bffr)
- try:
- copyfile(config.log_html_filename, config.local_www_log_html_filename)
- except:
- pr_status(True,0, "Fail with send html file to " + config.local_www_log_html_filename)
- pr_f(here, "FTP for log html file File names : ", config.ftp_credentials_log_html_filename + " : " + config.log_html_filename + " : " + "use_cred")
- ftp_result = send_by_ftp(config.ftp_credentials_log_html_filename, config.log_html_filename, "use_cred")
- # next lines in effect when "f" option selected
- for pres_ind in range(0,len(ftp_result)):
- pr_f(here, str(pres_ind) + " : ",ftp_result[pres_ind])
- return
- def fileexists(filename):
- #This checks for file but does not detect disconnected sensor
- try:
- with open(filename): pass
- except IOError:
- return False
- return True
- def mount_log_drive(mount_point,test_file,mount_arg1,mount_arg2):
- # NOT IN USE AT THE MOMENT IN thermoxx.py
- # mount_point : the location to mount the share (must exist) e.g. /rtr
- # test_file : a file that should be present at the share if its already mounted e.g. test.txt
- # mount_arg1 : first part of mount command (usually sudo)
- # mount_arg2 : second part of mount command (usualy name of script to run such as 'sudo /etc/mount_log.sh')
- # typical script command "sudo mount -t cifs //192.168.1.1./log /home/pi/rtr -o credentials=/etc/mountcred,sec=ntlm
- # credentials_file: a file containing the username and password (located in \etc) for the share e.g. mountcred
- # typical credentials file:
- # username=fredblogs
- # password=reallysecurepassword
- #check to see if the network drive for logging is mounted, if not then mount it
- here = "mount_log_drive"
- if (fileexists(mount_point + test_file)):
- pr_status(True,0, "Log Drive already mounted because " + mount_point + test_file + " exists")
- return ("Log Drive already mounted\n")
- else:
- pr_status(True,0, "Will use mount command: " + mount_arg1 + " " + mount_arg2 + "\n")
- subprocess.call([mount_arg1,mount_arg2]) # e.g. applies 'sudo /etc/mount_log.sh' as two parts of the command
- return("Log Drive now mounted\n")
- def show_html(html_filename):
- # open a file in the default program
- here = "show_html"
- pr(here, "Show file at this url : ", " file://" + html_filename)
- url = "file://" + html_filename
- webbrowser.open(url,new=2) # new=2 signals new tab
- def print_bffr(bffr):
- #print to screen contaents of a buffer
- for ind in range(bffr.size_max-1,-1,-1):
- stored = bffr[ind]
- if stored != "":
- print(stored)
- print( '\n' )
- def write_html(html_filename,bffr):
- #send contemts of buffer to website
- with open(html_filename,'w') as htmlfile:
- htmlfile.write("<p>" + html_filename + " : " + make_time_text(datetime.datetime.now()) + "</p>")
- for ind in range(bffr.size_max-1, -1,-1):
- htmlfile.write("<p>" + bffr[ind] + "</p>")
- def make_time_text(time_value):
- #make a time stamp in format mm:dd hr:mn:sc
- return(str(time_value.month).zfill(2) + "_" + str(time_value.day).zfill(2) + "__"
- + str(time_value.hour).zfill(2) + "_" + str(time_value.minute).zfill(2) +"_"
- + str(time_value.second).zfill(2))
- # ***********************************
- # End of small service functions *
- # **********************************
- # **********************************
- # Start of local display functions *
- # **********************************
- def date(seconds):
- device = led.sevensegment()
- deviceId = 0
- device.clear()
- for count in range(seconds):
- now = datetime.datetime.now()
- day = now.day
- month = now.month
- year = now.year - 2000
- # Set day
- device.letter(deviceId, 8, int(day / 10)) # Tens
- device.letter(deviceId, 7, day % 10) # Ones
- device.letter(deviceId, 6, '-') # dash
- # Set day
- device.letter(deviceId, 5, int(month / 10)) # Tens
- device.letter(deviceId, 4, month % 10) # Ones
- device.letter(deviceId, 3, '-') # dash
- # Set day
- device.letter(deviceId, 2, int(year / 10)) # Tens
- device.letter(deviceId, 1, year % 10) # Ones
- time.sleep(1)
- device.clear()
- def clock(seconds):
- device = led.sevensegment()
- deviceId = 0
- device.clear()
- for count in range(seconds):
- now = datetime.datetime.now()
- hour = now.hour
- minute = now.minute
- second = now.second
- dot = second % 2 == 0 # calculate blinking dot
- # Set hours
- device.letter(deviceId, 4, int(hour / 10)) # Tens
- device.letter(deviceId, 3, hour % 10, dot) # Ones
- # Set minutes
- device.letter(deviceId, 2, int(minute / 10)) # Tens
- device.letter(deviceId, 1, minute % 10) # Ones
- time.sleep(1)
- device.clear()
- def show_numbers(left_number,right_number):
- device = led.sevensegment()
- deviceId = 0
- device.clear()
- leftstr = str(left_number).zfill(3)
- rightstr =str(right_number).zfill(3)
- print("will try to display", leftstr, rightstr)
- #device.letter(deviceId, 8, int(leftstr[0]))
- device.letter(deviceId, 7, int(leftstr[0]))
- device.letter(deviceId, 6, int(leftstr[1]) )
- #device.letter(deviceId, 5, int(leftstr[3]))
- #device.letter(deviceId, 4, int(leftstr[1]))
- device.letter(deviceId, 3, int(rightstr[0]))
- device.letter(deviceId, 2, int(rightstr[1]))
- #device.letter(deviceId, 1, int(rightstr[3]))
- # **********************************
- # End of local display functions *
- # **********************************
- # ************************************
- # Start smartplug service functions *
- # ************************************
- # Check if IP is valid
- def validIP(ip):
- try:
- socket.inet_pton(socket.AF_INET, ip)
- except socket.error:
- parser.error("Invalid IP Address.")
- return ip
- # Predefined Smart Plug Commands
- # For a full list of commands, consult tplink_commands.txt
- commands = {'info' : '{"system":{"get_sysinfo":{}}}',
- 'on' : '{"system":{"set_relay_state":{"state":1}}}',
- 'off' : '{"system":{"set_relay_state":{"state":0}}}',
- 'read' : '{"emeter":{"get_realtime":{}}}'
- }
- # Encryption and Decryption of TP-Link Smart Home Protocol
- # XOR Autokey Cipher with starting key = 171
- #revied code refer https://github.com/softScheck/tplink-smartplug/issues/20
- def encrypt(string):
- key = 171
- result = b"\0\0\0"+ chr(len(string)).encode('latin-1')
- for i in string.encode('latin-1'):
- a = key ^ i
- key = a
- result += chr(a).encode('latin-1')
- return result
- def decrypt(string):
- key = 171
- result = ""
- for i in string:
- a = key ^ i
- key = i
- result += chr(a)
- return result
- def get_json(string,value):
- try:
- pos = string.find(":",string.find(value))
- if pos == -1 :
- return -1
- else:
- end1 = string.find(",",pos)
- end2 = string.find("}",pos)
- try:
- return float(string[pos+1:end1])
- except:
- try:
- return float(string[pos+1:end2])
- except:
- return -1
- except: return -99
- def send_command(cmded,ip,port) :
- try:
- sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock_tcp.connect((ip, port))
- sock_tcp.send(encrypt(cmded))
- data = sock_tcp.recv(2048)
- sock_tcp.close()
- return decrypt(data[4:])
- except:
- return ("error")
- def get_smartplug_status():
- global smartplug_info
- #temporary until imlement file to hold smartplug info separatly
- smartplug_info.ip[0] = config.heaterIPa
- smartplug_info.ip[1] = config.heaterIPb
- #smartplug_info.ip[2] = "192.168.222.63"
- #smartplug_info.ip[3] = "192.168.222.64"
- for index in range(0,len(smartplug_info.ip),1):
- cmd = commands["read"]
- result = send_command(cmd,smartplug_info.ip[index],9999)
- if result != "error" :
- st_cmd = commands["info"]
- smartplug_info.state[index] = get_json(send_command(st_cmd,smartplug_info.ip[index],9999),"relay_state")
- smartplug_info.current[index] = get_json(result,"current")
- smartplug_info.voltage[index] = get_json(result,"voltage")
- smartplug_info.power[index] = get_json(result,"power")
- smartplug_info.total[index] = get_json(result,"total")
- smartplug_info.error[index] = get_json(result,"err_code")
- smartplug_info.sent[index] = cmd
- smartplug_info.received[index] = result
- else:
- pr_status(True,0, "Error connecting to Smartplug on : " + smartplug_info.ip[index])
- def turn_on_smartplug(index):
- global config
- cmd = commands["on"]
- send_command(cmd,smartplug_info.ip[index],9999)
- def turn_off_smartplug(index):
- global config
- cmd = commands["off"]
- send_command(cmd,smartplug_info.ip[index],9999)
- # ************************************
- # End of smartplug service functions *
- # ************************************
- def init(margs):
- global config
- global my_sensors
- global temps_dir
- global exclude_filename
- global debug
- global debug_w1
- global debug_ftp
- global error
- global run_mode
- global smartplug_info
- global status_bffr
- global log_bffr
- global sauna
- global sevenseg
- here = "init"
- #*****************************************************
- # (1) set up buffers and sesnor data
- # and initialise various
- #
- log_bffr = textbffr(100)
- status_bffr = textbffr(200)
- my_sensors = class_my_sensors()
- init_printout = []
- debug = False
- debug_w1 = False
- debug_ftp = False
- exit_flag = False
- new_config_wanted = False
- new_sensor_file_wanted = False
- sauna = False # local Display
- sevenseg = False
- config = class_config() # set all to defaults
- config.last_target = 0
- starttime = datetime.datetime.now()
- timestamp = make_time_text(starttime)
- pr_f(here,"This is the time stamp used for log file >>>", timestamp + "<<<<")
- # GPIO pin set up for Sauna Control
- GPIO.setmode(GPIO.BCM)
- GPIO.setup(18, GPIO.OUT)
- #*****************************************************
- # (2) Get the option Flags
- #
- options_ok = False
- #try:
- try:
- opts, args = getopt.getopt(margs,"dhfsclmw")
- options_ok = True
- init_printout.append("No Options Errors")
- for opt, arg in opts:
- if opt == '-w':
- #debug the W1 function with extra printouts
- debug_w1 = True
- if opt == '-m':
- #Sauna control without 7 Seg display
- init_printout.append("Option : " + opt)
- sauna = True
- sevenseg = False
- if opt == '-l':
- #local control of sauna heater using SCR relay
- init_printout.append("Option : " + opt)
- sauna = True # script to run in Sauna Local { hence the l for local } Display.
- sevenseg = True
- if opt == '-c':
- # cretae a new Config file and exit
- init_printout.append("Option : " + opt)
- new_config_wanted = True
- exit_flag = True
- if opt == '-d':
- # extensive debug printouts
- init_printout.append("Option : " + opt)
- debug = True
- if opt == '-h':
- # print help info on options
- init_printout.append("Option : " + opt)
- init_printout.append("Help on Options:")
- init_printout.append("Help -h")
- init_printout.append("Make Sensor file -s")
- init_printout.append("Make Config file -c")
- init_printout.append("Debug -d")
- init_printout.append("Debug ftp and file name creation -f")
- init_printout.append("Extra Printouts for W1 thermsensor use -w")
- init_printout.append("Local Control of SCR Relay -l")
- init_printout.append("Make Config file -c")
- # init_printout.append(" xxx -?")
- exit_flag = True
- if opt == '-f':
- init_printout.append("Option : " + opt)
- debug_ftp = True
- if opt == '-s':
- init_printout.append("Option : " + opt)
- exit_flag = True
- new_sensor_file_wanted = True
- except:
- init_printout.append("Options error")
- init_printout.append("Correct Options are: ")
- init_printout.append("Debug -d")
- init_printout.append("Help -h")
- init_printout.append("Debug ftp -f")
- init_printout.append("Make Sensor file -s")
- init_printout.append("Make Config file -c")
- exit_flag = True
- #*****************************************************
- # (3) determine the programs location and set up filenames
- #
- config.prog_path = path.dirname(path.realpath(__file__))
- pr_f(here,"config.prog_path : ", config.prog_path )
- config.config_filename = config.prog_path + "/" + "config.cfg"
- pr_f(here,"config.config_filename : ", config.config_filename) # OK
- config.sensor_info_filename = config.prog_path + "/" + "sensor_data.csv"
- pr_f(here,"config.sensor_info_filename: ", config.sensor_info_filename )
- config.logging_filename_save_as = timestamp + "lg.csv"
- pr_f(here,"config.logging_filename_save_as: ", config.logging_filename_save_as )
- config.logging_filename = config.prog_path + config.log_directory + config.logging_filename_save_as
- pr_f(here,"config.logging_filename: ", config.logging_filename )
- config.html_filename = config.prog_path + "/" + "index.html"
- pr_f(here,"config.html_filename: ", config.html_filename)
- config.status_filename = config.prog_path + "/" +"status.html"
- pr_f(here,"config.status_filename: ", config.status_filename )
- config.log_html_filename = config.prog_path + "/" + "log.html"
- pr_f(here,"config.log_html_filename : ", config.log_html_filename)
- config.local_www = "/var/www/html"
- pr_f(here,"config.local_www: ", config.local_www )
- config.local_www_html_filename = config.local_www + "/" +"index.html"
- pr_f(here,"config.local_www_html_filename: ",config.local_www_html_filename )
- config.local_www_log_html_filename = config.local_www + "/" + "log.html"
- pr_f(here,"config.local_www_log_html_filename : ", config.local_www_log_html_filename)
- config.local_www_status_htlm_filename = config.local_www + "/" + "status.html"
- pr_f(here,"config.local_www_status_htlm_filename: ", config.local_www_status_htlm_filename )
- config.local_www_log_csv = config.local_www + "/" + config.logging_filename_save_as
- pr_f(here,"config.local_www_log_csv: ", config.local_www_log_csv)
- #*****************************************************
- # (3) Load in Configuration from config.cfg
- # (If no file then save defaults to config.cfg file)
- init_printout.append( "Will look for this config file : " + config.config_filename)
- #set up configuration
- if fileexists(config.config_filename) and new_config_wanted:
- init_printout.append("For a default config file please first rename or delete old file")
- exit_flag = True
- elif new_config_wanted:
- init_printout.append("New Config File Made with default values, now you can edit it")
- config_write(config.config_filename,config)
- exit_flag = True
- else:
- if fileexists(config.config_filename):
- init_printout.append("Config taken from file")
- print( "will try to read Config File : " , config.config_filename )
- config_read(config.config_filename) # overwrites from file
- else : # no file so needs to be written
- config_write(config.config_filename,config)
- init_printout.append("New Config File Made with default values, you probably need to edit it")
- #check for an existing sensor data file
- if fileexists(config.sensor_info_filename) and new_sensor_file_wanted:
- init_printout.append("For a new sensor data file please first rename or delete old file")
- exit_flag = True
- elif new_sensor_file_wanted:
- codes_count = check_number_connected()
- # then if there are any write to the new file
- if codes_count >0 :
- init_printout.append("Sensor Data found filecreated and can now be edited. It has "+ str(new_codes_count) + " entries" )
- write_sensor_data(new_codes_count,True)
- else:
- init_printout.append("No sensors connected Please connect at least one")
- exit_flag = True
- elif fileexists(config.sensor_info_filename):
- init_printout.append("Existing Sensor data file found and data read in")
- # if there is a file with sensor data read it in
- read_in_sensor_data(config.sensor_info_filename)
- new_codes_count = get_temps()
- #then if there are any new we have not seen before write to the sensor file
- if new_codes_count >0 :
- init_printout.append("New Sensors found"+ str(new_codes_count) + " new" )
- write_sensor_data(new_codes_count,True)
- # Initial setup Complete so write messages
- for initial_ind in range(0,len(init_printout)):
- pr_status(True,"Initial : ",init_printout[initial_ind])
- if exit_flag:
- sys.exit()
- #*****************************************************
- # (5) mount remote drive using parameters from config.cfg
- # (mount_point,test_file,mount_arg1,mount_arg2)
- # NOT USED AT THE MOMENT IN thermoxx.py
- #print(mount_log_drive(config.mount_point,config.test_file,config.mount_arg1,config.mount_arg2),"\n")
- #*****************************************************
- # (6) set up log file using parameters from config.cfg
- # (file based is based on current time)
- if len(config.log_directory) > 0:
- config.logging_outfile = open(config.logging_filename,'w')
- config.logging_on = True
- else:
- config.logging_on = False
- config.logging_filename = None
- config.logging_outfile = ""
- #*****************************************************
- # (7) Make sure the Gpio and thermometer modules are loaded
- # ()
- #Commented out for test
- #subprocess.call(['sudo','modprobe', 'w1-gpio'])
- #subprocess.call(['sudo','modprobe', 'w1-therm'])
- #*****************************************************
- # (9) set up empty lists to hold smartplug info
- # (see "class_smartplug" for information)
- # NOTE: Class is set from here for 2 (two) plugs
- smartplug_info = class_smartplug(2)
- #*****************************************************
- # (10) set up error codes
- # ()
- error=["OK","1File only","2New no Data","3Timeout","4CRC er",
- "5Read Err","6Retry Err","7Error","8No Data","9No Dev","10Disconn"]
- def write_sensor_data(new_data_count, new_file):
- # add a new record to the sensor file
- global my_sensors
- global config
- global smartplug_info
- here = "write_sensor_data"
- pr(here, "write_sensor_data will write : ", new_data_count)
- fields = ['number','code','location','stype','comment']
- # 'at' mode adds to end of the file and opens file as text
- if new_file:
- mode = 'wt'
- else:
- mode = 'at'
- try:
- with open(config.sensor_info_filename, mode) as sensorcsv_file:
- writer = csv.DictWriter(sensorcsv_file, fieldnames = fields)
- if new_file: # this is a blank file
- writer.writeheader() # new file needs headings.
- for line_count in range(len(my_sensors.code)-new_data_count,len(my_sensors.code),1):
- # We need to write to the new line with "number,code,location,stype,comment"
- writer.writerow({
- 'number': my_sensors.number[line_count],
- 'code': my_sensors.code[line_count],
- 'location': my_sensors.location[line_count],
- 'stype': my_sensors.stype[line_count],
- 'comment': my_sensors.comment[line_count]
- })
- except:
- pr_status(True,0,"Error accessing the existing sensor info file")
- pr_status(True,0,"Close the file if you are editing it!")
- sys.exit()
- return 0
- def config_read(c_filename):
- here = "config_read"
- config_read = configparser.RawConfigParser()
- config_read.read(c_filename)
- config.scan_delay = float(config_read.getint('SetUp', 'scan_delay'))
- config.max_scans = int(config_read.getint('SetUp', 'max_scans'))
- config.log_directory = config_read.get('SetUp', 'log_directory')
- config.ftp_credentials_filename = config_read.get('SetUp', 'ftp_credentials_filename')
- config.ftp_credentials_log_filename = config_read.get('SetUp', 'ftp_credentials_log_filename')
- config.ftp_credentials_status_filename = config_read.get('SetUp', 'ftp_credentials_status_filename')
- config.ftp_credentials_log_html_filename= config_read.get('SetUp', 'ftp_credentials_log_html_filename')
- config.mount_point = config_read.get('SetUp', 'mount_point')
- config.test_file = config_read.get('SetUp', 'test_file')
- config.mount_arg1 = config_read.get('SetUp', 'mount_arg1')
- config.mount_arg2 = config_read.get('SetUp', 'mount_arg2')
- config.delay_limit = float(config_read.get('SetUp', 'delay_limit'))
- config.delay_increment = float(config_read.get('SetUp', 'delay_increment'))
- config.ftplog = float(config_read.get('SetUp', 'ftplog'))
- config.heaterIPa = config_read.get('SetUp', 'heaterIPa')
- config.heaterIPb = config_read.get('SetUp', 'heaterIPb')
- config.sensor4readings = config_read.get('SetUp', 'sensor4readings')
- config.change4log = config_read.get('SetUp', 'change4log')
- config.control_hysteresis = float(config_read.get('SetUp', 'control_hysteresis'))
- config.default_target = float(config_read.get('SetUp', 'default_target'))
- config.precision = int(config_read.get('SetUp', 'precision'))
- return
- def config_write(c_filename,default_config):
- here = "config_write"
- config_write = configparser.RawConfigParser()
- config_write.add_section('SetUp')
- config_write.set('SetUp', 'scan_delay',default_config.scan_delay)
- config_write.set('SetUp', 'max_scans',default_config.max_scans)
- config_write.set('SetUp', 'log_directory',default_config.log_directory)
- config_write.set('SetUp', 'ftp_credentials_filename',default_config.ftp_credentials_filename)
- config_write.set('SetUp', 'ftp_credentials_log_filename',default_config.ftp_credentials_log_filename)
- config_write.set('SetUp', 'ftp_credentials_status_filename',default_config.ftp_credentials_status_filename)
- config_write.set('SetUp', 'ftp_credentials_log_html_filename',default_config.ftp_credentials_log_html_filename)
- config_write.set('SetUp', 'mount_point',default_config.mount_point)
- config_write.set('SetUp', 'test_file',default_config.test_file)
- config_write.set('SetUp', 'mount_arg1',default_config.mount_arg1)
- config_write.set('SetUp', 'mount_arg2',default_config.mount_arg2)
- config_write.set('SetUp', 'scan_delay',default_config.scan_delay)
- config_write.set('SetUp', 'delay_limit',default_config.delay_limit)
- config_write.set('SetUp', 'delay_increment',default_config.delay_increment)
- config_write.set('SetUp', 'ftplog',default_config.ftplog)
- config_write.set('SetUp', 'heaterIPa',default_config.heaterIPa)
- config_write.set('SetUp', 'heaterIPb',default_config.heaterIPb)
- config_write.set('SetUp', 'sensor4readings',default_config.sensor4readings)
- config_write.set('SetUp', 'change4log',default_config.change4log)
- config_write.set('SetUp', 'control_hysteresis',default_config.control_hysteresis)
- config_write.set('SetUp', 'default_target',default_config.default_target)
- config.write.set('SetUp', 'precision',default_config.precision)
- # Writing our configuration file to 'c_filename'
- pr(here, "ready to writenew : " , c_filename)
- with open(c_filename, 'w+') as configfile:
- config_write.write(configfile)
- return 0
- def read_in_sensor_data(s_filename):
- # Set sensor data lists with initial values
- # read in from file if it exists if not then set up
- # just defaults for one sensor
- # later any sensors that are connected will be added
- global my_sensors
- global smartplug_info
- global config
- here = "read_in_sensor_data"
- pr(here, "dictionary of my_sensors : ", my_sensors.__dict__ )
- with open(s_filename, 'r') as csvfile:
- d_file = csv.DictReader(csvfile)
- ind = 0
- for row in d_file:
- my_sensors.number.append(row['number'])
- my_sensors.code.append(row['code'])
- my_sensors.connected.append(False)
- my_sensors.reading.append(-108)
- my_sensors.last_logged.append(-108)
- my_sensors.code_seen.append(False)
- my_sensors.code_seen_but_disconnected.append(False)
- my_sensors.location.append(row['location'])
- my_sensors.stype.append(row['stype'])
- my_sensors.comment.append(row['comment'])
- my_sensors.delay.append(0)
- my_sensors.error_number.append(2)
- my_sensors.last_logged_error_number.append(2)
- my_sensors.status_text.append("?")
- ind += 1
- return(True)
- def read_in_schedule_data(sch_filename):
- # Read in Schedule data from schedule.csv
- global my_sensors
- global smartplug_info
- global config
- here = "read_in_schedule_data"
- pr(here, "Schedulke Data read in : ", schedule.__dict__ )
- with open( config.prog_path + "/schedule.csv", 'r') as csvfile:
- d_file = csv.DictReader(csvfile)
- ind = 0
- for row in d_file:
- schedule.index.append(row['index'])
- schedule.year.append(row['year'])
- schedule.month.append(row['month'])
- schedule.day.append(row['day'])
- schedule.hour.append(row['hour'])
- schedule.minute.append(row['minute'])
- schedule.target_temp.append(row['target_temp'])
- ind += 1
- return(True)
- def send_by_ftp(ftp_cred,send_filename, save_as_filename):
- here = "send_by_ftp"
- result = ["FTP attempt for :" + send_filename]
- # if controlling Sauna do not need to send by FTP
- if not sauna :
- try:
- with open(ftp_cred, 'r') as csvfile:
- cred_file = csv.DictReader(csvfile)
- ind = 0
- for row in cred_file:
- if ind == 0:
- ftp_user = row['user']
- pr_f(here ,"ftpuser : ",ftp_user)
- ftp_password = row['password']
- pr_f(here,"ftp password : ", ftp_password)
- file_2_send = str(send_filename)
- if save_as_filename == "use_cred":
- #use filename from credentials file
- file_as = str(row['file_as'])
- else:
- # use file from function parameter
- file_as = save_as_filename
- ftp_directory = str(row['directory'])
- pr_f(here, "ftp directory : ",ftp_directory)
- ftp_site = str(row['site'])
- pr_f(here, "ftp site : ", ftp_site)
- else:
- result.append("Error more than one line in FTP Credentials file")
- return(result)
- ind += 1
- ftp = FTP()
- pr_f(here,"Will try to connect to : ", ftp_site)
- ftp.connect(ftp_site, 21)
- pr_f(here, "logging in here is ftp welcome message : ",ftp.getwelcome())
- ftp.login(user=ftp_user,passwd=ftp_password)
- pr_f(here, "logged in to : ",ftp_site)
- ftp.cwd(ftp_directory)
- pr_f(here, "directory changed to : ", ftp_directory)
- sendfile = open(send_filename,'rb')
- result.append("Will try to send : " + send_filename + " : as : "
- + file_as + " to : " + ftp_site + "/" + ftp_directory)
- ftp.storbinary('STOR ' + file_as,sendfile)
- sendfile.close()
- ftp.quit()
- pr_f(here, "ftp quitedfrom : ", ftp_site)
- pr_f(here,"Done FTP")
- return(result)
- except:
- pr_f(here,"Failed FTP",save_as_filename)
- result.append("Error Trying To Send " + send_filename + " file by FTP")
- return(result)
- def send_temperature_data_using_ftp(ftp_credentials_file):
- global config # set n init() used to hold FTP credentials
- global my_sensors # sensor data
- global smartplug_info
- global target_temp
- here = "send_temperature_data_using_ftp"
- ftp_text_top = ["\t<!--"]
- ftp_text_top.append("\Temperature Logging and Control")
- ftp_text_top.append("\t-->")
- ftp_text_top.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"")
- ftp_text_top.append("\t\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">")
- ftp_text_top.append("<htmlError Trying To Send xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">")
- ftp_text_top.append("<head>")
- ftp_text_top.append("\t<title>Temperature Logging and Control</title>")
- ftp_text_top.append("\t<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />")
- ftp_text_top.append("\t<meta name=\"generator\" content=\"Geany 1.27\" />")
- ftp_text_top.append("\t<meta http-equiv=\"refresh\" content=\"15\" />")
- ftp_text_top.append("</head>")
- # ftp_text_top.append("" + str(config.scan_count) + " System Time: "
- # + make_time_text(datetime.datetime.now()) + " Log File: " + str(config.logging_filename))
- ftp_text_top.append("<body>")
- ftp_text_top.append("<table style=\"background-color: #f4e7e7; width: 350px; height: 150px; border: 1px \
- solid #1b58e4;\" cellpadding=\"5\" align=\"center\"><caption>Temperatures Logging</caption>")
- ftp_text_top.append("<tbody>")
- ftp_text_linestart = "<tr align=\"center\" valign=\"middle\"><td>"
- ftp_text_between = "</td><td>"
- ftp_text_line_end = "</td></tr>"
- ftp_text_end = ["</tbody>"]
- ftp_text_end.append("</table>")
- ftp_text_end.append("</body>")
- ftp_text_end.append("</html>")
- with open(config.html_filename,'w') as htmlfile:
- for element in ftp_text_top:
- htmlfile.write(element)
- htmlfile.write(ftp_text_linestart + " Scan Count: " + ftp_text_between \
- + str(config.scan_count) + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + " System Time: " + ftp_text_between \
- + make_time_text(datetime.datetime.now()) + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + " Html Log File: " + ftp_text_between \
- + "<a href=\"log.html\" target = \"_blank\">log.html</a>" + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + " Status File: " + ftp_text_between \
- + "<a href=\"status.html\" target = \"_blank\">status.html</a>" + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + " CSV Log File: " + ftp_text_between \
- + "<a href=" + "\"" + str(config.logging_filename_save_as) + "\"" + "target = \"_blank\">" + str(config.logging_filename_save_as) + "</a>" + ftp_text_line_end)
- s_numb = 0
- for element in my_sensors.number:
- htmlfile.write(ftp_text_linestart + str(element) + ftp_text_between + str(my_sensors.location[s_numb]) + ftp_text_between + str(my_sensors.status_text[s_numb]) + ftp_text_line_end)
- s_numb +=1
- if sauna:
- htmlfile.write(ftp_text_linestart + "Sauna" + ftp_text_between + "Target Temp : " + ftp_text_between + str(target_temp) + ftp_text_line_end)
- else:
- htmlfile.write(ftp_text_linestart + "Plug 1 Power and Total" + ftp_text_between + str(smartplug_info.power[0]) + ftp_text_between + str(smartplug_info.total[0]) + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + "Plug 2 Power and Total"+ ftp_text_between + str(smartplug_info.power[1]) + ftp_text_between + str(smartplug_info.total[1]) + ftp_text_line_end)
- htmlfile.write(ftp_text_linestart + "Scehduled" + ftp_text_between + "Target Temp : " + ftp_text_between + str(target_temp) + ftp_text_line_end)
- for element in ftp_text_end:
- htmlfile.write(element)
- ftp_result = send_by_ftp(ftp_credentials_file, config.html_filename, "use_cred")
- for pres_ind in range(0,len(ftp_result)):
- pr_f(here, str(pres_ind) + " : ", ftp_result[pres_ind])
- try:
- # send the same html file to the local web site
- copyfile(config.html_filename, config.local_www_html_filename)
- # remove # on next line for debug
- # pr_status(True,0, "Sent : " + config.html_filename + " to : " + config.local_www_html_filename)
- except:
- pr_status(True,0,"Fail with copy " + config.html_filename + " to : " + config.local_www_html_filename)
- def check_number_connected():
- sensors = W1ThermSensor.get_available_sensors()
- return(len(sensors))
- def get_temps():
- # check which sensors are connected and update relavant flags
- # build a list of all the new sensors and then add them to the data in use
- # note : requires that my_sensors variable is populated
- global my_sensors
- global dropped_list
- global smartplug_info
- global config
- here = "get_temps"
- # Look for what sensors are connected
- sensors = W1ThermSensor.get_available_sensors()
- connected_codes = ["nocodeyet"]*len(sensors)
- connected_temp = [-100]*len(sensors)
- pr_w1 (here, " number seen", len(sensors) )
- ind = 0
- for individual_sensor in W1ThermSensor.get_available_sensors():
- #follwoing does not work so commented out
- #try:
- #individual_sensor.set_precision(config.precision)
- #except:
- # pr_w1(here, "Cannot Change Precision" , str(individual_sensor.id) )
- connected_codes[ind] = individual_sensor.id
- try:
- connected_temp[ind] = individual_sensor.get_temperature()
- except:
- pr_w1(here, "Error Reading scanned item", str(ind) + " : " + individual_sensor.id)
- connected_temp[ind] = -100
- pr_w1(here , "Scan of seen sensors" , str(ind) + " : " + connected_codes[ind] + " : " + str(connected_temp[ind]))
- ind += 1
- config.number_seen = ind
- if len(sensors) > 0:
- config.sensor_present = True
- pr_w1(here, "Sensor present Set True with count equal to : ", str(len(connected_codes)))
- else:
- pr_w1(here, "Sensor present not Set with count equal to : ", str(len(connected_codes)))
- config.sensor_present = False
- new_codes = []
- config.ref_sensor_index = -1
- dropped_list = ""
- for my_sensor_count in range(0,len(my_sensors.code)):
- my_sensors.code_seen[my_sensor_count] = False
- my_sensors.reading[my_sensor_count] = -100 # signal that no data seen
- my_sensors.error_number[my_sensor_count] += 1
- for seen_count in range (0,config.number_seen):
- if connected_codes[seen_count] == my_sensors.code[my_sensor_count]:
- # set flag to indicate has been seen during this run of program
- pr_w1 (here, "Scanning My Count, seen count ", str(my_sensor_count) + " :" + str(seen_count) + " : " + str(connected_temp[seen_count]) )
- my_sensors.code_seen[my_sensor_count] = True
- my_sensors.connected[my_sensor_count] = True
- my_sensors.error_number[my_sensor_count] = 0 # signal that its not in error
- if connected_temp[seen_count] != -100:
- # code there but not ready to be read
- my_sensors.reading[my_sensor_count] = connected_temp[seen_count] # copy across id temp found
- else:
- my_sensors.reading[my_sensor_count] = -100
- my_sensors.code_seen[my_sensor_count] = False
- my_sensors.error_number[my_sensor_count] = 1
- if my_sensors.code[my_sensor_count] == config.sensor4readings:
- config.ref_sensor_index = my_sensor_count
- my_sensors.code_seen_but_disconnected[my_sensor_count] = False
- count_connected = 0
- new_codes = []
- for element in connected_codes:
- if not element in my_sensors.code:
- new_codes.append(element)
- count_connected += 1
- if len(new_codes) > 0:
- pr_w1(here, str(len(new_codes)), " new sensors found")
- pr_w1(here, " New sensors found ",len(new_codes))
- for ind in range(0, len(new_codes)):
- if len(my_sensors.number) > 0:
- my_sensors.number.append("n" + str(count_connected-len(new_codes)+ind+1))
- else:
- my_sensors.number.append("n" + str(1))
- my_sensors.code.append(new_codes[ind])
- my_sensors.connected.append(True)
- my_sensors.reading.append(-102)
- my_sensors.last_logged.append(-102)
- my_sensors.code_seen.append(True)
- my_sensors.code_seen_but_disconnected.append(False)
- my_sensors.location.append("New Sensor " + str(my_sensors.number[ind]) + " Location")
- my_sensors.stype.append("New Sensor " + str(my_sensors.number[ind]) + " Type")
- my_sensors.comment.append("New Sensor " + str(my_sensors.number[ind]) + " Comment")
- my_sensors.delay.append(0)
- my_sensors.error_number.append(2)
- my_sensors.status_text.append("New")
- my_sensors.last_logged_error_number.append(2)
- else:
- pr_w1(here, "no new codes, still only : ", str(count_connected) + " connected")
- return(len(new_codes))
- def log_temperature_data_to_file():
- global config
- global sensors
- global smartplug_info
- global sauna_on
- global sauna_off
- global control_error
- here = "log_temperature_data_to_file"
- #write the time at the start of the line in logging file
- logtime = datetime.datetime.now()
- config.logging_outfile.write(str(logtime.day).zfill(2) + "/" + str(logtime.month).zfill(2) +
- "/" + str(logtime.year).zfill(2) + " " + str(logtime.hour).zfill(2) + ":" +
- str(logtime.minute).zfill(2) + ":" + str(logtime.second).zfill(2))
- if (config.sensor_present == False):
- config.logging_outfile.write(" : no sensors with Trg Temp of : " + str(target_temp) + "\n")
- else:
- config.logging_outfile.write(",TrgTemp: ," + str(target_temp) + ",")
- for z in range(0,len(my_sensors.code),1):
- #record the data last saved for this sensor
- #send data to the file only if the sensor is connected
- if my_sensors.code_seen[z]:
- config.logging_outfile.write(" , " + str(my_sensors.number[z]) + " , " + str(my_sensors.status_text[z]))
- my_sensors.last_logged[z] = my_sensors.reading[z]
- my_sensors.last_logged_error_number[z] = my_sensors.error_number[z]
- if sauna :
- config.logging_outfile.write(", Error/On/Off ." + str(control_error) + "," + str(sauna_on) + "," + str(sauna_off))
- config.logging_outfile.write("\n")
- config.logging_outfile.flush()
- return
- else:
- get_smartplug_status()
- config.logging_outfile.write("," + str(smartplug_info.state[0]))
- config.logging_outfile.write("," + str(smartplug_info.current[0]))
- config.logging_outfile.write("," + str(smartplug_info.voltage[0]))
- config.logging_outfile.write(" , " + str(smartplug_info.power[0]))
- config.logging_outfile.write("," + str(smartplug_info.total[0]))
- config.logging_outfile.write("," + str(smartplug_info.error[0]))
- config.logging_outfile.write("," + str(smartplug_info.state[1]))
- config.logging_outfile.write("," + str(smartplug_info.current[1]))
- config.logging_outfile.write("," + str(smartplug_info.voltage[1]))
- config.logging_outfile.write("," + str(smartplug_info.power[1]))
- config.logging_outfile.write("," + str(smartplug_info.total[1]))
- config.logging_outfile.write("," + str(smartplug_info.error[1]))
- config.logging_outfile.write("\n")
- config.logging_outfile.flush()
- return
- def set_status_text():
- # set the status text based on the results of the last scan
- error_count = 0
- here = "set_status_text"
- for z in range(0,len(my_sensors.code),1):
- pr_w1(here, "setting status text (index:error:reading) ", str(z) + " : " + str(my_sensors.error_number[z]) + " : " + str(my_sensors.reading[z]))
- if my_sensors.error_number[z] == 0 :
- my_sensors.status_text[z] = ("{0:.4}".format(my_sensors.reading[z]))
- else:
- error_count +=1
- if my_sensors.delay[z] >= config.delay_limit:
- my_sensors.status_text[z] = ("Wait" + str(int(my_sensors.delay[z])))
- else:
- my_sensors.status_text[z] = ("ErrorCount" + str(my_sensors.error_number[z]))
- def make_printout_for_screen(datachange):
- global config
- global my_sensors
- global smartplug_info
- global target_temp
- here = "make_printout_for_screen"
- error_count = 0
- #set printout for start of the line
- printout = str(config.scan_count) + " of " + str(config.max_scans) + " " + datetime.datetime.now().strftime("%d:%m:%Y %H:%M:%S") + " Target: " + str(target_temp)
- for z in range(0,len(my_sensors.code),1):
- if my_sensors.connected[z]:
- printout += "[" + str(my_sensors.number[z]) + "=" + my_sensors.status_text[z] + "]"
- else:
- if my_sensors.code_seen[z]:
- printout += "[" + str(my_sensors.number[z]) + "= disconn ]"
- printout += " "
- #if not datachange:
- # printout += "[no changes]"
- if max(my_sensors.delay) >= config.delay_limit:
- printout +="[max delay count:" + "{0:.4}".format(max(my_sensors.delay)) +"]"
- if len(dropped_list) > 0:
- printout += "[disconnected:" + dropped_list + "]"
- if error_count >0 :
- printout += "[error count=" + str(error_count) +"]"
- if not max(my_sensors.connected):
- printout += "[no sensors]"
- return(printout)
- def get_target_temp(year,month,day,hour,minute):
- global schedule
- global config
- # From Scedule get Target Temperature
- # Search for Target Temp
- target_temp = -100
- ind_result = 1
- for ind in range(1,len(schedule.year)):
- if int(year) == int(schedule.year[ind]):
- if int(month) == int(schedule.month[ind]):
- if int(day) == int(schedule.day[ind]):
- if int(hour) == int(schedule.hour[ind]):
- if int(minute) >= int(schedule.minute[ind]):
- if int(minute) <= int(schedule.minute[ind+1]):
- ind_result = ind
- break
- else:
- ind_result = ind
- if config.last_target != schedule.target_temp[ind_result+1]:
- pr_status(True,0, " config.last_target : " + str(config.last_target) + " New Target : " +
- str(schedule.target_temp[ind_result+1]))
- config.last_target = schedule.target_temp[ind_result+1]
- if ind_result == 1:
- pr_status(True,0,"Error or very new schedule >> ind result : " + str(ind_result) + "Target set to 17.654321")
- return (17.654321)
- else:
- if ind_result +1 > len(schedule.year):
- pr_status(True,0,"Error>> ind result : " + ind_result + "Target set to 16.54321")
- return (16.54321)
- else:
- # remove # in next 4 lines for debug
- #pr_status(True,0,"Schedule Look Up Result>> index used: " + str(ind_result+1).zfill(4) + " Date : "
- # + str(schedule.year[ind_result+1]).zfill(4) + "/" + str(schedule.month[ind_result+1]).zfill(2) + "/"
- # + str(schedule.day[ind_result+1]).zfill(2) +" Time: " + str(schedule.hour[ind_result+1]).zfill(2) +":"
- # + str(schedule.minute[ind_result+1]).zfill(2) +" Target : " + str(schedule.target_temp[ind_result+1]))
- return (float(schedule.target_temp[ind_result+1]))
- def main(argv):
- global config
- global starttime
- global my_sensors
- global schedule
- global debug
- global target_temp
- global status_bffr
- global log_bffr
- global last_ref
- global sauna
- global sauna_on
- global sauna_off
- global control_error
- last_ref = -1
- min_on = 0.75
- period = 12
- sauna_on = period
- sauna_off = 0
- here = "main"
- #Set things up and read in
- init(argv) # 6 tasks setting up variables etc
- # if there is a schedule file read it in
- pr_status(True,0,"Loading Schedule File Data from : " + config.prog_path + "/schedule.csv")
- if fileexists(config.prog_path + "/schedule.csv"):
- schedule = class_schedule()
- read_in_schedule_data(config.prog_path + "schedule.csv")
- else:
- quit ( "No Schedule")
- if config.logging_on:
- pr(here, "Starting with Logging to : " + config.logging_filename + " at ", datetime.datetime.now())
- else:
- pr(here, "Starting with Logging Off at : " + datetime.datetime.now())
- if config.max_scans > 0:
- pr(here, "Starting Temp Sensor Scans for : " + str(config.max_scans) + " scans starting at : ", datetime.datetime.now())
- else:
- pr(here, "Starting Continuous Temp Sensor Scans for at : ", datetime.datetime.now())
- pr(here, "With an interval of : ", config.scan_delay)
- #Main Loop
- change_flag = False
- config.scan_count = 1
- config.sensor_present = False
- # Scan for max_scan times or for ever if config.max_scans = 0
- while (config.scan_count <= config.max_scans) or (config.max_scans == 0):
- # check_for_new codes and find out what sensors are cobnnected
- # geting new codes if there are any
- new_codes_count =get_temps()
- # then if there are any new we have not seen before write to the sensor file
- if new_codes_count >0 :
- write_sensor_data(new_codes_count,len(my_sensors.code) == new_codes_count)
- # now get data from all the sensor that are connected
- for z in range(0,len(my_sensors.code),1):
- if my_sensors.connected[z]:
- pr_w1(here, "Scan Reached " + str(z), "connected status : " + str(my_sensors.connected[z]))
- # if there has been a timeout value will be about 8 initially
- #if (my_sensors.delay[z] < config.delay_limit):
- # get_temperature(z)
- pr_w1(here,"sensor : " + str(my_sensors.number[z]) + " returned :", my_sensors.reading[z])
- #else:
- # if my_sensors.delay[z] >= config.delay_limit:
- # my_sensors.delay[z] -= config.delay_increment # so after few scans will try again
- # else:
- # my_sensors.delay[z] = 0
- # check if this sensor has changed more than 0.25 degrees, if so set flag
- # that will trigger print out and logging
- if (abs(my_sensors.last_logged[z] - my_sensors.reading[z])) > float(config.change4log):
- change_flag = True
- if config.scan_count > 1 :
- # Following two Lines helps watching changes but is not needed
- # pr_log(True,"Change detected in sensor : " + str(my_sensors.number[z]) + "was : " +
- # str(my_sensors.last_logged[z]) + " now : " + str(my_sensors.reading[z]))
- my_sensors.last_logged[z] = my_sensors.reading[z]
- if my_sensors.last_logged_error_number[z] != my_sensors.error_number[z]:
- change_flag = True
- # ****************************
- # Get target temperature
- #****************************
- time_now = datetime.datetime.now()
- if sauna:
- target_temp = config.default_target
- else:
- target_temp = get_target_temp(time_now.year,time_now.month,time_now.day,time_now.hour,time_now.minute)
- # ****************************
- # Start of smartplug Control *
- # ****************************
- # Check if reading indictates if heaters should be turned on of off
- between = False
- control_error = (my_sensors.reading[config.ref_sensor_index] - target_temp)/config.control_hysteresis
- if sauna :
- if (len(my_sensors.reading) > 0) and (config.ref_sensor_index != -1 ):
- if sevenseg :
- show_numbers(target_temp, my_sensors.reading[config.ref_sensor_index] )
- if my_sensors.reading[config.ref_sensor_index] > target_temp + config.control_hysteresis :
- GPIO.output(18, GPIO.LOW)
- print("temp error to send is ", my_sensors.reading[config.ref_sensor_index] - target_temp, "100% off" )
- else:
- if my_sensors.reading[config.ref_sensor_index] < target_temp - config.control_hysteresis:
- GPIO.output(18, GPIO.HIGH)
- print("temp error to send is ", my_sensors.reading[config.ref_sensor_index] - target_temp, "100% on")
- else:
- between = True
- sauna_on = min_on + (period-min_on)*((target_temp+config.control_hysteresis)-my_sensors.reading[config.ref_sensor_index])/(2*config.control_hysteresis)
- if sauna_on <0 :
- sauna_on = 0
- sauna_off = period - sauna_on
- if sauna_off < 0 :
- sauna_off = 0
- print("Error" , str(control_error), " We are between. On Time : " + str(sauna_on) + " Off Time : " + str(sauna_off))
- GPIO.output(18, GPIO.HIGH)
- time.sleep(sauna_on)
- if sauna_off > 0 :
- GPIO.output(18, GPIO.LOW)
- time.sleep(sauna_off)
- else:
- print( len(my_sensors.reading), config.ref_sensor_index )
- pr_status(True,0, "Cannot control no sensors or reference sensor not defined")
- else :
- if (len(my_sensors.reading) > 0) and (config.ref_sensor_index != -1 ):
- print("smartplug control temperatures being used", my_sensors.reading[config.ref_sensor_index], target_temp )
- if my_sensors.reading[config.ref_sensor_index] > target_temp + float(config.control_hysteresis) :
- pr_w1(here,"Turn off smart plugs at temp: ", my_sensors.reading[config.ref_sensor_index])
- turn_off_smartplug(0)
- turn_off_smartplug(1)
- else:
- if my_sensors.reading[config.ref_sensor_index] < target_temp - float(config.control_hysteresis) :
- pr_w1(here,"Turn on smart plugs at temp : ", my_sensors.reading[config.ref_sensor_index])
- turn_on_smartplug(0)
- turn_on_smartplug(1)
- else:
- pr_status(True,0, "Cannot control no sensors or reference sensor not defined")
- # ****************************
- # End of smartplug Control *
- # ****************************
- #for test only to get more print outs and logging
- #change_flag = True
- if change_flag or (config.scan_count == config.max_scans) or (config.scan_count == 0):
- # now if data changed do logging printing and sending
- set_status_text() # figure out the status text for the temperature sensors
- if config.logging_on:
- log_temperature_data_to_file()
- if config.ftplog_count > config.ftplog :
- ftp_result = send_by_ftp(config.ftp_credentials_log_filename, config.logging_filename, config.logging_filename_save_as)
- for pres_ind in range(0,len(ftp_result)):
- pr_f(here, str(pres_ind) + " : ",ftp_result[pres_ind])
- try:
- copyfile(config.logging_filename, config.local_www_log_csv)
- # Uncoment following line for info on copying file to web site dir
- pr_f( "Sent : "+ config.logging_filename + " to : " + config.local_www_log_csv)
- except:
- pr_status(True,0, "Fail with send log csv to " + config.local_www_log_csv)
- config.ftplog_count = 0
- else:
- config.ftplog_count = config.ftplog_count +1
- # *******************************************************************************
- # After logging done, have all info send html file to websites done every scan *
- # *******************************************************************************
- send_temperature_data_using_ftp(config.ftp_credentials_filename)
- if (len(my_sensors.reading) > 0):
- #must be some sensor before we can print data
- if debug:
- #printouts if in debug
- pr_log(True, "\n" + make_printout_for_screen(change_flag) + "\n")
- else:
- if change_flag:
- #printouts if not in debug and data changed
- pr_log(True,make_printout_for_screen(change_flag))
- else:
- #printouts if not in debug and no data changed
- pr_log(False,make_printout_for_screen(change_flag))
- #reset change flag and operate delay before next scan
- change_flag = False
- last_ended = make_time_text(datetime.datetime.now())
- if not between :
- time.sleep(config.scan_delay)
- config.scan_count += 1
- # following line can be used
- pr_status(False,1,"Scan " + str(config.scan_count-1) + " ended at " + last_ended + " Now Start Scan : " + str(config.scan_count))
- pr(here, " ******** Scan Counting to " + str(config.max_scans) + " now at scan " , config.scan_count)
- return 0
- if __name__ == '__main__':
- main(sys.argv[1:])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement