10
\$\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/')
\$\endgroup\$
4
\$\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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.