Please tell me how I could have done this better. The code is here on Github.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Write some message on the display.
# This project uses https://github.com/dbrgn/RPLCD.
# sudo apt-get install python-matplotlib
from __future__ import print_function, division, absolute_import, unicode_literals
import sys
# Import LCD stuff from RPLCD, et. al.
from RPLCD import CharLCD
from RPLCD import Alignment, CursorMode, ShiftMode
from RPLCD import cursor, cleared
from xml.dom.minidom import *
import urllib
import RPi.GPIO as GPIO
import time
import csv
# Imports for graph plotting
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import os
import subprocess
import shutil
# some LCD magic happens here
try:
input = raw_input
except NameError:
pass
try:
unichr = unichr
except NameError:
unichr = chr
# ###################################################################################
# Configure stuff here:
# Temp sensor ID is the folder name for ds18b20 1-wire sensors
# found in /sys/bus/w1/devices/
# Drivers are loaded with
# sudo modprobe w1-gpio
# sudo modprobe w1-therm
# or put them in /etc/modules
TempSensorInside = '28-000005ad1070'
TempSensorOutside = '28-000005ad0691'
# Yahoo location code. Get the right one for your location from Yahoo's weather page.
LocationID = '700029'
# ###################################################################################
# Disable useless GPIO warnings
GPIO.setwarnings(False)
# Start Yahoo weather stuff
# Weather array
# Dimensions: 1 = today, 2 = tomorrow
# Elements: 1 = day, 2 = date, 3 = low temp, 4 = high temp, 5 = weather text
Weatherarray = [["", "", "", "", ""] , ["", "", "", "", ""]]
# Fetch weather XML for Trier, Germany
Trier = urllib.urlopen('http://weather.yahooapis.com/forecastrss?w=' + LocationID + '&u=c').read()
# Parse the XML
Trier = parseString(Trier)
# Get town
Place = Trier.getElementsByTagName('yweather:location')[0]
City = Place.attributes["city"].value
Country = Place.attributes["country"].value
# Get date
Date = Trier.getElementsByTagName('lastBuildDate')[0].firstChild.data
# Get coordinates
Geo_Lat = Trier.getElementsByTagName('geo:lat')[0].firstChild.data
Geo_Long = Trier.getElementsByTagName('geo:long')[0].firstChild.data
# Get today's weather
Today = Trier.getElementsByTagName('yweather:condition')[0]
Weathertext = Today.attributes["text"].value
Temperature = float(Today.attributes["temp"].value)
Conditioncode = Today.attributes["code"].value
# Put it all in a list
for Counter in range(2):
# Weather data for two days
# Get data
Future = Trier.getElementsByTagName('yweather:forecast')[Counter]
# Process data
Weatherarray[Counter][0] = Future.attributes["day"].value
Weatherarray[Counter][1] = Future.attributes["date"].value
Weatherarray[Counter][2] = float(Future.attributes["low"].value)
Weatherarray[Counter][3] = float(Future.attributes["high"].value)
Weatherarray[Counter][4] = Future.attributes["text"].value
# End Yahoo weather stuff.
# Start sensor stuff
# The inside sensor
# Open, read, close the sensor files
tempfilein = open("/sys/bus/w1/devices/" + TempSensorInside + "/w1_slave")
textin = tempfilein.read()
tempfilein.close()
# Jump to the right position in the sensor file, convert the string to a number, put the decimal point in
secondlinein = textin.split("\n")[1]
temperaturedatain = secondlinein.split(" ")[9]
temperaturein = float(temperaturedatain[2:])
temperaturein = temperaturein / 1000
# print temperaturein
# The outside sensor
tempfileout = open("/sys/bus/w1/devices/" + TempSensorOutside + "/w1_slave")
textout = tempfileout.read()
tempfileout.close()
# Jump to the right position in the sensor file, convert the string to a number, put the decimal point in
secondlineout = textout.split("\n")[1]
temperaturedataout = secondlineout.split(" ")[9]
temperatureout = float(temperaturedataout[2:])
temperatureout = temperatureout / 1000
# print temperatureout
lcd = CharLCD()
# Print the data onto the display.
lcd.clear()
lcd.write_string(time.strftime("%d.%m.%Y %H:%M"))
lcd.cursor_pos = (1, 0)
#lcd.write_string(str(City) + ' ')
lcd.write_string('Innen: '+ str(temperaturein) + ' Grad')
lcd.cursor_pos = (2, 0)
lcd.write_string('Aussen: '+ str(temperatureout) + ' Grad')
lcd.cursor_pos = (3, 0)
lcd.write_string(Weathertext)
# Write the data to a webpage on the local server
# Get some weather icons that are compliant with Yahoo condition codes. The ones by MerlinTheRed are nice and work well <http://merlinthered.deviantart.com/art/plain-weather-icons-157162192> CC-BY-NC-SA
index = open('/var/www/aktuell.html','w')
index.write('<style type="text/css">body {font-weight:lighter; font-family:Arial; font-size:100%; } h2 {margin:0 0 0 0;}''</style><h6>Updated: ' + time.strftime("%d.%m.%Y %H:%M:%S") + '</h6>' + Weathertext + '<img src="' + Conditioncode + '.png" align="right" alt="Wettericon"><br> Sensordaten: <br> Innen:<br><h2>' + str(temperaturein) + ' °C</h2><br> Aussen:<br><h2>' + str(temperatureout) + '°C</h2><br>')
index.close()
# Write data to a .csv file for graph creation
weather_csv = open('/home/pi/YAWP/weather.csv', 'a')
datawriter = csv.writer(weather_csv)
datawriter.writerow([str(time.strftime('%Y-%m-%d %H:%M')),str(temperaturein),str(temperatureout)])
weather_csv.close()
# From here, a gnuplot file will take over.
p = subprocess.Popen("gnuplot plotter.gpi", shell = True)
os.waitpid(p.pid, 0)
# Copy it over to the webserver
shutil.copy2('/home/pi/YAWP/temps.png', '/var/www/')
1 Answer 1
Basic Structure
I would recommend creating an Elements
class.
class Elements():
def __init__(self, day='', date='', low='', high='', weather=''):
self.day = day
self.date = date
self.low = low
self.high = high
self.text= weather
Then, when you need to set the values, loop through the elements and set the properties using the setattr
function:
# Creates `[Elements(), Elements()]`
weather_array = [Elements()]*2
...
for elem in weather_array:
for prop in ['day', 'date', 'low', 'high', 'text']:
val = Future.attributes[prop].value
if prop == 'low' or prop == 'high':
val = float(val)
setattr(elem, prop, val)
You could access the properties directly using elem.day
but because we are accessing Future's
attributes using text values, it makes sense to do the same with elem's
.
More classes could be useful depending on the structure of the rest of your code.
String Interpolation
There are several times in your code where you do the following:
string = 'This is ' + foo + ' some string concatenation'
String concatenation is fine in a lot of situations. However, in this case, where you need to sandwich some variable information with strings, you should use str.format()
. This syntax allows you to interpolate one to many pieces of information into a string:
>>>'Hello {}!'.format('World')
'Hello World!'
Files
Use the with
syntax when opening files. It automatically closes the file once the block has been left:
with open('some_file.txt', 'r') as file:
# Do something
# The file is closed at this point.
foo = 'hello world!'
Since we are only worried about the 2nd line in both files, just break once we have read that line:
temp_data= ''
temperatures = {temp_sensor_inside:0.0, temp_sensor_outside:0.0}
for sensor in temperatures:
with open('/sys/bus/w1/devices/{}/w1_slave'.format(sensor), 'r') as my_file:
# Iterate line-by-line grabbing both the line number and its text
for line, text in enumerate(my_file):
if line == 2:
temp_data = line.split(' ')[9]
# Assign the correct variable.
temperatures[sensor] = float(temp_data[2:])/1000
break
os.join
Whenever you are creating a filepath, I would recommend to use os.path.join. This function assures that the correct slashes are used based on the current OS.
>>>import os.path
>>>file_path = os.path.join('~', 'Documents', 'my_file.txt')
>>>file_path
'~\\Documents\\my_file.txt'
Closing Comments
Other than that, look into PEP8, the official Python style guide. This will help make your code look and feel more Pythonic. Some of the thing I see really quickly that don't follow convention:
- Use underscores_in_variable_names
- Spacing and whitespace needs to be consistent
- Order of
import
typically goes: standard libraries, 3rd party libraries, local libraries
Look for places where you can split your code into functions. This will help improve readability and make the program more modular, which will help immensely as the program grows.