11
\$\begingroup\$

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) + ' &deg;C</h2><br> Aussen:<br><h2>' + str(temperatureout) + '&deg;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/')
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked May 16, 2014 at 13:10
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

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:

  1. Use underscores_in_variable_names
  2. Spacing and whitespace needs to be consistent
  3. 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.

answered May 16, 2014 at 14:16
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.