Skip to content

Commit

Permalink
Merge branch 'release/v3.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
peted-davis committed Jun 8, 2020
2 parents 5452ee8 + dcb11f7 commit d642534
Show file tree
Hide file tree
Showing 20 changed files with 937 additions and 205 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ copyright 2015-2020 Brian Underdown. The Weather34 Home Weather Station Template
is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
International License.

http://weatherflow.com/smart-home-weather-stations/
https://weatherflow.com/tempest-weather-system/
https://community.weatherflow.com/

## Contents
Expand All @@ -29,7 +29,7 @@ be started from the terminal with a single command. The automated installation
should take ~1 hour.

The automated installer assumes you have already sucesfully setup your Raspberry
Pi and have installed Raspbian Buster with Desktop. You should have also
Pi and have installed Raspberry Pi OS with Desktop. You should have also
attached the touch screen, and have either a keyboard and mouse attached
directly to the Pi, or have accessesd the Pi remotely through SSH/VNC. If you
are starting from scratch, the Raspberry Pi documentation should help get you
Expand Down
1 change: 1 addition & 0 deletions atlas/wfpiconsole.atlas
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"wfpiconsole_atlas.png": {"tab_btn": [2, 12, 10, 10], "tab_btn_pressed": [2, 0, 10, 10]}}
Binary file added atlas/wfpiconsole_atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed background/credits.png
Binary file not shown.
Binary file removed buttons/credits.png
Binary file not shown.
Binary file removed buttons/creditsPressed.png
Binary file not shown.
Binary file added buttons/mainMenu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added buttons/mainMenuPressed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed buttons/settings.png
Binary file not shown.
Binary file removed buttons/settingsPressed.png
Binary file not shown.
69 changes: 32 additions & 37 deletions lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import os

# Define wfpiconsole version number
Version = 'v3.4'
Version = 'v3.5'

# Define required variables
TEMPEST = False
Expand Down Expand Up @@ -169,8 +169,8 @@ def copyConfigKey(newConfig,currentConfig,Section,Key,keyDetails):
if keyDetails['Type'] == 'fixed':
Value = keyDetails['Value']

# Copy key value from existing configuration. Ignore AIR/SKY module IDs if
# switching to TEMPEST module
# Copy key value from existing configuration. Ignore AIR/SKY device IDs if
# switching to TEMPEST
else:
if (Key == 'SkyID' or Key == 'SkyHeight') and TEMPEST:
Value = ''
Expand Down Expand Up @@ -207,21 +207,21 @@ def writeConfigKey(Config,Section,Key,keyDetails):
# Define global variables
global TEMPEST, INDOORAIR

# Request user input to determine which modules are present
# Request user input to determine which devices are present
if Key == 'TempestID':
if queryUser('Do you own a TEMPEST module?*',None):
if queryUser('Do you own a TEMPEST?*',None):
TEMPEST = True
else:
Value = ''
keyRequired = False
elif Key == 'InAirID':
if queryUser('Do you own an Indoor AIR module?*',None):
if queryUser('Do you own an Indoor AIR?*',None):
INDOORAIR = True
else:
Value = ''
keyRequired = False

# Skip module ID keys for modules that are not present
# Skip device ID keys for devices that are not present
if Key == 'SkyID' and TEMPEST:
Value = ''
keyRequired = False
Expand Down Expand Up @@ -379,8 +379,7 @@ def writeConfigKey(Config,Section,Key,keyDetails):
METOFFICE = METOFFICE.json()
break

# Validate TEMPEST module ID and get height above ground of TEMPEST
# module
# Validate TEMPEST device ID and get height above ground of TEMPEST
if Section == 'Station':
if Key == 'TempestHeight' and Config['Station']['TempestID']:
while True:
Expand All @@ -390,22 +389,22 @@ def writeConfigKey(Config,Section,Key,keyDetails):
if Device['device_type'] == 'ST':
Value = Device['device_meta']['agl']
if not Value:
inputStr = ' TEMPEST module not found. Please re-enter your TEMPEST ID*: '
inputStr = ' TEMPEST not found. Please re-enter your TEMPEST device ID*: '
while True:
ID = input(inputStr)
if not ID:
print(' TEMPEST ID cannot be empty. Please try again')
print(' TEMPEST device ID cannot be empty. Please try again')
continue
try:
ID = int(ID)
break
except ValueError:
inputStr = ' TEMPEST ID not valid. Please re-enter your TEMPEST ID*: '
inputStr = ' TEMPEST device ID not valid. Please re-enter your TEMPEST device ID*: '
Config.set('Station','TempestID',str(ID))
else:
break

# Validate AIR module ID and get height above ground of AIR module
# Validate AIR device ID and get height above ground of AIR
if Section == 'Station':
if Key == 'OutAirHeight' and Config['Station']['OutAirID']:
while True:
Expand All @@ -415,22 +414,22 @@ def writeConfigKey(Config,Section,Key,keyDetails):
if Device['device_type'] == 'AR':
Value = Device['device_meta']['agl']
if not Value:
inputStr = ' Outdoor AIR module not found. Please re-enter your Outdoor AIR ID*: '
inputStr = ' Outdoor AIR not found. Please re-enter your Outdoor AIR device ID*: '
while True:
ID = input(inputStr)
if not ID:
print(' Outdoor AIR module ID cannot be empty. Please try again')
print(' Outdoor AIR device ID cannot be empty. Please try again')
continue
try:
ID = int(ID)
break
except ValueError:
inputStr = ' Outdoor AIR ID not valid. Please re-enter your Outdoor AIR ID*: '
inputStr = ' Outdoor AIR device ID not valid. Please re-enter your Outdoor AIR device ID*: '
Config.set('Station','OutAirID',str(ID))
else:
break

# Validate SKY module ID and get height above ground of SKY module
# Validate SKY device ID and get height above ground of SKY
if Section == 'Station':
if Key == 'SkyHeight' and Config['Station']['SkyID']:
while True:
Expand All @@ -440,17 +439,17 @@ def writeConfigKey(Config,Section,Key,keyDetails):
if Device['device_type'] == 'SK':
Value = Device['device_meta']['agl']
if not Value:
inputStr = ' SKY module not found. Please re-enter your SKY ID*: '
inputStr = ' SKY not found. Please re-enter your SKY device ID*: '
while True:
ID = input(inputStr)
if not ID:
print(' SKY module ID cannot be empty. Please try again')
print(' SKY device ID cannot be empty. Please try again')
continue
try:
ID = int(ID)
break
except ValueError:
inputStr = ' SKY module ID not valid. Please re-enter your SKY ID*: '
inputStr = ' SKY device ID not valid. Please re-enter your SKY device ID*: '
Config.set('Station','SkyID',str(ID))
else:
break
Expand Down Expand Up @@ -488,24 +487,19 @@ def writeConfigKey(Config,Section,Key,keyDetails):
else:
Value = ''

# Get station latitude/longitude
# Get station latitude/longitude, timezone, or name
if Section == 'Station':
if Key in ['Latitude','Longitude']:
if Key in ['Latitude','Longitude','Timezone','Name']:
Value = STATION['stations'][0][Key.lower()]

# Get station timezone
if Section == 'Station':
if Key in ['Timezone']:
Value = STATION['stations'][0]['timezone']

# Get station elevation
if Section == 'Station':
if Key in ['Elevation']:
if Key == 'Elevation':
Value = STATION['stations'][0]['station_meta']['elevation']

# Get station country code
if Section == 'Station':
if Key in ['Country']:
if Key == 'Country':
Value = GEONAMES['geonames'][0]['countryCode']

# Get station units
Expand Down Expand Up @@ -571,19 +565,20 @@ def defaultConfig():
('DarkSky', {'Type': 'userInput', 'State': 'optional', 'Format': str, 'Desc': 'DarkSky API Key (if you have one)',}),
('CheckWX', {'Type': 'userInput', 'State': 'required', 'Format': str, 'Desc': 'CheckWX API Key',}),
('WeatherFlow', {'Type': 'fixed', 'Value': '146e4f2c-adec-4244-b711-1aeca8f46a48', 'Desc': 'WeatherFlow API Key'})])
Default['Station'] = collections.OrderedDict([('Description', ' Station and module IDs'),
Default['Station'] = collections.OrderedDict([('Description', ' Station and device IDs'),
('StationID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'Station ID'}),
('TempestID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'TEMPEST module ID'}),
('SkyID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'SKY module ID'}),
('OutAirID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'outdoor AIR module ID'}),
('InAirID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'indoor AIR module ID'}),
('TempestHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of TEMPEST module'}),
('SkyHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of SKY module'}),
('OutAirHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of outdoor AIR module'}),
('TempestID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'TEMPEST device ID'}),
('SkyID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'SKY device ID'}),
('OutAirID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'outdoor AIR device ID'}),
('InAirID', {'Type': 'userInput', 'State': 'required', 'Format': int, 'Desc': 'indoor AIR device ID'}),
('TempestHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of TEMPEST'}),
('SkyHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of SKY'}),
('OutAirHeight', {'Type': 'request', 'Source': 'station', 'Desc': 'height of outdoor AIR'}),
('Latitude', {'Type': 'request', 'Source': 'station', 'Desc': 'station latitude'}),
('Longitude', {'Type': 'request', 'Source': 'station', 'Desc': 'station longitude'}),
('Elevation', {'Type': 'request', 'Source': 'station', 'Desc': 'station elevation'}),
('Timezone', {'Type': 'request', 'Source': 'station', 'Desc': 'station timezone'}),
('Name', {'Type': 'request', 'Source': 'station', 'Desc': 'station name'}),
('Country', {'Type': 'request', 'Source': 'GeoNames', 'Desc': 'station country'}),
('ForecastLocn', {'Type': 'request', 'Source': 'MetOffice', 'Desc': 'station forecast location'}),
('MetOfficeID', {'Type': 'request', 'Source': 'MetOffice', 'Desc': 'station forecast ID'})])
Expand Down
73 changes: 15 additions & 58 deletions lib/derivedVariables.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ def SLPTrend(Pres,Time,Data3h,Config):
if requestAPI.weatherflow.verifyResponse(Data3h,'obs'):
Data3h = Data3h.json()['obs']
if Config['Station']['OutAirID']:
Pres3h = [Data3h[0][1],'mb']
Pres3h = [Data3h[0][1] if Data3h[0][1] != None else NaN,'mb']
elif Config['Station']['TempestID']:
Pres3h = [Data3h[0][6],'mb']
Pres3h = [Data3h[0][6] if Data3h[0][6] != None else NaN,'mb']
else:
Pres3h = [NaN,'mb']

Expand Down Expand Up @@ -255,7 +255,7 @@ def SLPMaxMin(Time,Pres,maxPres,minPres,Device,Config,flagAPI):
# Define current time in station timezone
Tz = pytz.timezone(Config['Station']['Timezone'])
Now = datetime.now(pytz.utc).astimezone(Tz)

# Set time format based on user configuration
if Config['Display']['TimeFormat'] == '12 hr':
if Config['System']['Hardware'] != 'Other':
Expand Down Expand Up @@ -286,7 +286,7 @@ def SLPMaxMin(Time,Pres,maxPres,minPres,Device,Config,flagAPI):
# Calculate sea level pressure
SLP = [derive.SLP(P,Config) for P in Pres]

# Define maximum and minimum pressure.
# Define maximum and minimum pressure.
MaxPres = [max(SLP)[0],'mb',datetime.fromtimestamp(Time[SLP.index(max(SLP))],Tz).strftime(Format),max(SLP)[0],Now]
MinPres = [min(SLP)[0],'mb',datetime.fromtimestamp(Time[SLP.index(min(SLP))],Tz).strftime(Format),min(SLP)[0],Now]
else:
Expand Down Expand Up @@ -348,7 +348,7 @@ def TempMaxMin(Time,Temp,maxTemp,minTemp,Device,Config,flagAPI):
# Define current time in station timezone
Tz = pytz.timezone(Config['Station']['Timezone'])
Now = datetime.now(pytz.utc).astimezone(Tz)

# Set time format based on user configuration
if Config['Display']['TimeFormat'] == '12 hr':
if Config['System']['Hardware'] != 'Other':
Expand Down Expand Up @@ -547,12 +547,12 @@ def StrikeCount(Count,strikeCount,Device,Config,flagAPI):
monthStrikes = [sum(x for x in Strikes),'count',sum(x for x in Strikes),Now]
else:
monthStrikes = [NaN,'count',NaN,Now]
# Adjust monthly lightning strike total for strikes that have been

# Adjust monthly lightning strike total for strikes that have been
# recorded today
if not math.isnan(todayStrikes[0]):
monthStrikes[0] += todayStrikes[0]
monthStrikes[2] += todayStrikes[2]
monthStrikes[2] += todayStrikes[2]

# Code initialising. Download all data for current year using
# Weatherflow API and calculate total yearly lightning strikes
Expand All @@ -572,12 +572,12 @@ def StrikeCount(Count,strikeCount,Device,Config,flagAPI):
yearStrikes = [sum(x for x in Strikes),'count',sum(x for x in Strikes),Now]
else:
yearStrikes = [NaN,'count',NaN,Now]
# Adjust monthly lightning strike total for strikes that have been

# Adjust monthly lightning strike total for strikes that have been
# recorded today
if not math.isnan(todayStrikes[0]):
yearStrikes[0] += todayStrikes[0]
yearStrikes[2] += todayStrikes[2]
yearStrikes[2] += todayStrikes[2]

# Return Daily, Monthly, and Yearly lightning strike counts
return {'Today':todayStrikes, 'Month':monthStrikes, 'Year':yearStrikes}
Expand Down Expand Up @@ -730,12 +730,12 @@ def RainAccumulation(Rain,rainAccum,Device,Config,flagAPI):
MonthRain = [sum(x for x in Rain),'mm',sum(x for x in Rain),Now]
else:
MonthRain = [NaN,'mm',NaN,Now]

# Adjust monthly rainfall total for rain that has fallen today
if not math.isnan(TodayRain[0]):
MonthRain[0] += TodayRain[0]
MonthRain[2] += TodayRain[2]

# Code initialising. Download all data for current year using
# Weatherflow API and calculate total yearly rainfall
if rainAccum['Year'][0] == '-' or flagAPI:
Expand All @@ -753,12 +753,12 @@ def RainAccumulation(Rain,rainAccum,Device,Config,flagAPI):
YearRain = [sum(x for x in Rain),'mm',sum(x for x in Rain),Now]
else:
YearRain = [NaN,'mm',NaN,Now]

# Adjust yearly rainfall total for rain that has fallen today
if not math.isnan(TodayRain[0]):
YearRain[0] += TodayRain[0]
YearRain[2] += TodayRain[2]

# Return Daily, Monthly, and Yearly rainfall accumulation totals
return {'Today':TodayRain, 'Yesterday':YesterdayRain, 'Month':MonthRain, 'Year':YearRain}

Expand Down Expand Up @@ -1066,12 +1066,9 @@ def peakSunHours(Radiation,peakSun,Astro,Device,Config,flagAPI):
else:
watthrs = peakSun[2] + Radiation[0]*1/60 if not math.isnan(Radiation[0]) else peakSun[2]
peakSun = [watthrs/1000,'hrs',watthrs,Now]
if math.isnan(Radiation[0]):
print("Radiation is NaN")

# Calculate proportion of daylight hours that have passed
daylightTotal = (Astro['Sunset'][0] - Astro['Sunrise'][0]).total_seconds()

if Astro['Sunrise'][0] <= Now <= Astro['Sunset'][0]:
daylightElapsed = (Now - Astro['Sunrise'][0]).total_seconds()
else:
Expand All @@ -1094,43 +1091,3 @@ def peakSunHours(Radiation,peakSun,Astro,Device,Config,flagAPI):

# Return Peak Sun Hours
return peakSun

# # CHECK STATUS OF SKY AND AIR MODULES
# # --------------------------------------------------------------------------
# def SkyAirStatus(self,dt):

# # Define current time in station timezone
# Tz = pytz.timezone(self.config['Station']['Timezone'])
# Now = datetime.now(pytz.utc).astimezone(Tz)

# # Check latest AIR observation time is less than 5 minutes old and
# # battery voltage is greater than 1.9 v
# if 'Obs' in self.Obs:
# AirTime = datetime.fromtimestamp(self.Obs['Obs'][0],Tz)
# AirDiff = (Now - AirTime).total_seconds()
# if self.Obs['Battery'][0] != '-':
# AirVoltage = float(self.Obs['Battery'][0])
# else:
# AirVoltage = 0;
# if AirDiff < 300 and AirVoltage > 1.9:
# self.Obs['StatusIcon'] = 'OK'

# # Latest AIR observation time is greater than 5 minutes old
# else:
# self.Obs['StatusIcon'] = 'Error'

# # Check latest Sky observation time is less than 5 minutes old and
# # battery voltage is greater than 2.0 v
# if 'Obs' in self.Obs:
# SkyTime = datetime.fromtimestamp(self.Obs['Obs'][0],Tz)
# SkyDiff = (Now - SkyTime).total_seconds()
# if self.Obs['Battery'][0] != '-':
# SkyVoltage = float(self.Obs['Battery'][0])
# else:
# SkyVoltage = 0;
# if SkyDiff < 300 and SkyVoltage > 2.0:
# self.Obs['StatusIcon'] = 'OK'

# # Latest Sky observation time is greater than 5 minutes old
# else:
# self.Obs['StatusIcon'] = 'Error'
Loading

0 comments on commit d642534

Please sign in to comment.