def rapidWind(Msg,wfpiconsole): """ Handles RapidWind Websocket messages received from either SKY or TEMPEST module INPUTS: Msg Websocket messages received from SKY or TEMPEST wfpiconsole wfpiconsole object """ # Replace missing observations from Rapid Wind Websocket JSON # with NaN Ob = [x if x != None else NaN for x in Msg['ob']] # Discard duplicate Rapid Wind Websocket messages if 'RapidMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['RapidMsg']['ob'][0] == Ob[0]: print('Discarding duplicate Rapid Wind Websocket message') return # Extract observations from latest Rapid Wind Websocket JSON Time = [Ob[0],'s'] WindSpd = [Ob[1],'mps'] WindDir = [Ob[2],'degrees'] # Extract wind direction from previous SKY Rapid-Wind Websocket JSON if 'RapidMsg' in wfpiconsole.Obs: Ob = [x if x != None else NaN for x in wfpiconsole.Obs['RapidMsg']['ob']] WindDirOld = [Ob[2],'degrees'] else: WindDirOld = [0,'degrees'] # If windspeed is zero, freeze direction at last direction of non-zero wind # speed and edit latest Rapid Wind Websocket JSON. Calculate wind shift if WindSpd[0] == 0: WindDir = WindDirOld Msg['ob'][2] = WindDirOld[0] # Store latest Rapid Wind wfpiconsole.Observation JSON message wfpiconsole.Obs['RapidMsg'] = Msg # Calculate derived variables from Rapid Wind observations WindDir = derive.CardinalWindDirection(WindDir,WindSpd) # Convert observation units as required WindSpd = observation.Units(WindSpd,wfpiconsole.config['Units']['Wind']) WindDir = observation.Units(WindDir,'degrees') # Update wfpiconsole display with derived Rapid Wind observations wfpiconsole.Obs['rapidShift'] = WindDir[0] - WindDirOld[0] wfpiconsole.Obs['rapidSpd'] = observation.Format(WindSpd,'Wind') wfpiconsole.Obs['rapidDir'] = observation.Format(WindDir,'Direction') # Animate wind rose arrow if WindSpeedPanel panel is active if hasattr(wfpiconsole,'WindSpeedPanel'): for panel in getattr(wfpiconsole,'WindSpeedPanel'): panel.animateWindRose() # Return wfpiconsole object return wfpiconsole
def indoorAir(Msg, wfpiconsole): """ Handles Websocket messages received from indoor AIR module INPUTS: Msg Websocket messages received from indoor AIR module wfpiconsole wfpiconsole object """ # Replace missing observations in latest indoor AIR Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate indoor AIR Websocket messages if 'inAirMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['inAirMsg']['obs'][0] == Ob[0]: print('Discarding duplicate indoor AIR Websocket message') return # Extract indoor AIR device ID and API flag, and station configuration # object Device = wfpiconsole.config['Station']['InAirID'] flagAPI = wfpiconsole.flagAPI[3] Config = wfpiconsole.config # Extract required observations from latest indoor AIR Websocket JSON Time = [Ob[0], 's'] Temp = [Ob[2], 'c'] # Store latest indoor AIR Websocket message wfpiconsole.Obs['inAirMsg'] = Msg # Extract required derived observations minTemp = wfpiconsole.Obs['inTempMin'] maxTemp = wfpiconsole.Obs['inTempMax'] # Calculate derived variables from indoor AIR observations MaxTemp, MinTemp = derive.TempMaxMin(Time, Temp, maxTemp, minTemp, Device, Config, flagAPI) # Convert observation units as required Temp = observation.Units(Temp, Config['Units']['Temp']) MaxTemp = observation.Units(MaxTemp, Config['Units']['Temp']) MinTemp = observation.Units(MinTemp, Config['Units']['Temp']) # Store derived indoor AIR observations in Data dictionary derivedObs = {} derivedObs['inTemp'] = observation.Format(Temp, 'Temp') derivedObs['inTempMax'] = observation.Format(MaxTemp, 'Temp') derivedObs['inTempMin'] = observation.Format(MinTemp, 'Temp') # Update wfpiconsole display with derived indoor AIR observations updateDisplay(derivedObs, wfpiconsole, 'indoorAir') # Set flags for required API calls wfpiconsole.flagAPI[3] = 0 # Return wfpiconsole object return wfpiconsole
def evtStrike(Msg, wfpiconsole): """ Handles lightning strike event Websocket messages received from either AIR or TEMPEST module INPUTS: Msg Websocket messages received from AIR or TEMPEST wfpiconsole wfpiconsole object """ # Discard duplicate evt_strike Websocket messages if 'evtStrikeMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['evtStrikeMsg']['evt'][0] == Msg['evt'][0]: print('Discarding duplicate evt_strike Websocket message') return # Extract required observations from latest evt_strike Websocket JSON StrikeTime = [Msg['evt'][0], 's'] StrikeDist = [Msg['evt'][1], 'km'] # Store latest Rapid Wind wfpiconsole.Observation JSON message wfpiconsole.Obs['evtStrikeMsg'] = Msg # Calculate derived variables from evt_strike observations StrikeDeltaT = derive.StrikeDeltaT(StrikeTime) # Convert observation units as required StrikeDist = observation.Units(StrikeDist, wfpiconsole.config['Units']['Distance']) # Update wfpiconsole display with derived Rapid Wind observations wfpiconsole.Obs['StrikeDeltaT'] = observation.Format( StrikeDeltaT, 'TimeDelta') wfpiconsole.Obs['StrikeDist'] = observation.Format(StrikeDist, 'StrikeDistance') # If required, open secondary lightning panel to show strike has been # detected if wfpiconsole.config['Display']['LightningPanel'] == '1': for ii, Button in enumerate(wfpiconsole.CurrentConditions.buttonList): if "Lightning" in Button[2]: wfpiconsole.CurrentConditions.SwitchPanel([], Button) # Set and animate lightning bolt icon if LightningPanel panel is active if hasattr(wfpiconsole, 'LightningPanel'): for panel in getattr(wfpiconsole, 'LightningPanel'): panel.setLightningBoltIcon() panel.animateLightningBoltIcon() # Return wfpiconsole object return wfpiconsole
def Tempest(Msg, wfpiconsole): """ Handles Websocket messages received from TEMPEST module INPUTS: Msg Websocket messages received from TEMPEST module wfpiconsole wfpiconsole object """ # Replace missing observations from latest TEMPEST Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate TEMPEST Websocket messages if 'TempestMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['TempestMsg']['obs'][0] == Ob[0]: print('Discarding duplicate TEMPEST Websocket message') return # Extract TEMPEST device ID, API flag, and station configuration object Device = wfpiconsole.config['Station']['TempestID'] flagAPI = wfpiconsole.flagAPI[0] Config = wfpiconsole.config # Extract required observations from latest TEMPEST Websocket JSON Time = [Ob[0], 's'] WindSpd = [Ob[2], 'mps'] WindGust = [Ob[3], 'mps'] WindDir = [Ob[4], 'degrees'] Pres = [Ob[6], 'mb'] Temp = [Ob[7], 'c'] Humidity = [Ob[8], '%'] UV = [Ob[10], 'index'] Radiation = [Ob[11], 'Wm2'] Rain = [Ob[12], 'mm'] Strikes = [Ob[15], 'count'] # Extract lightning strike data from the latest AIR Websocket JSON "Summary" # object StrikeTime = [ Msg['summary']['strike_last_epoch'] if 'strike_last_epoch' in Msg['summary'] else NaN, 's' ] StrikeDist = [ Msg['summary']['strike_last_dist'] if 'strike_last_dist' in Msg['summary'] else NaN, 'km' ] Strikes3hr = [ Msg['summary']['strike_count_3h'] if 'strike_count_3h' in Msg['summary'] else NaN, 'count' ] # Store latest TEMPEST Websocket message wfpiconsole.Obs['TempestMsg'] = Msg # Extract required derived observations minPres = wfpiconsole.Obs['MinPres'] maxPres = wfpiconsole.Obs['MaxPres'] minTemp = wfpiconsole.Obs['outTempMin'] maxTemp = wfpiconsole.Obs['outTempMax'] StrikeCount = { 'Today': wfpiconsole.Obs['StrikesToday'], 'Month': wfpiconsole.Obs['StrikesMonth'], 'Year': wfpiconsole.Obs['StrikesYear'] } rainAccum = { 'Today': wfpiconsole.Obs['TodayRain'], 'Yesterday': wfpiconsole.Obs['YesterdayRain'], 'Month': wfpiconsole.Obs['MonthRain'], 'Year': wfpiconsole.Obs['YearRain'] } peakSun = wfpiconsole.Obs['peakSun'] avgWind = wfpiconsole.Obs['AvgWind'] maxGust = wfpiconsole.Obs['MaxGust'] # Request TEMPEST data from the previous three hours Data3h = requestAPI.weatherflow.Last3h(Device, Time[0], Config) # Calculate derived variables from TEMPEST observations DewPoint = derive.DewPoint(Temp, Humidity) SLP = derive.SLP(Pres, Config) PresTrend = derive.SLPTrend(Pres, Time, Data3h, Config) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) MaxTemp, MinTemp = derive.TempMaxMin(Time, Temp, maxTemp, minTemp, Device, Config, flagAPI) MaxPres, MinPres = derive.SLPMaxMin(Time, Pres, maxPres, minPres, Device, Config, flagAPI) StrikeCount = derive.StrikeCount(Strikes, StrikeCount, Device, Config, flagAPI) StrikeFreq = derive.StrikeFrequency(Time, Data3h, Config) StrikeDeltaT = derive.StrikeDeltaT(StrikeTime) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) RainRate = derive.RainRate(Rain) rainAccum = derive.RainAccumulation(Rain, rainAccum, Device, Config, flagAPI) AvgWind = derive.MeanWindSpeed(WindSpd, avgWind, Device, Config, flagAPI) MaxGust = derive.MaxWindGust(WindGust, maxGust, Device, Config, flagAPI) WindSpd = derive.BeaufortScale(WindSpd) WindDir = derive.CardinalWindDirection(WindDir, WindSpd) peakSun = derive.peakSunHours(Radiation, peakSun, wfpiconsole.Astro, Device, Config, flagAPI) UVIndex = derive.UVIndex(UV) # Convert observation units as required Temp = observation.Units(Temp, Config['Units']['Temp']) MaxTemp = observation.Units(MaxTemp, Config['Units']['Temp']) MinTemp = observation.Units(MinTemp, Config['Units']['Temp']) DewPoint = observation.Units(DewPoint, Config['Units']['Temp']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) SLP = observation.Units(SLP, Config['Units']['Pressure']) MaxPres = observation.Units(MaxPres, Config['Units']['Pressure']) MinPres = observation.Units(MinPres, Config['Units']['Pressure']) PresTrend = observation.Units(PresTrend, Config['Units']['Pressure']) StrikeDist = observation.Units(StrikeDist, Config['Units']['Distance']) RainRate = observation.Units(RainRate, Config['Units']['Precip']) TodayRain = observation.Units(rainAccum['Today'], Config['Units']['Precip']) YesterdayRain = observation.Units(rainAccum['Yesterday'], Config['Units']['Precip']) MonthRain = observation.Units(rainAccum['Month'], Config['Units']['Precip']) YearRain = observation.Units(rainAccum['Year'], Config['Units']['Precip']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) WindDir = observation.Units(WindDir, Config['Units']['Direction']) WindGust = observation.Units(WindGust, Config['Units']['Wind']) AvgWind = observation.Units(AvgWind, Config['Units']['Wind']) MaxGust = observation.Units(MaxGust, Config['Units']['Wind']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) # Store derived TEMPEST observations in dictionary derivedObs = {} derivedObs['outTemp'] = observation.Format(Temp, 'Temp') derivedObs['outTempMax'] = observation.Format(MaxTemp, 'Temp') derivedObs['outTempMin'] = observation.Format(MinTemp, 'Temp') derivedObs['DewPoint'] = observation.Format(DewPoint, 'Temp') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['Pres'] = observation.Format(SLP, 'Pressure') derivedObs['MaxPres'] = observation.Format(MaxPres, 'Pressure') derivedObs['MinPres'] = observation.Format(MinPres, 'Pressure') derivedObs['PresTrend'] = observation.Format(PresTrend, 'Pressure') derivedObs['StrikeDeltaT'] = observation.Format(StrikeDeltaT, 'TimeDelta') derivedObs['StrikeDist'] = observation.Format(StrikeDist, 'StrikeDistance') derivedObs['StrikeFreq'] = observation.Format(StrikeFreq, 'StrikeFrequency') derivedObs['Strikes3hr'] = observation.Format(Strikes3hr, 'StrikeCount') derivedObs['StrikesToday'] = observation.Format(StrikeCount['Today'], 'StrikeCount') derivedObs['StrikesMonth'] = observation.Format(StrikeCount['Month'], 'StrikeCount') derivedObs['StrikesYear'] = observation.Format(StrikeCount['Year'], 'StrikeCount') derivedObs['Humidity'] = observation.Format(Humidity, 'Humidity') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['RainRate'] = observation.Format(RainRate, 'Precip') derivedObs['TodayRain'] = observation.Format(TodayRain, 'Precip') derivedObs['YesterdayRain'] = observation.Format(YesterdayRain, 'Precip') derivedObs['MonthRain'] = observation.Format(MonthRain, 'Precip') derivedObs['YearRain'] = observation.Format(YearRain, 'Precip') derivedObs['WindSpd'] = observation.Format(WindSpd, 'Wind') derivedObs['WindGust'] = observation.Format(WindGust, 'Wind') derivedObs['AvgWind'] = observation.Format(AvgWind, 'Wind') derivedObs['MaxGust'] = observation.Format(MaxGust, 'Wind') derivedObs['WindDir'] = observation.Format(WindDir, 'Direction') derivedObs['Radiation'] = observation.Format(Radiation, 'Radiation') derivedObs['peakSun'] = observation.Format(peakSun, 'peakSun') derivedObs['UVIndex'] = observation.Format(UVIndex, 'UV') # Update wfpiconsole display with derived TEMPEST observations updateDisplay(derivedObs, wfpiconsole, 'Tempest') # Set flags for required API calls wfpiconsole.flagAPI[0] = 0 # Return wfpiconsole object return wfpiconsole
def outdoorAir(Msg, wfpiconsole): """ Handles Websocket messages received from outdoor AIR module INPUTS: Msg Websocket messages received from outdoor AIR module wfpiconsole wfpiconsole object """ # Replace missing observations in latest outdoor AIR Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate outdoor AIR Websocket messages if 'outAirMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['outAirMsg']['obs'][0] == Ob[0]: print('Discarding duplicate outdoor AIR Websocket message') return # Extract outdoor AIR device ID and API flag, and station configuration # object Device = wfpiconsole.config['Station']['OutAirID'] flagAPI = wfpiconsole.flagAPI[2] Config = wfpiconsole.config # Extract required observations from latest outdoor AIR Websocket JSON Time = [Ob[0], 's'] Pres = [Ob[1], 'mb'] Temp = [Ob[2], 'c'] Humidity = [Ob[3], '%'] Strikes = [Ob[4], 'count'] # Extract lightning strike data from the latest outdoor AIR Websocket JSON # "Summary" object StrikeTime = [ Msg['summary']['strike_last_epoch'] if 'strike_last_epoch' in Msg['summary'] else NaN, 's' ] StrikeDist = [ Msg['summary']['strike_last_dist'] if 'strike_last_dist' in Msg['summary'] else NaN, 'km' ] Strikes3hr = [ Msg['summary']['strike_count_3h'] if 'strike_count_3h' in Msg['summary'] else NaN, 'count' ] # Extract required derived observations minPres = wfpiconsole.Obs['MinPres'] maxPres = wfpiconsole.Obs['MaxPres'] minTemp = wfpiconsole.Obs['outTempMin'] maxTemp = wfpiconsole.Obs['outTempMax'] StrikeCount = { 'Today': wfpiconsole.Obs['StrikesToday'], 'Month': wfpiconsole.Obs['StrikesMonth'], 'Year': wfpiconsole.Obs['StrikesYear'] } # Request outdoor AIR data from the previous three hours Data3h = requestAPI.weatherflow.Last3h(Device, Time[0], Config) # Store latest outdoor AIR Websocket message wfpiconsole.Obs['outAirMsg'] = Msg # Extract required observations from latest SKY Websocket JSON while not 'SkyMsg' in wfpiconsole.Obs: time.sleep(0.01) Ob = [x if x != None else NaN for x in wfpiconsole.Obs['SkyMsg']['obs'][0]] WindSpd = [Ob[5], 'mps'] # Calculate derived variables from AIR observations DewPoint = derive.DewPoint(Temp, Humidity) SLP = derive.SLP(Pres, Config) PresTrend = derive.SLPTrend(Pres, Time, Data3h, Config) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) MaxTemp, MinTemp = derive.TempMaxMin(Time, Temp, maxTemp, minTemp, Device, Config, flagAPI) MaxPres, MinPres = derive.SLPMaxMin(Time, Pres, maxPres, minPres, Device, Config, flagAPI) StrikeCount = derive.StrikeCount(Strikes, StrikeCount, Device, Config, flagAPI) StrikeFreq = derive.StrikeFrequency(Time, Data3h, Config) StrikeDeltaT = derive.StrikeDeltaT(StrikeTime) # Convert observation units as required Temp = observation.Units(Temp, Config['Units']['Temp']) MaxTemp = observation.Units(MaxTemp, Config['Units']['Temp']) MinTemp = observation.Units(MinTemp, Config['Units']['Temp']) DewPoint = observation.Units(DewPoint, Config['Units']['Temp']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) SLP = observation.Units(SLP, Config['Units']['Pressure']) MaxPres = observation.Units(MaxPres, Config['Units']['Pressure']) MinPres = observation.Units(MinPres, Config['Units']['Pressure']) PresTrend = observation.Units(PresTrend, Config['Units']['Pressure']) StrikeDist = observation.Units(StrikeDist, Config['Units']['Distance']) # Store derived outdoor AIR observations in dictionary derivedObs = {} derivedObs['outTemp'] = observation.Format(Temp, 'Temp') derivedObs['outTempMax'] = observation.Format(MaxTemp, 'Temp') derivedObs['outTempMin'] = observation.Format(MinTemp, 'Temp') derivedObs['DewPoint'] = observation.Format(DewPoint, 'Temp') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['Pres'] = observation.Format(SLP, 'Pressure') derivedObs['MaxPres'] = observation.Format(MaxPres, 'Pressure') derivedObs['MinPres'] = observation.Format(MinPres, 'Pressure') derivedObs['PresTrend'] = observation.Format(PresTrend, 'Pressure') derivedObs['StrikeDeltaT'] = observation.Format(StrikeDeltaT, 'TimeDelta') derivedObs['StrikeDist'] = observation.Format(StrikeDist, 'StrikeDistance') derivedObs['StrikeFreq'] = observation.Format(StrikeFreq, 'StrikeFrequency') derivedObs['Strikes3hr'] = observation.Format(Strikes3hr, 'StrikeCount') derivedObs['StrikesToday'] = observation.Format(StrikeCount['Today'], 'StrikeCount') derivedObs['StrikesMonth'] = observation.Format(StrikeCount['Month'], 'StrikeCount') derivedObs['StrikesYear'] = observation.Format(StrikeCount['Year'], 'StrikeCount') derivedObs['Humidity'] = observation.Format(Humidity, 'Humidity') # Update wfpiconsole display with derived outdoor AIR observations updateDisplay(derivedObs, wfpiconsole, 'outdoorAir') # Set flags for required API calls wfpiconsole.flagAPI[2] = 0 # Return wfpiconsole object return wfpiconsole
def Sky(Msg, wfpiconsole): """ Handles Websocket messages received from SKY module INPUTS: Msg Websocket messages received from SKY module wfpiconsole wfpiconsole object """ # Replace missing observations from latest SKY Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate SKY Websocket messages if 'SkyMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['SkyMsg']['obs'][0] == Ob[0]: print('Discarding duplicate SKY Websocket message') return # Extract SKY device ID and API flag, and station configuration object Device = wfpiconsole.config['Station']['SkyID'] flagAPI = wfpiconsole.flagAPI[1] Config = wfpiconsole.config # Extract required observations from latest SKY Websocket JSON Time = [Ob[0], 's'] UV = [Ob[2], 'index'] Rain = [Ob[3], 'mm'] WindSpd = [Ob[5], 'mps'] WindGust = [Ob[6], 'mps'] WindDir = [Ob[7], 'degrees'] Radiation = [Ob[10], 'Wm2'] # Store latest SKY Websocket message wfpiconsole.Obs['SkyMsg'] = Msg # Extract required observations from latest AIR Websocket observations while not 'outAirMsg' in wfpiconsole.Obs: time.sleep(0.01) Ob = [ x if x != None else NaN for x in wfpiconsole.Obs['outAirMsg']['obs'][0] ] Temp = [Ob[2], 'c'] Humidity = [Ob[3], '%'] # Set wind direction to None if wind speed is zero if WindSpd[0] == 0: WindDir = [None, 'degrees'] # Extract required derived observations rainAccum = { 'Today': wfpiconsole.Obs['TodayRain'], 'Yesterday': wfpiconsole.Obs['YesterdayRain'], 'Month': wfpiconsole.Obs['MonthRain'], 'Year': wfpiconsole.Obs['YearRain'] } peakSun = wfpiconsole.Obs['peakSun'] avgWind = wfpiconsole.Obs['AvgWind'] maxGust = wfpiconsole.Obs['MaxGust'] # Calculate derived variables from SKY observations FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) RainRate = derive.RainRate(Rain) rainAccum = derive.RainAccumulation(Rain, rainAccum, Device, Config, flagAPI) AvgWind = derive.MeanWindSpeed(WindSpd, avgWind, Device, Config, flagAPI) MaxGust = derive.MaxWindGust(WindGust, maxGust, Device, Config, flagAPI) WindSpd = derive.BeaufortScale(WindSpd) WindDir = derive.CardinalWindDirection(WindDir, WindSpd) peakSun = derive.peakSunHours(Radiation, peakSun, wfpiconsole.Astro, Device, Config, flagAPI) UVIndex = derive.UVIndex(UV) # Convert observation units as required RainRate = observation.Units(RainRate, Config['Units']['Precip']) TodayRain = observation.Units(rainAccum['Today'], Config['Units']['Precip']) YesterdayRain = observation.Units(rainAccum['Yesterday'], Config['Units']['Precip']) MonthRain = observation.Units(rainAccum['Month'], Config['Units']['Precip']) YearRain = observation.Units(rainAccum['Year'], Config['Units']['Precip']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) WindDir = observation.Units(WindDir, Config['Units']['Direction']) WindGust = observation.Units(WindGust, Config['Units']['Wind']) AvgWind = observation.Units(AvgWind, Config['Units']['Wind']) MaxGust = observation.Units(MaxGust, Config['Units']['Wind']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) # Store derived SKY observations in dictionary derivedObs = {} derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['RainRate'] = observation.Format(RainRate, 'Precip') derivedObs['TodayRain'] = observation.Format(TodayRain, 'Precip') derivedObs['YesterdayRain'] = observation.Format(YesterdayRain, 'Precip') derivedObs['MonthRain'] = observation.Format(MonthRain, 'Precip') derivedObs['YearRain'] = observation.Format(YearRain, 'Precip') derivedObs['WindSpd'] = observation.Format(WindSpd, 'Wind') derivedObs['WindGust'] = observation.Format(WindGust, 'Wind') derivedObs['AvgWind'] = observation.Format(AvgWind, 'Wind') derivedObs['MaxGust'] = observation.Format(MaxGust, 'Wind') derivedObs['WindDir'] = observation.Format(WindDir, 'Direction') derivedObs['Radiation'] = observation.Format(Radiation, 'Radiation') derivedObs['peakSun'] = observation.Format(peakSun, 'peakSun') derivedObs['UVIndex'] = observation.Format(UVIndex, 'UV') # Update wfpiconsole display with derived SKY observations updateDisplay(derivedObs, wfpiconsole, 'Sky') # Set flags for required API calls wfpiconsole.flagAPI[1] = 0 # Return wfpiconsole object return wfpiconsole
def Download(metData, Config, dt): """ Download the latest daily and hourly weather forecast data using the WeatherFlow BetterForecast API INPUTS: metData Dictionary holding weather forecast data Config Station configuration dt Time in seconds since function last called OUTPUT: metData Dictionary holding weather forecast data """ # Get current time in station time zone Tz = pytz.timezone(Config['Station']['Timezone']) funcCalled = datetime.now(pytz.utc).astimezone(Tz) Midnight = int( Tz.localize(datetime(funcCalled.year, funcCalled.month, funcCalled.day)).timestamp()) funcError = 0 # Set time format based on user configuration if Config['Display']['TimeFormat'] == '12 hr': if Config['System']['Hardware'] != 'Other': TimeFormat = '%-I %P' else: TimeFormat = '%I %p' else: TimeFormat = '%H:%M' # Download latest forecast data Data = requestAPI.weatherflow.Forecast(Config) # Verify API response and extract forecast if requestAPI.weatherflow.verifyResponse(Data, 'forecast'): metData['Dict'] = Data.json() else: funcError = 1 if not 'Dict' in metData: metData['Dict'] = {} # Extract all forecast data from WeatherFlow JSON object try: # Extract all hourly and daily forecasts hourlyForecasts = (metData['Dict']['forecast']['hourly']) dailyForecasts = (metData['Dict']['forecast']['daily']) # Extract 'valid from' time of all available hourly forecasts and # retrieve forecast for the current hour Hours = list(forecast['time'] for forecast in hourlyForecasts) hoursInd = bisect.bisect(Hours, int(UNIX.time())) hourlyCurrent = hourlyForecasts[hoursInd] hourlyLocalDay = hourlyCurrent['local_day'] # Extract 'Valid' until time of forecast for current hour Valid = Hours[bisect.bisect(Hours, int(UNIX.time()))] Valid = datetime.fromtimestamp(Valid, pytz.utc).astimezone(Tz) # Extract 'day_start_local' time of all available daily forecasts and # retrieve forecast for the current day dailyDayNum = list(forecast['day_num'] for forecast in dailyForecasts) dailyCurrent = dailyForecasts[dailyDayNum.index(hourlyLocalDay)] # Extract weather variables from current hourly forecast Temp = [hourlyCurrent['air_temperature'], 'c'] WindSpd = [hourlyCurrent['wind_avg'], 'mps'] WindGust = [hourlyCurrent['wind_gust'], 'mps'] WindDir = [hourlyCurrent['wind_direction'], 'degrees'] Icon = hourlyCurrent['icon'].replace('cc-', '') # Extract Precipitation Type, Percent, and Amount from current hourly # forecast if 'precip_type' in hourlyCurrent: PrecipType = hourlyCurrent['precip_type'] if PrecipType not in ['rain', 'snow']: PrecipType = 'rain' else: PrecipType = 'rain' if 'precip_probability' in hourlyCurrent: PrecipPercnt = [hourlyCurrent['precip_probability'], '%'] else: PrecipPercnt = [0, '%'] if 'precip' in hourlyCurrent: PrecipAmount = [hourlyCurrent['precip'], 'mm'] else: PrecipAmount = [0, 'mm'] # Extract weather variables from current daily forecast highTemp = [dailyCurrent['air_temp_high'], 'c'] lowTemp = [dailyCurrent['air_temp_low'], 'c'] precipDay = [dailyCurrent['precip_probability'], '%'] # Extract list of expected conditions and find time when expected conditions # will change conditionList = list(forecast['conditions'] for forecast in hourlyForecasts[hoursInd:]) try: Ind = next(i for i, C in enumerate(conditionList) if C != hourlyCurrent['conditions']) except StopIteration: Ind = len(conditionList) - 1 Time = datetime.fromtimestamp(Hours[Ind], pytz.utc).astimezone(Tz) if Time.date() == funcCalled.date(): Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime(Time, TimeFormat) + ' today' elif Time.date() == funcCalled.date() + timedelta(days=1): Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime(Time, TimeFormat) + ' tomorrow' else: Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime( Time, TimeFormat) + ' on ' + Time.strftime('%A') # Calculate derived variables from forecast WindDir = derive.CardinalWindDirection(WindDir, WindSpd) # Convert forecast units as required Temp = observation.Units(Temp, Config['Units']['Temp']) highTemp = observation.Units(highTemp, Config['Units']['Temp']) lowTemp = observation.Units(lowTemp, Config['Units']['Temp']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) WindGust = observation.Units(WindGust, Config['Units']['Wind']) WindDir = observation.Units(WindDir, Config['Units']['Direction']) PrecipAmount = observation.Units(PrecipAmount, Config['Units']['Precip']) # Define and format labels metData['Time'] = funcCalled metData['Valid'] = datetime.strftime(Valid, TimeFormat) metData['Temp'] = observation.Format(Temp, 'forecastTemp') metData['highTemp'] = observation.Format(highTemp, 'forecastTemp') metData['lowTemp'] = observation.Format(lowTemp, 'forecastTemp') metData['WindSpd'] = observation.Format(WindSpd, 'forecastWind') metData['WindGust'] = observation.Format(WindGust, 'forecastWind') metData['WindDir'] = observation.Format(WindDir, 'Direction') metData['PrecipPercnt'] = observation.Format(PrecipPercnt, 'Humidity') metData['PrecipDay'] = observation.Format(precipDay, 'Humidity') metData['PrecipAmount'] = observation.Format(PrecipAmount, 'Precip') metData['PrecipType'] = PrecipType metData['Conditions'] = Conditions metData['Icon'] = Icon metData['Status'] = '' # Check expected conditions icon is recognised if Icon in [ 'clear-day', 'clear-night', 'rainy', 'possibly-rainy-day', 'possibly-rainy-night', 'snow', 'possibly-snow-day', 'possibly-snow-night', 'sleet', 'possibly-sleet-day', 'possibly-sleet-night', 'thunderstorm', 'possibly-thunderstorm-day' 'possibly-thunderstorm-night', 'windy', 'foggy', 'cloudy', 'partly-cloudy-day', 'partly-cloudy-night' ]: metData['Icon'] = Icon else: metData['Icon'] = '--' # Unable to extract forecast data from JSON object. Set set forecast # variables to blank and indicate to user that forecast is unavailable except (IndexError, KeyError, ValueError): metData['Time'] = funcCalled metData['Valid'] = '--' metData['Temp'] = '--' metData['highTemp'] = '--' metData['lowTemp'] = '--' metData['WindSpd'] = '--' metData['WindGust'] = '--' metData['WindDir'] = '--' metData['PrecipPercnt'] = '--' metData['PrecipDay'] = '--' metData['PrecipAmount'] = '--' metData['PrecipType'] = '--' metData['Conditions'] = '' metData['Icon'] = '--' metData['Status'] = 'Forecast currently\nunavailable...' funcError = 1 # Schedule new forecast to be downloaded at the top of the next hour, or in # 5 minutes if error was detected. Note secondsSched refers to number of # seconds since the function was last called. Now = datetime.now(pytz.utc).astimezone(Tz) downloadTime = Tz.localize( datetime.combine(Now.date(), time(Now.hour, 0, 0)) + timedelta(hours=1)) if not funcError: secondsSched = math.ceil((downloadTime - funcCalled).total_seconds()) else: secondsSched = 300 + math.ceil((funcCalled - Now).total_seconds()) Clock.schedule_once(partial(Download, metData, Config), secondsSched) # Return metData dictionary return metData
def ExtractDaily(app): metData = app.MetData dailyForecast = app.DailyForecast Config = app.config """ INPUTS: metData Dictionary holding weather forecast data dailyForecast screen with array of panels Config Station configuration """ # Get current time in station time zone Tz = pytz.timezone(Config['Station']['Timezone']) Now = datetime.now(pytz.utc).astimezone(Tz) weekdays = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ] for i in range(len(dailyForecast.panels)): d = {} try: wfDayDict = metData['Dict']['forecast']['daily'][i] # Extract weather variables from WeatherFlow forecast date = "%02d/%02d" % (wfDayDict['month_num'], wfDayDict['day_num']) tempMax = [wfDayDict['air_temp_high'], 'c'] tempMin = [wfDayDict['air_temp_low'], 'c'] precip = [wfDayDict['precip_probability'], '%'] weather = wfDayDict['icon'] dt = datetime.fromtimestamp(wfDayDict['sunrise'], Tz) weekday = calendar.weekday(dt.year, wfDayDict['month_num'], wfDayDict['day_num']) except IndexError: date = '0/0' tempMax = [ 0, 'c'] tempMin = [ 0, 'c'] precip = [ 0, '%'] weather = 'XX' weekday = 0 except KeyError: date = '0/0' tempMax = [ 0, 'c'] tempMin = [ 0, 'c'] precip = [ 0, '%'] weather = 'XX' weekday = 0 # Convert forecast units as required tempMax = observation.Units(tempMax, Config['Units']['Temp']) tempMin = observation.Units(tempMin, Config['Units']['Temp']) dailyForecast.panels[i].date = date dailyForecast.panels[i].tempMax = ['{:.0f}'.format(tempMax[0]), tempMax[1]] dailyForecast.panels[i].tempMin = ['{:.0f}'.format(tempMin[0]), tempMin[1]] dailyForecast.panels[i].precip = ['{:.0f}'.format(precip[0]), ' %'] dailyForecast.panels[i].weekday = weekdays[weekday] # Define weather icon # Check expected conditions icon is recognised if weather in ['clear-day', 'clear-night', 'rainy', 'possibly-rainy-day', 'possibly-rainy-night', 'snow', 'possibly-snow-day', 'possibly-snow-night', 'sleet', 'possibly-sleet-day', 'possibly-sleet-night', 'thunderstorm', 'possibly-thunderstorm-day' 'possibly-thunderstorm-night', 'windy', 'foggy', 'cloudy', 'partly-cloudy-day','partly-cloudy-night']: dailyForecast.panels[i].weather = weather else: dailyForecast.panels[i].weather = '--'
def ExtractMetOffice(metData, Config): """ Parse the weather forecast from the UK MetOffice INPUTS: metData Dictionary holding weather forecast data Config Station configuration OUTPUT: metData Dictionary holding weather forecast data """ # Get current time in station time zone Tz = pytz.timezone(Config['Station']['Timezone']) Now = datetime.now(pytz.utc).astimezone(Tz) # Extract all forecast data from MetOffice JSON file. If forecast is # unavailable, set forecast variables to blank and indicate to user that # forecast is unavailable try: Issued = str(metData['Dict']['SiteRep']['DV']['dataDate'][11:-4]) metDict = (metData['Dict']['SiteRep']['DV']['Location']['Period']) except KeyError: metData['Time'] = Now metData['Temp'] = '--' metData['WindDir'] = '--' metData['WindSpd'] = '--' metData['Weather'] = 'ForecastUnavailable' metData['Precip'] = '--' metData['Valid'] = '--' # Attempt to download forecast again in 10 minutes and return # metData dictionary Clock.schedule_once(lambda dt: Download(metData, Config), 600) return metData # Extract date of all available forecasts, and retrieve forecast for # today Dates = list(item['value'] for item in metDict) metDict = metDict[Dates.index(Now.strftime('%Y-%m-%dZ'))]['Rep'] # Extract 'valid from' time of all available three-hourly forecasts, and # retrieve forecast for the current three-hour period Times = list(int(item['$']) // 60 for item in metDict) metDict = metDict[bisect.bisect(Times, Now.hour) - 1] # Extract 'valid until' time for the retrieved forecast Valid = Times[bisect.bisect(Times, Now.hour) - 1] + 3 if Valid == 24: Valid = 0 # Extract weather variables from MetOffice forecast Temp = [float(metDict['T']), 'c'] WindSpd = [float(metDict['S']) / 2.2369362920544, 'mps'] WindDir = [metDict['D'], 'cardinal'] Precip = [metDict['Pp'], '%'] Weather = metDict['W'] # Convert forecast units as required Temp = observation.Units(Temp, Config['Units']['Temp']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) # Define and format labels metData['Time'] = Now metData['Issued'] = Issued metData['Valid'] = '{:02.0f}'.format(Valid) + ':00' metData['Temp'] = ['{:.1f}'.format(Temp[0]), Temp[1]] metData['WindDir'] = WindDir[0] metData['WindSpd'] = ['{:.0f}'.format(WindSpd[0]), WindSpd[1]] metData['Weather'] = Weather metData['Precip'] = Precip[0] # Return metData dictionary return metData
def ExtractDarkSky(metData, Config): """ Parse the weather forecast from DarkSky INPUTS: metData Dictionary holding weather forecast data Config Station configuration OUTPUT: metData Dictionary holding weather forecast data """ # Get current time in station time zone Tz = pytz.timezone(Config['Station']['Timezone']) Now = datetime.now(pytz.utc).astimezone(Tz) # Extract all forecast data from DarkSky JSON file. If forecast is # unavailable, set forecast variables to blank and indicate to user that # forecast is unavailable Tz = pytz.timezone(Config['Station']['Timezone']) try: metDict = (metData['Dict']['hourly']['data']) except KeyError: metData['Time'] = Now metData['Temp'] = '--' metData['WindDir'] = '--' metData['WindSpd'] = '--' metData['Weather'] = 'ForecastUnavailable' metData['Precip'] = '--' metData['Valid'] = '--' # Attempt to download forecast again in 10 minutes and return # metData dictionary Clock.schedule_once(lambda dt: Download(metData, Config), 600) return metData # Extract 'valid from' time of all available hourly forecasts, and # retrieve forecast for the current hourly period Times = list(item['time'] for item in metDict) metDict = metDict[bisect.bisect(Times, int(time.time())) - 1] # Extract 'Issued' and 'Valid' times Issued = Times[0] Valid = Times[bisect.bisect(Times, int(time.time()))] Issued = datetime.fromtimestamp(Issued, pytz.utc).astimezone(Tz) Valid = datetime.fromtimestamp(Valid, pytz.utc).astimezone(Tz) # Extract weather variables from DarkSky forecast Temp = [metDict['temperature'], 'c'] WindSpd = [metDict['windSpeed'] / 2.2369362920544, 'mps'] WindDir = [metDict['windBearing'], 'degrees'] Precip = [metDict['precipProbability'] * 100, '%'] Weather = metDict['icon'] # Convert forecast units as required Temp = observation.Units(Temp, Config['Units']['Temp']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) # Define and format labels metData['Time'] = Now metData['Issued'] = datetime.strftime(Issued, '%H:%M') metData['Valid'] = datetime.strftime(Valid, '%H:%M') metData['Temp'] = ['{:.1f}'.format(Temp[0]), Temp[1]] metData['WindDir'] = derive.CardinalWindDirection(WindDir)[2] metData['WindSpd'] = ['{:.0f}'.format(WindSpd[0]), WindSpd[1]] metData['Precip'] = '{:.0f}'.format(Precip[0]) # Define weather icon if Weather == 'clear-day': metData['Weather'] = '1' elif Weather == 'clear-night': metData['Weather'] = '0' elif Weather == 'rain': metData['Weather'] = '12' elif Weather == 'snow': metData['Weather'] = '27' elif Weather == 'sleet': metData['Weather'] = '18' elif Weather == 'wind': metData['Weather'] = 'wind' elif Weather == 'fog': metData['Weather'] = '6' elif Weather == 'cloudy': metData['Weather'] = '7' elif Weather == 'partly-cloudy-day': metData['Weather'] = '3' elif Weather == 'partly-cloudy-night': metData['Weather'] = '2' else: metData['Weather'] = 'ForecastUnavailable' # Return metData dictionary return metData
def format_derived_variables(self, config, device_type): """ Format derived variables from available device observations INPUTS: config Console configuration object device_type Device type """ # Convert derived variable units from obs_out_air and obs_st observations if device_type in ('obs_out_air', 'obs_st', 'obs_all'): outTemp = observation.Units(self.device_obs['outTemp'], config['Units']['Temp']) feelsLike = observation.Units(self.derive_obs['feelsLike'], config['Units']['Temp']) dewPoint = observation.Units(self.derive_obs['dewPoint'], config['Units']['Temp']) outTempDiff = observation.Units(self.derive_obs['outTempDiff'], config['Units']['Temp']) outTempTrend = observation.Units(self.derive_obs['outTempTrend'], config['Units']['Temp']) outTempMax = observation.Units(self.derive_obs['outTempMax'], config['Units']['Temp']) outTempMin = observation.Units(self.derive_obs['outTempMin'], config['Units']['Temp']) humidity = observation.Units(self.device_obs['humidity'], config['Units']['Other']) SLP = observation.Units(self.derive_obs['SLP'], config['Units']['Pressure']) SLPTrend = observation.Units(self.derive_obs['SLPTrend'], config['Units']['Pressure']) SLPMax = observation.Units(self.derive_obs['SLPMax'], config['Units']['Pressure']) SLPMin = observation.Units(self.derive_obs['SLPMin'], config['Units']['Pressure']) strikeDist = observation.Units(self.device_obs['strikeDist'], config['Units']['Distance']) strikeDeltaT = observation.Units(self.derive_obs['strikeDeltaT'], config['Units']['Other']) strikeFreq = observation.Units(self.derive_obs['strikeFreq'], config['Units']['Other']) strike3hr = observation.Units(self.device_obs['strike3hr'], config['Units']['Other']) strikeToday = observation.Units(self.derive_obs['strikeCount']['today'], config['Units']['Other']) strikeMonth = observation.Units(self.derive_obs['strikeCount']['month'], config['Units']['Other']) strikeYear = observation.Units(self.derive_obs['strikeCount']['year'], config['Units']['Other']) # Convert derived variable units from obs_sky and obs_st observations if device_type in ('obs_sky', 'obs_st', 'obs_all'): rainRate = observation.Units(self.derive_obs['rainRate'], config['Units']['Precip']) todayRain = observation.Units(self.derive_obs['rainAccum']['today'], config['Units']['Precip']) yesterdayRain = observation.Units(self.derive_obs['rainAccum']['yesterday'], config['Units']['Precip']) monthRain = observation.Units(self.derive_obs['rainAccum']['month'], config['Units']['Precip']) yearRain = observation.Units(self.derive_obs['rainAccum']['year'], config['Units']['Precip']) radiation = observation.Units(self.device_obs['radiation'], config['Units']['Other']) uvIndex = observation.Units(self.derive_obs['uvIndex'], config['Units']['Other']) peakSun = observation.Units(self.derive_obs['peakSun'], config['Units']['Other']) windSpd = observation.Units(self.derive_obs['windSpd'], config['Units']['Wind']) windDir = observation.Units(self.derive_obs['windDir'], config['Units']['Direction']) windGust = observation.Units(self.device_obs['windGust'], config['Units']['Wind']) windAvg = observation.Units(self.derive_obs['windAvg'], config['Units']['Wind']) windMax = observation.Units(self.derive_obs['gustMax'], config['Units']['Wind']) # Convert derived variable units from obs_in_air observations if device_type in ('obs_in_air', 'obs_all'): inTemp = observation.Units(self.device_obs['inTemp'], config['Units']['Temp']) inTempMax = observation.Units(self.derive_obs['inTempMax'], config['Units']['Temp']) inTempMin = observation.Units(self.derive_obs['inTempMin'], config['Units']['Temp']) # Convert derived variable units from rapid_wind observations if device_type in ('rapid_wind', 'obs_all'): rapidWindSpd = observation.Units(self.device_obs['rapidWindSpd'], config['Units']['Wind']) rapidWindDir = observation.Units(self.derive_obs['rapidWindDir'], 'degrees') # Convert derived variable units from available evt_strike observations if device_type in ('evt_strike', 'obs_all'): strikeDist = observation.Units(self.device_obs['strikeDist'], config['Units']['Distance']) strikeDeltaT = observation.Units(self.derive_obs['strikeDeltaT'], config['Units']['Other']) # Format derived variables from obs_air and obs_st observations if device_type in ('obs_out_air', 'obs_st', 'obs_all'): self.display_obs['outTemp'] = observation.Format(outTemp, 'Temp') self.display_obs['FeelsLike'] = observation.Format(feelsLike, 'Temp') self.display_obs['DewPoint'] = observation.Format(dewPoint, 'Temp') self.display_obs['outTempDiff'] = observation.Format(outTempDiff, 'Temp') self.display_obs['outTempTrend'] = observation.Format(outTempTrend, 'Temp') self.display_obs['outTempMax'] = observation.Format(outTempMax, ['Temp', 'Time'], config) self.display_obs['outTempMin'] = observation.Format(outTempMin, ['Temp', 'Time'], config) self.display_obs['Humidity'] = observation.Format(humidity, 'Humidity') self.display_obs['SLP'] = observation.Format(SLP, 'Pressure') self.display_obs['SLPTrend'] = observation.Format(SLPTrend, 'Pressure') self.display_obs['SLPMax'] = observation.Format(SLPMax, ['Pressure', 'Time'], config) self.display_obs['SLPMin'] = observation.Format(SLPMin, ['Pressure', 'Time'], config) self.display_obs['StrikeDist'] = observation.Format(strikeDist, 'StrikeDistance') self.display_obs['StrikeDeltaT'] = observation.Format(strikeDeltaT, 'TimeDelta') self.display_obs['StrikeFreq'] = observation.Format(strikeFreq, 'StrikeFrequency') self.display_obs['Strikes3hr'] = observation.Format(strike3hr, 'StrikeCount') self.display_obs['StrikesToday'] = observation.Format(strikeToday, 'StrikeCount') self.display_obs['StrikesMonth'] = observation.Format(strikeMonth, 'StrikeCount') self.display_obs['StrikesYear'] = observation.Format(strikeYear, 'StrikeCount') # Format derived variables from obs_sky and obs_st observations if device_type in ('obs_sky', 'obs_st', 'obs_all'): self.display_obs['Radiation'] = observation.Format(radiation, 'Radiation') self.display_obs['UVIndex'] = observation.Format(uvIndex, 'UV') self.display_obs['peakSun'] = observation.Format(peakSun, 'peakSun') self.display_obs['RainRate'] = observation.Format(rainRate, 'Precip') self.display_obs['TodayRain'] = observation.Format(todayRain, 'Precip') self.display_obs['YesterdayRain'] = observation.Format(yesterdayRain, 'Precip') self.display_obs['MonthRain'] = observation.Format(monthRain, 'Precip') self.display_obs['YearRain'] = observation.Format(yearRain, 'Precip') self.display_obs['WindSpd'] = observation.Format(windSpd, 'Wind') self.display_obs['WindGust'] = observation.Format(windGust, 'Wind') self.display_obs['AvgWind'] = observation.Format(windAvg, 'Wind') self.display_obs['MaxGust'] = observation.Format(windMax, 'Wind') self.display_obs['WindDir'] = observation.Format(windDir, 'Direction') # Format derived variables from obs_in_air observations if device_type in ('obs_in_air', 'obs_all'): self.display_obs['inTemp'] = observation.Format(inTemp, 'Temp') self.display_obs['inTempMax'] = observation.Format(inTempMax, ['Temp', 'Time'], config) self.display_obs['inTempMin'] = observation.Format(inTempMin, ['Temp', 'Time'], config) # Format derived variables from rapid_wind observations if device_type in ('rapid_wind', 'obs_all'): self.display_obs['rapidSpd'] = observation.Format(rapidWindSpd, 'Wind') self.display_obs['rapidDir'] = observation.Format(rapidWindDir, 'Direction') # Format derived variables from evt_strike observations if device_type in ('evt_strike', 'obs_all'): self.display_obs['StrikeDist'] = observation.Format(strikeDist, 'StrikeDistance') self.display_obs['StrikeDeltaT'] = observation.Format(strikeDeltaT, 'TimeDelta') # Update display with new variables self.update_display(device_type)
def parse_forecast(self): """ Parse the latest daily and hourly weather forecast from the WeatherFlow BetterForecast API and format for display based on user specified units """ # Extract Forecast dictionary Forecast = self.met_data['Response'] # Get current time in station time zone Tz = pytz.timezone(self.app.config['Station']['Timezone']) Now = datetime.now(pytz.utc).astimezone(Tz) # Set time format based on user configuration if self.app.config['Display']['TimeFormat'] == '12 hr': if self.app.config['System']['Hardware'] == 'Other': TimeFormat = '%#I %p' else: TimeFormat = '%-I %p' else: TimeFormat = '%H:%M' # Extract all forecast data from WeatherFlow JSON object try: # Extract all hourly and daily forecasts hourlyForecasts = (Forecast['forecast']['hourly']) dailyForecasts = (Forecast['forecast']['daily']) # Extract 'valid from' time of all available hourly forecasts and # retrieve forecast for the current hour Hours = list(forecast['time'] for forecast in hourlyForecasts) hoursInd = bisect.bisect(Hours, int(UNIX.time())) hourlyCurrent = hourlyForecasts[hoursInd] hourlyLocalDay = hourlyCurrent['local_day'] # Extract 'Valid' until time of forecast for current hour Valid = Hours[bisect.bisect(Hours, int(UNIX.time()))] Valid = datetime.fromtimestamp(Valid, pytz.utc).astimezone(Tz) # Extract 'day_start_local' time of all available daily forecasts and # retrieve forecast for the current day dailyDayNum = list(forecast['day_num'] for forecast in dailyForecasts) dailyCurrent = dailyForecasts[dailyDayNum.index(hourlyLocalDay)] # Extract weather variables from current hourly forecast Temp = [hourlyCurrent['air_temperature'], 'c'] WindSpd = [hourlyCurrent['wind_avg'], 'mps'] WindGust = [hourlyCurrent['wind_gust'], 'mps'] WindDir = [hourlyCurrent['wind_direction'], 'degrees'] Icon = hourlyCurrent['icon'] # Extract Precipitation Type, Percent, and Amount from current hourly # forecast if 'precip_type' in hourlyCurrent: if hourlyCurrent['precip_type'] in ['rain', 'snow']: PrecipType = hourlyCurrent['precip_type'].title() + 'fall' else: PrecipType = hourlyCurrent['precip_type'].title() else: PrecipType = 'Rainfall' if 'precip_probability' in hourlyCurrent: PrecipPercnt = [hourlyCurrent['precip_probability'], '%'] else: PrecipPercnt = [0, '%'] if 'precip' in hourlyCurrent: PrecipAmount = [hourlyCurrent['precip'], 'mm'] else: PrecipAmount = [0, 'mm'] # Extract weather variables from current daily forecast highTemp = [dailyCurrent['air_temp_high'], 'c'] lowTemp = [dailyCurrent['air_temp_low'], 'c'] precipDay = [dailyCurrent['precip_probability'], '%'] # Extract list of expected conditions and find time when expected conditions # will change conditionList = list(forecast['conditions'] for forecast in hourlyForecasts[hoursInd:]) try: Ind = next(i for i, C in enumerate(conditionList) if C != hourlyCurrent['conditions']) except StopIteration: Ind = len(conditionList) - 1 Time = datetime.fromtimestamp(Hours[Ind], pytz.utc).astimezone(Tz) if Time.date() == Now.date(): Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime(Time, TimeFormat) + ' today' elif Time.date() == Now.date() + timedelta(days=1): Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime(Time, TimeFormat) + ' tomorrow' else: Conditions = hourlyCurrent['conditions'].capitalize( ) + ' until ' + datetime.strftime( Time, TimeFormat) + ' on ' + Time.strftime('%A') # Calculate derived variables from forecast WindDir = derive.cardinalWindDir(WindDir, WindSpd) # Convert forecast units as required Temp = observation.Units(Temp, self.app.config['Units']['Temp']) highTemp = observation.Units(highTemp, self.app.config['Units']['Temp']) lowTemp = observation.Units(lowTemp, self.app.config['Units']['Temp']) WindSpd = observation.Units(WindSpd, self.app.config['Units']['Wind']) WindGust = observation.Units(WindGust, self.app.config['Units']['Wind']) WindDir = observation.Units(WindDir, self.app.config['Units']['Direction']) PrecipAmount = observation.Units( PrecipAmount, self.app.config['Units']['Precip']) # Define and format labels self.met_data['Valid'] = datetime.strftime(Valid, TimeFormat) self.met_data['Temp'] = observation.Format(Temp, 'forecastTemp') self.met_data['highTemp'] = observation.Format( highTemp, 'forecastTemp') self.met_data['lowTemp'] = observation.Format( lowTemp, 'forecastTemp') self.met_data['WindSpd'] = observation.Format( WindSpd, 'forecastWind') self.met_data['WindGust'] = observation.Format( WindGust, 'forecastWind') self.met_data['WindDir'] = observation.Format(WindDir, 'Direction') self.met_data['PrecipPercnt'] = observation.Format( PrecipPercnt, 'Humidity') self.met_data['PrecipDay'] = observation.Format( precipDay, 'Humidity') self.met_data['PrecipAmount'] = observation.Format( PrecipAmount, 'Precip') self.met_data['PrecipType'] = PrecipType self.met_data['Conditions'] = Conditions self.met_data['Icon'] = Icon self.met_data['Status'] = '' # Check expected conditions icon is recognised if Icon in [ 'clear-day', 'clear-night', 'rainy', 'possibly-rainy-day', 'possibly-rainy-night', 'snow', 'possibly-snow-day', 'possibly-snow-night', 'sleet', 'possibly-sleet-day', 'possibly-sleet-night', 'thunderstorm', 'possibly-thunderstorm-day', 'possibly-thunderstorm-night', 'windy', 'foggy', 'cloudy', 'partly-cloudy-day', 'partly-cloudy-night' ]: self.met_data['Icon'] = Icon else: self.met_data['Icon'] = '-' # Update display self.update_display() # Update forecast icon if hasattr(self.app, 'ForecastPanel'): for panel in getattr(self.app, 'ForecastPanel'): panel.setForecastIcon() # Schedule new forecast Clock.schedule_once(self.schedule_forecast) # Unable to extract forecast data from JSON object. Set forecast # variables to blank and indicate to user that forecast is unavailable except (IndexError, KeyError, ValueError): Clock.schedule_once(self.fail_forecast)