class Clock: def __init__(self, matrixobject, positioninmatrix, clockdefinition, clock1224): logging.info('Creating new Clock instance') self.__Matrix = matrixobject self.__MatrixPosition = positioninmatrix self.__ClockDefinition = clockdefinition self.__Clock24h = clock1224 self.__ClockCanvas = Canvas(clockdefinition['Size']) self.__ImageChanged = True self.__CurrentHour = -1 self.__CurrentMinute = -1 self.__CurrentSecond = -1 self.__CurrentDay = -1 self.__CurrentMonth = -1 self.__CurrentDoW = -1 # ------------------------------------------------------------------------- # update_time_canvas # Update the time every second. The time can be 12h or 24h # ------------------------------------------------------------------------- def update_time_canvas(self): while True: delay = 1.0 if 'Time' in self.__ClockDefinition: if self.__ClockDefinition['Time'] != {}: self.draw_time() delay = 1.0 - float(time.time() % 1) time.sleep(delay) # ------------------------------------------------------------------------- # update_date_canvas # Updates the date every day # ------------------------------------------------------------------------- def update_date_canvas(self): while True: todaysdate = datetime.today() datedow = todaysdate.weekday() datemonth = todaysdate.month dateday = todaysdate.day if 'DoW' in self.__ClockDefinition: if self.__ClockDefinition['DoW'] != {}: if self.__CurrentDoW != datedow: self.__CurrentDoW = datedow self.__ImageChanged = True self.__ClockCanvas.draw_on_canvas(self.__ClockDefinition['DoW'], self.__CurrentDoW) if 'Day' in self.__ClockDefinition: if self.__ClockDefinition['Day'] != {}: if self.__CurrentDay != dateday: self.__CurrentDay = dateday self.__ImageChanged = True self.draw_day() if 'Month' in self.__ClockDefinition: if self.__ClockDefinition['Month'] != {}: if self.__CurrentMonth != datemonth: self.__CurrentMonth = datemonth self.__ImageChanged = True self.__ClockCanvas.draw_on_canvas(self.__ClockDefinition['Month'], self.__CurrentMonth) if self.__ClockDefinition['AutoDraw']: self.draw_on_matrix_canvas() # Calculate the number of seconds until midnight and wait for then secondstomidnight = (datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)) - datetime.now() time.sleep(secondstomidnight.total_seconds()) # ---------------------------------------------------------------------------------- # draw_clock_digit # Draw an individual clock digit at the pre-defined position (x, y) tuple # ---------------------------------------------------------------------------------- def draw_clock_digit(self, position, digit): self.__ClockCanvas.draw_on_canvas( (position[0], position[1], self.__ClockDefinition['Time'][2], self.__ClockDefinition['Time'][3]), digit) # ------------------------------------------------------------------------- # add_image_width # Increase x by a defined width # ------------------------------------------------------------------------- def add_image_width(self, x, imagedef): return x + imagedef[2] - imagedef[0] + 1 # ---------------------------------------------------------------------------------- # draw_day # Update the day (date), consisting of a one or two digit number # ---------------------------------------------------------------------------------- def draw_day(self): x = self.__ClockDefinition['Day'][0] y = self.__ClockDefinition['Day'][1] image = self.__ClockDefinition['Day'][2] imagedefinition = self.__ClockDefinition['Day'][3] # 10's digit if self.__CurrentDay >= 10.0: tensdigit = str(abs(int(self.__CurrentDay)))[0] self.__ClockCanvas.draw_on_canvas((x, y, image, imagedefinition), tensdigit) x = self.add_image_width(x, imagedefinition[tensdigit]) else: tensdigit = 0 self.__ClockCanvas.draw_on_canvas((x, y, image, imagedefinition), 'sp') x = self.add_image_width(x, imagedefinition['sp']) # Units digit unitdigit = str(abs(int(self.__CurrentDay)) - (int(tensdigit) * 10)) self.__ClockCanvas.draw_on_canvas((x, y, image, imagedefinition), unitdigit) # ---------------------------------------------------------------------------------- # draw_time # Update the time display, hh:mm a/p # ---------------------------------------------------------------------------------- def draw_time(self): x = self.__ClockDefinition['Time'][0] y = self.__ClockDefinition['Time'][1] image = self.__ClockDefinition['Time'][2] imagedefinition = self.__ClockDefinition['Time'][3] self.__ImageChanged = False # The font is assumed to be non-proportional for all but the : and am/pm # Calculate the positions of the various items xhourtens = x xhourunits = self.add_image_width(xhourtens, imagedefinition[1]) xcolon = self.add_image_width(xhourunits, imagedefinition[1]) xminutetens = self.add_image_width(xcolon, imagedefinition[':']) xminuteunits = self.add_image_width(xminutetens, imagedefinition[1]) xampm = self.add_image_width(xminuteunits, imagedefinition[1]) # Get the current hour currenttime = time.localtime() # Only update the hour if it has changed if currenttime.tm_hour != self.__CurrentHour: self.__ImageChanged = True self.__CurrentHour = currenttime.tm_hour if self.__Clock24h == 12: # Change to 12 hour clock if currenttime.tm_hour > 12: hour = self.__CurrentHour - 12 ampm = 1 else: hour = self.__CurrentHour ampm = 0 else: # 24 hour hour = self.__CurrentHour ampm = -1 # Draw the hours - first digit if hour >= 20: firstdigit = 2 seconddigit = hour - 20 elif hour >= 10: firstdigit = 1 seconddigit = hour - 10 else: firstdigit = ' ' seconddigit = hour # Draw the first digit self.draw_clock_digit((xhourtens, y), firstdigit) # Draw the second digit self.draw_clock_digit((xhourunits, y), seconddigit) # Draw AM/PM if ampm == 0: self.draw_clock_digit((xampm, y), 'am') elif ampm == 1: self.draw_clock_digit((xampm, y), 'pm') # Draw the : flashing each second if currenttime.tm_sec != self.__CurrentSecond: self.__ImageChanged = True self.__CurrentSecond = currenttime.tm_sec if self.__CurrentSecond / 2.0 == int(self.__CurrentSecond / 2): self.draw_clock_digit((xcolon, y), ':') else: self.draw_clock_digit((xcolon, y), ': ') # Only update the minutes if they have changed if currenttime.tm_min != self.__CurrentMinute: self.__ImageChanged = True self.__CurrentMinute = currenttime.tm_min minute = self.__CurrentMinute if self.__CurrentMinute < 10: minute_firstdigit = 0 minute_seconddigit = self.__CurrentMinute else: minute_firstdigit = int(str(self.__CurrentMinute)[0]) minute_seconddigit = int(str(self.__CurrentMinute)[1]) self.draw_clock_digit((xminutetens, y), minute_firstdigit) self.draw_clock_digit((xminuteunits, y), minute_seconddigit) self.draw_on_matrix_canvas() # ---------------------------------------------------------------------------------- # draw_on_matrix_canvas # If the canvas has changed, draw it on the # ---------------------------------------------------------------------------------- def draw_on_matrix_canvas(self): # Only draw on matrix canvas if AutoDraw is set to True, and the image has changed if self.__ClockDefinition['AutoDraw']: if self.__ImageChanged: self.__Matrix.paste_to_matrix_canvas(self.__MatrixPosition, self.__ClockCanvas) self.__ImageChanged = False
class Weather: def __init__(self, matrixobject, positioninmatrix, weatherdefinition, hours_from_now, update_interval, getweatherobject): logging.info('Creating new Weather instance') self.__Matrix = matrixobject self.__MatrixPosition = positioninmatrix self.__WeatherDefinition = weatherdefinition self.__HoursFromNow = hours_from_now self.__UpdateInterval = update_interval self.__WeatherCanvas = Canvas(weatherdefinition['Size']) self.__ImageChanged = True self.__GetWeatherObject = getweatherobject self.__WeatherData = [] self.__WeatherForecast = {} self.__CurrentWeatherIcon = '' self.__CurrentWeatherWind = -1 self.__CurrentWeatherRain = -1.0 self.__CurrentWeatherSnow = -1.0 self.__CurrentWeatherMaxTemp = 999.0 self.__CurrentWeatherMinTemp = -999.0 self.__CurrentWeatherTime = -1 self.__WeatherReadError = False self.__LastUpdateTime = 0 self.__WindSpeedIcon = -1 self.__RainSnowIcon = '' # ------------------------------------------------------------------------- # get_canvas_matrix_position # Returns the position of the weather canvas on the matrix canvas # ------------------------------------------------------------------------- def get_canvas_matrix_position(self): return self.__MatrixPosition # ------------------------------------------------------------------------- # get_weather_canvas # Returns the weather canvas # ------------------------------------------------------------------------- def get_weather_canvas(self): return self.__WeatherCanvas # ------------------------------------------------------------------------- # update_weather_canvas_thread # The weather canvas threads # ------------------------------------------------------------------------- def update_weather_canvas_thread(self): while True: # Calculate the date of the forecast (only every 3 hours) # Adding an offset. date = self.get_offset_date(self.__HoursFromNow) # Read the weather from TheWeather thread thisweather = self.__GetWeatherObject.read_forecast_for_date(date) # If there was a read error (happens when the time is too close to the # next forecast, read the one 3 hours ahead instead if thisweather == {}: date = self.get_offset_date(self.__HoursFromNow + 3) thisweather = self.__GetWeatherObject.read_forecast_for_date(date) self.__WeatherForecast = self.get_readable_forecast(thisweather) # Draw the weather forecast canvas self.draw_weather_canvas() # Don't update unless the time is greater than updateinterval (s) or 10 # minutes (if there has been an error) if self.__WeatherReadError: delay = 600 self.__WeatherReadError = False else: delay = self.__UpdateInterval # Draw the weather canvas on the matrix canvas self.draw_on_matrix_canvas() time.sleep(delay) # ------------------------------------------------------------------------- # draw_weather_canvas # Draw the new Weather canvas according to the definition # ------------------------------------------------------------------------- def draw_weather_canvas(self): # Go through each item, updating any images if required current_weather = self.__WeatherForecast if current_weather == {}: logging.warning('The weather was not updated correctly') self.__ImageChanged = False self.__WeatherReadError += 1 else: self.__ImageChanged = False # Weather Icon if self.__WeatherDefinition['WeatherIcon'] != (): if self.__CurrentWeatherIcon != current_weather['icon']: self.__CurrentWeatherIcon = current_weather['icon'] self.draw_weather_icon() self.__ImageChanged = True # Max Temperature if self.__WeatherDefinition['MaxTemp'] != (): if self.__CurrentWeatherMaxTemp != current_weather['MaxTemp']: self.__CurrentWeatherMaxTemp = current_weather['MaxTemp'] self.draw_temperature('MaxTemp', self.__CurrentWeatherMaxTemp, 'C') self.__ImageChanged = True # Min Temperature if self.__WeatherDefinition['MinTemp'] != (): if self.__CurrentWeatherMinTemp != current_weather['MinTemp']: self.__CurrentWeatherMinTemp = current_weather['MinTemp'] self.draw_temperature('MinTemp', self.__CurrentWeatherMinTemp, 'C') self.__ImageChanged = True # Wind speed Icon if self.__WeatherDefinition['WindSpeedIcon'] != (): if self.__WindSpeedIcon != True: self.draw_windspeed_icon() self.__ImageChanged = True self.__WindSpeedIcon = True # Wind Speed if self.__WeatherDefinition['WindSpeed'] != (): windspeedtobeaufort = current_weather['beaufort'] if self.__CurrentWeatherWind != windspeedtobeaufort: self.__CurrentWeatherWind = windspeedtobeaufort self.draw_windspeed() self.__ImageChanged = True # Rain or snow Icon? # If there is snow, replace the rain icon with snow icon if self.__WeatherDefinition['RainSnowIcon'] != (): # We have snow! if current_weather['snow'] > 0: self.__CurrentWeatherRain = -1.0 if self.__RainSnowIcon != 'snow': self.draw_rainsnow_icon('SnowIcon') self.__ImageChanged = True self.__RainSnowIcon = 'snow' if self.__CurrentWeatherSnow != current_weather['snow']: self.__CurrentWeatherSnow = current_weather['snow'] self.draw_snowfall() self.__ImageChanged = True # Booo! Rain else: self.__CurrentWeatherSnow = -1.0 if self.__RainSnowIcon != 'rain': self.draw_rainsnow_icon('RainIcon') self.__ImageChanged = True self.__RainSnowIcon = 'rain' if self.__CurrentWeatherRain != current_weather['rain']: self.__CurrentWeatherRain = current_weather['rain'] self.draw_rainfall() self.__ImageChanged = True # Weather Time Indicator (indicator of the 3 hours the forcast covers) if self.__WeatherDefinition['WeatherTime'] != (): if self.__CurrentWeatherTime != current_weather['WeatherTime']: self.__CurrentWeatherTime = current_weather['WeatherTime'] self.draw_weather_time() self.__ImageChanged = True # ------------------------------------------------------------------------- # draw_rainsnow_icon # ------------------------------------------------------------------------- def draw_rainsnow_icon(self, whichicon): self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['RainSnowIcon'], whichicon) # ------------------------------------------------------------------------- # draw_windspeed_icon # ------------------------------------------------------------------------- def draw_windspeed_icon(self): self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['WindSpeedIcon'], 'Icon') # ------------------------------------------------------------------------- # draw_windspeed # ------------------------------------------------------------------------- def draw_windspeed(self): self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['WindSpeed'], 'blank') self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['WindSpeed'], self.__CurrentWeatherWind) # ------------------------------------------------------------------------- # draw_rainfall # ------------------------------------------------------------------------- def draw_rainfall(self): whattodraw = 'rain' + str(self.__CurrentWeatherRain) self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['RainSnow'], whattodraw) # ------------------------------------------------------------------------- # draw_snowfall # ------------------------------------------------------------------------- def draw_snowfall(self): whattodraw = 'snow' + str(self.__CurrentWeatherSnow) self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['RainSnow'], whattodraw) # ------------------------------------------------------------------------- # draw_weather_icon # ------------------------------------------------------------------------- def draw_weather_icon(self): self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['WeatherIcon'], self.__CurrentWeatherIcon) # ------------------------------------------------------------------------- # Draw the Weather Time indicator # ------------------------------------------------------------------------- def draw_weather_time(self): self.__WeatherCanvas.draw_on_canvas(self.__WeatherDefinition['WeatherTime'], self.__CurrentWeatherTime) # ------------------------------------------------------------------------- # draw_on_matrix_canvas # Draw the Weather canvas onto the LED Matrix canvas if it has changed # and only if 'Autodraw' is true # ------------------------------------------------------------------------- def draw_on_matrix_canvas(self): if self.__WeatherDefinition['AutoDraw']: if self.__ImageChanged: self.__Matrix.paste_to_matrix_canvas(self.__MatrixPosition, self.__WeatherCanvas) self.__ImageChanged = False # ------------------------------------------------------------------------- # # ------------------------------------------------------------------------- def add_image_width(self, x, imagedef): return x + imagedef[2] - imagedef[0] + 1 # ------------------------------------------------------------------------- # Draw the Temperature # Draws the temperature in the form of # Max/Min icon, temperature, Unit # ------------------------------------------------------------------------- def draw_temperature(self, MaxMin, Temperature, TempUnit): x = self.__WeatherDefinition[MaxMin][0] y = self.__WeatherDefinition[MaxMin][1] image = self.__WeatherDefinition[MaxMin][2] imagedefinition = self.__WeatherDefinition[MaxMin][3] # The Max/Min arrow if MaxMin == 'MaxTemp': self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), 'max') x = Weather.add_image_width(self, x, imagedefinition['max']) else: self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), 'min') x = Weather.add_image_width(self, x, imagedefinition['min']) # Temperature is below 0 if Temperature < 0: self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), '-') x = Weather.add_image_width(self, x, imagedefinition['-']) else: self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), 'ssp') x = Weather.add_image_width(self, x, imagedefinition['ssp']) # 10's digit if Temperature >= 10.0: tensdigit = str(abs(int(Temperature)))[0] self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), tensdigit) x = Weather.add_image_width(self, x, imagedefinition[tensdigit]) else: tensdigit = 0 self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), 'sp') x = Weather.add_image_width(self, x, imagedefinition['sp']) # Units digit unitdigit = str(abs(int(Temperature)) - int(tensdigit) * 10) self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), unitdigit) x = Weather.add_image_width(self, x, imagedefinition[unitdigit]) # Decimal point self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), '.') x = Weather.add_image_width(self, x, imagedefinition['.']) # Decimal Dec = str(int(abs(Temperature) * 10 % 10)) self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), Dec) x = Weather.add_image_width(self, x, imagedefinition[Dec]) # Temperature unit self.__WeatherCanvas.draw_on_canvas((x, y, image, imagedefinition), TempUnit) # ------------------------------------------------------------------------- # get_offset_date # Calculate the date for the forecast (offset hours from now) # ------------------------------------------------------------------------- def get_offset_date(self, offset): forecastdate = datetime.now() + timedelta(hours=offset) forecasthour = int(forecastdate.strftime('%H')) forecastseachhour = forecasthour - forecasthour % 3 return forecastdate.strftime('%Y') + '-' + forecastdate.strftime('%m') + '-' + \ forecastdate.strftime('%d') + ' ' + '{:02}'.format(forecastseachhour) + ':00:00' # ------------------------------------------------------------------------- # get_readable_forecast # Converts the weather for the defined time (weather_forecast_time) into a # readable (list) format # ------------------------------------------------------------------------- def get_readable_forecast(self, weatherdict): # logging.info('Putting the weather in readable format') weatherdata = {} if weatherdict == {}: logging.warning('The json was blank!') return {} try: # weatherdata['cloudiness'] = weatherdict['clouds']['all'] # weatherdata['CurrentTemp'] = round(float(weatherdict['main']['temp']) - 273.0, 1) weatherdata['MaxTemp'] = self.kelvin_to_celcius(weatherdict['main']['temp_max']) weatherdata['MinTemp'] = self.kelvin_to_celcius(weatherdict['main']['temp_min']) weatherdata['humidity'] = int(weatherdict['main']['humidity']) # If [rain][3h] is in the forecast, set it, or set rainfall to 0mm if 'rain' in weatherdict: if '3h' in weatherdict['rain']: weatherdata['rain'] = min(8.0, math.ceil(float(weatherdict['rain']['3h'] * 2)) / 2.0) else: weatherdata['rain'] = 0.0 else: weatherdata['rain'] = 0.0 if 'snow' in weatherdict: if '3h' in weatherdict['snow']: weatherdata['snow'] = min(16, int(math.ceil(float(weatherdict['snow']['3h'])))) else: weatherdata['snow'] = 0 else: weatherdata['snow'] = -1 weatherdata['description'] = weatherdict['weather'][0]['description'] weatherdata['wind'] = float(weatherdict['wind']['speed']) weatherdata['beaufort'] = self.windspeed_to_beaufort(float(weatherdict['wind']['speed'])) weatherdata['winddir'] = self.degrees_to_direction(float(weatherdict['wind']['deg'])) weatherdata['icon'] = weatherdict['weather'][0]['icon'] weatherdata['WeatherTime'] = time.strptime(weatherdict['dt_txt'], '%Y-%m-%d %H:%M:%S').tm_hour weatherdata['dt_txt'] = weatherdict['dt_txt'] return weatherdata except: logging.warning('Error get_readable_forecast') return {} # ------------------------------------------------------------------------- # kelvin_to_celcius # Converts the temperature from Kelvins to celcius # ------------------------------------------------------------------------- def kelvin_to_celcius(self, kelvin): return round(float(kelvin) - 273.15, 1) # ------------------------------------------------------------------------- # windspeed_to_beaufort # Converts the wind speed from m/s to Beaufort scale # ------------------------------------------------------------------------- def windspeed_to_beaufort(self, windspeedmps): beaufort = 0 if windspeedmps <= 2.0: beaufort = 1 elif windspeedmps <= 3.0: beaufort = 2 elif windspeedmps <= 5.0: beaufort = 3 elif windspeedmps <= 8.0: beaufort = 4 elif windspeedmps <= 11.0: beaufort = 5 elif windspeedmps <= 14.0: beaufort = 6 elif windspeedmps <= 17.0: beaufort = 7 elif windspeedmps <= 21.0: beaufort = 8 elif windspeedmps <= 24.0: beaufort = 9 elif windspeedmps <= 28.0: beaufort = 10 elif windspeedmps <= 32.0: beaufort = 11 elif windspeedmps > 32.0: beaufort = 12 return beaufort # ------------------------------------------------------------------------- # degrees_to_direction # Convert the wind direction from degrees to compass direction # ------------------------------------------------------------------------- def degrees_to_direction(self, degrees): try: degrees = float(degrees) except ValueError: return None if degrees < 0 or degrees > 360: return None if degrees <= 11.25 or degrees >= 348.76: return "N" elif degrees <= 33.75: return "NNE" elif degrees <= 56.25: return "NE" elif degrees <= 78.75: return "ENE" elif degrees <= 101.25: return "E" elif degrees <= 123.75: return "ESE" elif degrees <= 146.25: return "SE" elif degrees <= 168.75: return "SSE" elif degrees <= 191.25: return "S" elif degrees <= 213.75: return "SSW" elif degrees <= 236.25: return "SW" elif degrees <= 258.75: return "WSW" elif degrees <= 281.25: return "W" elif degrees <= 303.75: return "WNW" elif degrees <= 326.25: return "NW" elif degrees <= 348.75: return "NNW" else: return None