def drawMoon(self,now = None): bg = Image.new("RGBA",(self.width,self.height)) dr = ImageDraw.Draw(bg) radius = self.size/5 sunx1 = self.x + self.size/2 - radius sunx2 = self.x + self.size/2 + radius suny1 = self.y + self.size/2 - radius suny2 = self.y + self.size/2 + radius phase = moon.phase(now) # print("Moonphase = ",phase) el = [sunx1,suny1,sunx2,suny2] dr.ellipse(el,fill=(5,5,5),width=0) if phase < 7: # new moon 0 pass elif phase < 14: # first quarter pass elif phase < 21: # full moon pass elif phase < 28: # last quarter 360 = phase/28 * 360 pass angle = phase/28*360 start = 180 - angle/2 end = 180 + angle/2 dr.pieslice(el,start,end,fill=(80,80,80)) return bg
def render_moonphase(_, query): """moonpahse(m) A symbol describing the phase of the moon """ moon_phase = moon.phase(date=datetime.datetime.today()) moon_index = int(int(32.0*moon_phase/28+2)%32/4) return MOON_PHASES[moon_index]
def index(self, latitude=None, longitude=None): if latitude: try: latitude = float(latitude) except ValueError: return 'latitude must be a floating point number' if longitude: try: longitude = float(longitude) except ValueError: return 'longitude must be a floating point number' # Default to Dublin, Ireland city = LocationInfo(latitude=(latitude or 53.427), longitude=(longitude or -6.25)) s = sun(city.observer, datetime.date.today()) ret = 'Sunrise: %s<br/>\n' % (s['sunrise'].strftime('%H:%M')) ret += 'Sunset: %s<br/>\n' % (s['sunset'].strftime('%H:%M')) phasenum = moon.phase(datetime.date.today()) if 14 < phasenum < 15: ret += 'Full moon' return ret
def weather_to_fits(target, verbose=False): #Access BOM FTP service for NSW/ACT Forecast stat_id = 'IDN60920.xml' #CANBERRA AIRPORT SUMMARY ftp = FTP('ftp2.bom.gov.au') ftp.login() ftp.cwd('anon/gen/fwo') with open(stat_id, 'wb') as fp: ftp.retrbinary('RETR ' + stat_id, fp.write) ftp.quit() #Retrieve forecast for Canberra tree = ET.parse(stat_id) root = tree.getroot() stat_weather = root.findall( ".//observations/station/[@stn-name='TIDBINBILLA (PCS)']/period/level/" ) keys = [ 'DELTA_T', 'GUST', 'AIR_TEMP', 'PRESSURE', 'HUMID', 'WIND_DIR', 'WIND_SPD' ] choice = [ 'delta_t', 'gust_kmh', 'air_temperature', 'pres', 'rel-humidity', 'wind_dir_deg', 'wind_spd_kmh' ] with fits.open(target, mode='update') as hdu: target_head = hdu[0].header k = 0 target_head.append(('STATNAME', 'Tidbinbilla (PCS)')) for i in range(len(stat_weather)): if stat_weather[i].attrib['type'] in choice: try: comment = stat_weather[i].attrib['type']\ + ' ' + stat_weather[i].attrib['units'] except KeyError: comment = stat_weather[i].attrib['type'] try: entry = round(float(stat_weather[i].text), 2) except ValueError: entry = stat_weather[i].text target_head.append((keys[k], entry, comment)) k += 1 phase = round(moon.phase(datetime.date.today()), 2) target_head.append(('MOON', phase, 'phase of moon (0-28)')) if verbose: print(target_head)
async def moon_command( self, ctx, ): mon_phase = moon.phase(datetime.datetime.now()) message = "" if mon_phase < 6.99: message = "Aktuell ist Neumond :new_moon:" elif 6.99 < mon_phase < 13.99: message = "Aktuell ist zunehmender Mond :first_quarter_moon:" elif 14 < mon_phase < 20.99: message = "Aktuell ist Vollmond :full_moon:" else: message = "Aktuell ist abnehmender Mond :last_quarter_moon:" await ctx.send(message)
def moon_phase_name(date, lang='en'): ''' Given a date, this function will return the moon's phase name. English by Default lang argument: 'en' = English 'fr' = French 'it' = Italian 'es' = Spanish 'de' = German 'no' = Norwegian 'ru' = Russian 'cn' = Chinese (Simplified) NB: Translations were wupplied by 'Google Translation'. ''' phase_name = { 'en': ("New Moon", "First Quarter", "Full Moon", "Last Quarter"), 'fr': ("Nouvelle Lune", "Lune Croissante", "Pleine Lune", "Lune Décroissante"), 'it': ("Nuova Luna", "Luna Crescente", "Luna Piena", "Luna calante"), 'es': ("Luna nueva", "Luna Creciente", "Luna Llena", "Luna Menguante"), 'de': ("Neumond", "Halbmond", "Vollmond", "Abnehmender Mond"), 'no': ("Nymåne", "Halvmåne", "Fullmåne", "Avtagende måne"), 'ru': ("Новолуние", "полумесяц", "полнолуние", "Убывающая Луна"), 'cn': ("新月", "新月", "满月", "残月") } moon_phase = round(moon.phase(date), 2) if moon_phase in float_range(0, 7, 0.01): return phase_name[lang][0] elif moon_phase in float_range(7, 14, 0.01): return phase_name[lang][1] elif moon_phase in float_range(14, 21, 0.01): return phase_name[lang][2] elif moon_phase in float_range(21, 28, 0.01): return phase_name[lang][3] else: return None
def get_lunar_phase_data(reference_datetime=None, fix_at_noon=True): if reference_datetime is None: reference_datetime = datetime.datetime.now() if fix_at_noon: reference_datetime = reference_datetime.replace(hour=12, minute=0, second=0) moon_phase_day = int(phase(date=reference_datetime)) phase_code = get_lunar_phasecode_from_day(moon_phase_day) phase_name = get_lunar_phasename_from_code(phase_code) lunar_phase_data = { 'datetime': reference_datetime, 'code': phase_code, 'name': phase_name, 'moon_phase_day': moon_phase_day } return lunar_phase_data
def drawmoon(clockface): """drawmoon - Fill in the moon phase on LEDS 60-75 inclusive Args: clockface: The Neopixels to draw onto - note that this uses indices 60-75 """ moonphase = int(moon.phase()) # 0.0-27.99 - 0 = new moon, 14 = full moon gradient = [ 0, 0, 0, 0, 0, 0, 16, 24, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 48, 32, 16, 0, 0, 0, 0, 0 ] # Fade gradient - 28 phases offset = [0, -2, -4, -5, -6, -5, -4, -2, 0, 2, 4, 5, 6, 5, 4, 2] # Phase offset for each of the 16 LEDs in sequence for position in range(len(offset)): led = position + 60 # LED60=top, then anti-clockwise to LED75 if moonphase == 0: val = 0 else: val = gradient[(moonphase + offset[position]) % len(gradient)] clockface[led] = (val // 4, val // 2, val + 4 ) # Make it a little but blue
async def async_update(self) -> None: """Get the time and updates the states.""" today = dt_util.now().date() state = moon.phase(today) if state < 0.5 or state > 27.5: self._attr_native_value = STATE_NEW_MOON elif state < 6.5: self._attr_native_value = STATE_WAXING_CRESCENT elif state < 7.5: self._attr_native_value = STATE_FIRST_QUARTER elif state < 13.5: self._attr_native_value = STATE_WAXING_GIBBOUS elif state < 14.5: self._attr_native_value = STATE_FULL_MOON elif state < 20.5: self._attr_native_value = STATE_WANING_GIBBOUS elif state < 21.5: self._attr_native_value = STATE_LAST_QUARTER else: self._attr_native_value = STATE_WANING_CRESCENT self._attr_icon = MOON_ICONS.get(self._attr_native_value)
end_date = datetime.date(year2, month2, day2) #Manual entry #start_date = datetime.date(2020, 11, 20) #end_date = datetime.date(2021, 1, 30) dates = [ start_date + datetime.timedelta(n) for n in range(int((end_date - start_date).days)) ] #dates #Get moonphases moonphases = [] for i in dates: moonphases.append(moon.phase(i)) #Plot moonphases save_option = input('Do you want to save the plot? input: y or n :') if save_option == 'y': plt.figure(figsize=(15, 8)) ax = plt.gca() plt.plot(dates, moonphases, linewidth=2.5, color='b', label='Moonphases') ax.axhline(y=6.99, color='g', linestyle='--', linewidth=2, alpha=0.9) ax.axhline(y=14, color='r', linestyle='--', linewidth=2, alpha=0.9) ax.axhline(y=20.99, color='r', linestyle='--', linewidth=2, alpha=0.9) ax.axhline(y=27.99, color='b', linestyle='--', alpha=0.9) #fills
from datetime import date, timedelta from astral import moon now = date.today() for i in range(30): day = now + timedelta(days=i) moon_phase = moon.phase(day) print(day.isoformat() + ' Moon Phase: %d' % moon_phase)
def render_moonday(_, query): """moonday(M) An number describing the phase of the moon (days after the New Moon) """ moon_phase = moon.phase(date=datetime.datetime.today()) return str(int(moon_phase))
def test_moon_phase(date_, phase): """Test moon phase calculation""" assert moon.phase(date_) == pytest.approx(phase, abs=0.01)
def draw_astronomical(city_name, geo_data, config): datetime_day_start = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) city = LocationInfo() city.latitude = geo_data["latitude"] city.longitude = geo_data["longitude"] city.timezone = geo_data["timezone"] answer = "" moon_line = "" for time_interval in range(72): current_date = (datetime_day_start + datetime.timedelta(hours=1 * time_interval)).replace( tzinfo=pytz.timezone(geo_data["timezone"])) try: dawn = sun.dawn(city.observer, date=current_date) except ValueError: dawn = current_date try: dusk = sun.dusk(city.observer, date=current_date) except ValueError: dusk = current_date + datetime.timedelta(hours=24) try: sunrise = sun.sunrise(city.observer, date=current_date) except ValueError: sunrise = current_date try: sunset = sun.sunset(city.observer, date=current_date) except ValueError: sunset = current_date + datetime.timedelta(hours=24) char = "." if current_date < dawn: char = " " elif current_date > dusk: char = " " elif dawn <= current_date and current_date <= sunrise: char = u"─" elif sunset <= current_date and current_date <= dusk: char = u"─" elif sunrise <= current_date and current_date <= sunset: char = u"━" answer += char if config.get("view") in ["v2n", "v2d"]: moon_phases = constants.MOON_PHASES_WI moon_phases = [" %s" % x for x in moon_phases] else: moon_phases = constants.MOON_PHASES # moon if time_interval in [0, 23, 47, 69]: # time_interval % 3 == 0: moon_phase = moon.phase(date=datetime_day_start + datetime.timedelta(hours=time_interval)) moon_phase_emoji = moon_phases[int( math.floor(moon_phase * 1.0 / 28.0 * 8 + 0.5)) % len(moon_phases)] # if time_interval in [0, 24, 48, 69]: moon_line += moon_phase_emoji # + " " elif time_interval % 3 == 0: if time_interval not in [24, 28]: #se: moon_line += " " else: moon_line += " " answer = moon_line + "\n" + answer + "\n" answer += "\n" return answer
def run(self): global stats temp_upd = None if plugin_options['use_footer']: temp_upd = showInFooter() # instantiate class to enable data in footer temp_upd.button = "sunrise_and_sunset/status" # button redirect on footer temp_upd.label = _('Sunrise and Sunset') # label on footer msg = _('Waiting to state') temp_upd.val = msg.encode('utf8').decode('utf8') # value on footer try: from astral.geocoder import database except ImportError: log.clear(NAME) log.info(NAME, _('Astral is not installed.')) log.info(NAME, _('Please wait installing astral...')) cmd = "pip3 install astral" run_command(cmd) log.info(NAME, _('Astral is now installed.')) while not self._stop_event.is_set(): try: if plugin_options['use_astro']: log.clear(NAME) city = None found_name = '' found_region = '' found_timezone ='' found_latitude = 0 found_longitude = 0 try: if plugin_options['location'] != 0: # 0 is none location from astral.geocoder import database, lookup find_loc = city_table[plugin_options['location']] city = lookup(find_loc, database()) # return example: LocationInfo(name='Addis Ababa', region='Ethiopia', timezone='Africa/Addis_Ababa', latitude=9.033333333333333, longitude=38.7) found_name = city.name found_region = city.region found_timezone = city.timezone found_latitude = city.latitude found_longitude = city.longitude else: if plugin_options['custom_location'] and plugin_options['custom_region'] and plugin_options['custom_timezone'] and plugin_options['custom_lati_longit']: from astral.geocoder import add_locations, database, lookup db = database() _loc = '{},{},{},{}'.format(plugin_options['custom_location'], plugin_options['custom_region'], plugin_options['custom_timezone'], plugin_options['custom_lati_longit']) add_locations(_loc, db) # "Somewhere,Secret Location,UTC,24°28'N,39°36'E" city = lookup(plugin_options['custom_location'], db) found_name = city.name found_region = city.region found_timezone = city.timezone found_latitude = city.latitude found_longitude = city.longitude else: log.info(NAME, _('You must fill in all required fields (location, region, timezone/name, latitude and longitude!')) city = None s = None if city is not None: log.info(NAME, _('Found city')) log.info(NAME, _('Name') + ': {}'.format(found_name)) log.info(NAME, _('Region') + ': {}'.format(found_region)) log.info(NAME, _('Timezone') + ': {}'.format(found_timezone)) log.info(NAME, _('Latitude') + ': {}'.format(round(found_latitude, 2))) log.info(NAME, _('Longitude') + ': {}'.format(round(found_longitude, 2))) import datetime from astral.sun import sun today = datetime.date.today() _day = int(today.strftime("%d")) _month = int(today.strftime("%m")) _year = int(today.strftime("%Y")) s = sun(city.observer, date=datetime.date(_year, _month, _day), tzinfo=found_timezone) log.info(NAME, '_______________ ' + '{}'.format(today) + ' _______________') log.info(NAME, _('Dawn') + ': {}'.format(s["dawn"].strftime("%H:%M:%S"))) log.info(NAME, _('Sunrise') + ': {}'.format(s["sunrise"].strftime("%H:%M:%S"))) log.info(NAME, _('Noon') + ': {}'.format(s["noon"].strftime("%H:%M:%S"))) log.info(NAME, _('Sunset') + ': {}'.format(s["sunset"].strftime("%H:%M:%S"))) log.info(NAME, _('Dusk') + ': {}'.format(s["dusk"].strftime("%H:%M:%S"))) msg = _('Sunrise') + ': {}, '.format(s["sunrise"].strftime("%H:%M:%S")) + _('Sunset') + ': {}'.format(s["sunset"].strftime("%H:%M:%S")) from astral import moon m = moon.phase(datetime.date(_year, _month, _day)) log.info(NAME, _('Moon phase') + ': {}'.format(round(m, 2))) msg += ', ' + _('Moon phase') + ': ' if m < 7: log.info(NAME, '* ' + _('New moon')) msg += _('New moon') elif m >= 7 and m < 14: log.info(NAME, '* ' + _('First quarter')) msg += _('First quarter') elif m >= 14 and m < 21: log.info(NAME, '* ' + _('Full moon')) msg += _('Full moon') elif m >= 21 and m < 28: log.info(NAME, '* ' + _('Last quarter')) msg += _('Last quarter') else: log.info(NAME, '* ' + _('Unkown phase')) msg += _('Unkown phase') except Exception: self.started.set() log.error(NAME, _('Astro plug-in') + ':\n' + traceback.format_exc()) self._sleep(2) else: msg =_(u'Plugin is not enabled') if plugin_options['use_footer']: if temp_upd is not None: temp_upd.val = msg.encode('utf8').decode('utf8') # value on footer else: log.error(NAME, _('Error: restart this plugin! Show in homepage footer have enabled.')) self._sleep(3600) except Exception: self.started.set() log.error(NAME, _('Astro plug-in') + ':\n' + traceback.format_exc()) self._sleep(60)
def get(latitude, longitude, useragent_string, flask_app=None): ''' Use the weather data from the NOAA Weather API. Latitude longitude: geolocation obtained from the request URL useragent_string: required by NOAA to identify the caller. See https://www.weather.gov/documentation/services-web-api flask_app : an object containing a Flask application's details. Used to allow us to write into the application log. Returns a DarkSky JSON structure that can be the output of this web service ''' # Get the NOAA grid coordinates, timezone and URL links for this # location from their "points" service, based on the lat/long noaa_headers = { 'User-Agent': useragent_string, 'Accept': 'application/geo+json' } url = 'https://api.weather.gov/points/{},{}'.format(latitude, longitude) noaa_points_obj = functions.getURL(url, noaa_headers, flask_app) if noaa_points_obj is False: flask_app.logger.critical('NOAA request for location information on this latitude and longitude failed: {},{}'.format(latitude, longitude)) return False # Create the output JSON structure using the location information # that came back from the service output = { 'latitude': latitude, 'longitude': longitude, 'timezone': functions.getKeyValue(noaa_points_obj, ['properties', 'timeZone']), 'currently': {}, # NOAA does not provide minutely data 'minutely': {}, 'hourly': {}, 'daily': {}, 'alerts': [], 'flags': { 'sources': [], 'units': 'us' } } # Define a location for the astral functions that determine sunrise # and sunset for the "daily" section of the output astral_location = LocationInfo('dummy_name', 'dummy_region', functions.getKeyValue(noaa_points_obj, ['properties', 'timeZone']), latitude, longitude) # Set the system timezone so that all times are local relative to # this lat/long os.environ['TZ'] = output['timezone'] # Calculate the timezone's offset from UTC in hours and add that to the # output tz_now = datetime.datetime.now(pytz.timezone(output['timezone'])) output['offset'] = tz_now.utcoffset().total_seconds() / 3600 # Using the stations URL that we got from the NOAA points lookup, find # the nearest station and get its dstance away. Add this to the output. url = functions.getKeyValue(noaa_points_obj, ['properties', 'observationStations']); noaa_stations_obj = functions.getURL(url, noaa_headers, flask_app) if noaa_stations_obj is False: flask.abort(501) for feature in functions.getKeyValue(noaa_stations_obj,['features']): miles = great_circle((latitude, longitude), (functions.getKeyValue(feature, ['geometry', 'coordinates'])[1], functions.getKeyValue(feature, ['geometry', 'coordinates'])[0])).miles if 'nearest-station' not in output['flags'] or miles < output['flags']['nearest-station']: noaa_station_url = functions.getKeyValue(feature, ['id']) output['flags']['nearest-station'] = round(miles, 2) # Do all of the rest of the NOAA API calls simultaneously, in # seperate threads, to save time with concurrent.futures.ThreadPoolExecutor() as executor: # Get the current conditions url = '{}/observations/latest'.format(noaa_station_url) get_current = executor.submit(functions.getURL, url, noaa_headers, flask_app) url = functions.getKeyValue(noaa_points_obj, ['properties', 'forecastHourly']); get_hourly = executor.submit(functions.getURL, url, noaa_headers, flask_app) # Get the grid forecast data url = functions.getKeyValue(noaa_points_obj, ['properties', 'forecastGridData']); get_griddata = executor.submit(functions.getURL, url, noaa_headers, flask_app) # Get the daily forecast data url = functions.getKeyValue(noaa_points_obj, ['properties', 'forecast']); get_daily = executor.submit(functions.getURL, url, noaa_headers, flask_app) # Get the alerts url = 'https://api.weather.gov/alerts?point={},{}'.format(latitude, longitude) get_alerts = executor.submit(functions.getURL, url, noaa_headers, flask_app) # Get the list of all the counties in this location's state # and their id codes. url = 'https://api.weather.gov/zones?type=county&area={}'.format(functions.getKeyValue(noaa_points_obj, ['properties', 'relativeLocation', 'properties', 'state'])) get_counties = executor.submit(functions.getURL, url, noaa_headers, flask_app) # Get the list of forecast zones in this location's state # and their id codes url = 'https://api.weather.gov/zones?type=forecast&area={}'.format(functions.getKeyValue(noaa_points_obj, ['properties', 'relativeLocation', 'properties', 'state'])) get_zones = executor.submit(functions.getURL, url, noaa_headers, flask_app) try: noaa_current_obj = get_current.result() except: print('Exception occurred during noaa_current_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_current_obj API call: {}'.format(sys.exc_info()[0])) noaa_current_obj = None # If this request failed, return an empty dictionary if not noaa_current_obj: return False try: noaa_hourly_obj = get_hourly.result() except: print('Exception occurred during noaa_hourly_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_hourly_obj API call: {}'.format(sys.exc_info()[0])) noaa_hourly_obj = None # If this request failed, return an empty dictionary if not noaa_hourly_obj: return False try: noaa_griddata_obj = get_griddata.result() except: print('Exception occurred during noaa_griddata_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_griddata_obj API call: {}'.format(sys.exc_info()[0])) noaa_griddata_obj = None # If this request failed, return an empty dictionary if not noaa_griddata_obj: return False try: noaa_daily_obj = get_daily.result() except: print('Exception occurred during noaa_daily_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_daily_obj API call: {}'.format(sys.exc_info()[0])) noaa_daily_obj = None # If this request failed, return an empty dictionary if not noaa_daily_obj: return False try: noaa_alerts_obj = get_alerts.result() except: print('Exception occurred during noaa_alerts_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_alerts_obj API call: {}'.format(sys.exc_info()[0])) noaa_alerts_obj = None try: noaa_counties_obj = get_counties.result() except: print('Exception occurred during noaa_counties_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_counties_obj API call: {}'.format(sys.exc_info()[0])) noaa_counties_obj = None try: noaa_zones_obj = get_zones.result() except: print('Exception occurred during noaa_counties_obj API call: {}'.format(sys.exc_info())) if flask_app: flask_app.logger.error('Exception occurred during noaa_counties_obj API call: {}'.format(sys.exc_info()[0])) noaa_zones_obj = None # Put the county codes and their associated names into a dictionary. noaa_county_list = {}; if noaa_counties_obj: for feature in functions.getKeyValue(noaa_counties_obj, ['features']): noaa_county_list[functions.getKeyValue(feature, ['properties', 'id'])] = functions.getKeyValue(feature, ['properties', 'name']); # Append the list of forecast zones to the county list dictionary. # Sometimes they use these in alerts instead of county codes. if noaa_zones_obj: for feature in functions.getKeyValue(noaa_zones_obj, ['features']): noaa_county_list[functions.getKeyValue(feature, ['properties', 'id'])] = functions.getKeyValue(feature, ['properties', 'name']); #-------------------------- C u r r e n t l y --------------------------# # Populate the output dictionary with the current observations from # that station we just found props = functions.getKeyValue(noaa_current_obj, ['properties']) if props: output['currently'] = { 'time': functions.getKeyValue(props, ['timestamp'], lambda x: int(functions.parseInterval(x)['start'])), 'summary': functions.getKeyValue(props, ['textDescription'], lambda x: x.capitalize()), 'icon': functions.getKeyValue(props, ['icon'], lambda x: _mapIcons(x, flask_app)), #'nearestStormDistance': None, 'precipIntensity': functions.getKeyValue(props, ['precipitationLastHour', 'value'], lambda x: round(x / 39.37014, 2)), #'precipIntensityError': None, #'precipProbability': None, #'precipType': None, 'temperature': functions.getKeyValue(props, ['temperature', 'value'], lambda x: round((x * 9 / 5) + 32, 2)), 'apparentTemperature': functions.getKeyValue(props, ['windchill', 'value'], lambda x: round((x * 9 / 5) + 32, 2)) if functions.getKeyValue(props, ['windchill', 'value']) else functions.getKeyValue(props, ['heatIndex', 'value'], lambda x: round((x * 9 / 5) + 32, 2)), 'dewPoint': functions.getKeyValue(props, ['dewpoint', 'value'], lambda x: round((x * 9 / 5) + 32, 2)), 'humidity': round(100 - (5 * (props['temperature']['value'] - props['dewpoint']['value'])), 2) if functions.getKeyValue(props, ['temperature', 'value']) and functions.getKeyValue(props, ['dewpoint', 'value']) else None, 'pressure': functions.getKeyValue(props, ['barometricpressure', 'value'], lambda x: round(x / 100, 2)), 'windSpeed': functions.getKeyValue(props, ['windSpeed', 'value'], lambda x: round(x * 0.621371, 2)), 'windGust': functions.getKeyValue(props, ['windGust', 'value'], lambda x: round(x * 0.621371, 2)), 'windBearing': functions.getKeyValue(props, ['windDirection', 'value']), #'cloudCover': None, #'uvIndex': None, 'visibility': functions.getKeyValue(props, ['visibility', 'value'], lambda x: round(x / 1609.34, 2)), #'ozone': None } #----------------------------- H o u r l y -----------------------------# # Populate the output dictionary with the hourly data hours = functions.getKeyValue(noaa_hourly_obj, ['properties', 'periods']); if hours: hourly_data = [] for hour in hours: # Break out of the loop once we've got 48 hours worth if len(hourly_data) >= 48: break # Include this period if its end time is greater than # the current time if functions.parseInterval(hour['endTime'])['start'] > datetime.datetime.now().timestamp(): hourly_data.append({ 'time': functions.parseInterval(hour['startTime'])['start'], 'summary': functions.getKeyValue(hour, ['shortForecast'], lambda x: x.capitalize()), 'icon': functions.getKeyValue(hour, ['icon'], lambda x: _mapIcons(x, flask_app)), #'pressure': None, #'uvIndex': None, #'ozone': None }) # Use NOAA's grid forecast to complete the hourly data array quantities = functions.getKeyValue(noaa_griddata_obj, ['properties', 'quantitativePrecipitation', 'values']); if quantities: for quantity in quantities: interval = functions.parseInterval(quantity['validTime']) hours = (interval['end'] - interval['start']) / 3600 perhour = round(quantity['value'] / 25.4 / hours, 2) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['precipIntensity'] = perhour if hourly_data[i]['time'] > interval['end']: break snowamounts = functions.getKeyValue(noaa_griddata_obj, ['properties', 'snowfallAmount', 'values']); if snowamounts: for snow in snowamounts: interval = functions.parseInterval(snow['validTime']) hours = (interval['end'] - interval['start']) / 3600 perhour = round(snow['value'] / 25.4 / hours, 2) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: if 'precipIntensity' in hourly_data[i]: if perhour > hourly_data[i]['precipIntensity']: hourly_data[i]['precipIntensity'] = perhour else: hourly_data[i]['precipIntensity'] = perhour if hourly_data[i]['time'] > interval['end']: break probabilities = functions.getKeyValue(noaa_griddata_obj, ['properties', 'probabilityOfPrecipitation', 'values']); if probabilities: for probability in probabilities: interval = functions.parseInterval(probability['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['precipProbability'] = round(probability['value'] / 100, 2) if hourly_data[i]['time'] > interval['end']: break temperatures = functions.getKeyValue(noaa_griddata_obj, ['properties', 'temperature', 'values']); if temperatures: for temp in temperatures: interval = functions.parseInterval(temp['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['temperature'] = round((temp['value'] * 9 / 5) + 32, 2) if hourly_data[i]['time'] > interval['end']: break apparents = functions.getKeyValue(noaa_griddata_obj, ['properties', 'apparentTemperature', 'values']); if apparents: for apparent in apparents: interval = functions.parseInterval(apparent['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['apparentTemperature'] = round((apparent['value'] * 9 / 5) + 32, 2) if hourly_data[i]['time'] > interval['end']: break dewpoints = functions.getKeyValue(noaa_griddata_obj, ['properties', 'dewpoint', 'values']); if dewpoints: for dewpoint in dewpoints: interval = functions.parseInterval(dewpoint['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['dewpoint'] = round((dewpoint['value'] * 9 / 5) + 32, 2) if hourly_data[i]['time'] > interval['end']: break humidities = functions.getKeyValue(noaa_griddata_obj, ['properties', 'relativeHumidity', 'values']); if humidities: for humidity in humidities: interval = functions.parseInterval(humidity['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['humdity'] = round(humidity['value'] / 100, 2) if hourly_data[i]['time'] > interval['end']: break windspeeds = functions.getKeyValue(noaa_griddata_obj, ['properties', 'windSpeed', 'values']); if windspeeds: for speed in windspeeds: interval = functions.parseInterval(speed['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['windSpeed'] = round(speed['value'] * 0.621371, 2) if hourly_data[i]['time'] > interval['end']: break windgusts = functions.getKeyValue(noaa_griddata_obj, ['properties', 'windGust', 'values']); if windgusts: for gust in windgusts: interval = functions.parseInterval(gust['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['windGust'] = round(gust['value'] * 0.621371, 2) if hourly_data[i]['time'] > interval['end']: break winddirections = functions.getKeyValue(noaa_griddata_obj, ['properties', 'windDirection', 'values']); if winddirections: for direction in winddirections: interval = functions.parseInterval(direction['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['windBearing'] = round(direction['value']) if hourly_data[i]['time'] > interval['end']: break skycovers = functions.getKeyValue(noaa_griddata_obj, ['properties', 'skyCover', 'values']); if skycovers: for skycover in skycovers: interval = functions.parseInterval(skycover['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['cloudCover'] = round(skycover['value'] / 100, 2) if hourly_data[i]['time'] > interval['end']: break visibilities = functions.getKeyValue(noaa_griddata_obj, ['properties', 'visibility', 'values']); if visibilities: for visibility in visibilities: interval = functions.parseInterval(visibility['validTime']) for i in range(len(hourly_data)): if hourly_data[i]['time'] >= interval['start'] and hourly_data[i]['time'] < interval['end']: hourly_data[i]['visibility'] = round(visibility['value'] / 1609.34, 2) if hourly_data[i]['time'] > interval['end']: break # Add the hourly data array to the output dictionary if len(hourly_data) > 0: output['hourly'] = { 'summary': hourly_data[0]['summary'], 'icon': hourly_data[0]['icon'], 'data': hourly_data } #------------------------------ D a i l y ------------------------------# # Populate the output dictionary with the hourly data. NOAA # provides seperate daytime and nighttime forecasts for each day, # we focus on the daytime forecasts daily_data = [] periods = functions.getKeyValue(noaa_daily_obj, ['properties', 'periods']) # Calculate UNIX Epoch timestamp for the first day in the daily # forecast array d = datetime.datetime.now() d = datetime.datetime.combine(datetime.date(d.year, d.month, d.day), datetime.time()) d = pytz.timezone(functions.getKeyValue(noaa_points_obj, ['properties', 'timeZone'])).localize(d) timestamp = int(d.timestamp()) # Build an array to hold the daily forecasts for i in range(len(periods)): if _dailyEpochTime(periods[i]['startTime']) == timestamp: the_sun = sun(astral_location.observer, date=datetime.datetime.utcfromtimestamp(timestamp)) daily_data.append({ 'time': timestamp, 'summary': functions.getKeyValue(periods, [i, 'shortForecast']), 'icon': functions.getKeyValue(periods, [i, 'icon'], lambda x: _mapIcons(x, flask_app)), 'sunriseTime': round(the_sun['sunrise'].timestamp()), 'sunsetTime': round(the_sun['sunset'].timestamp()), 'moonPhase': round(moon.phase(datetime.datetime.utcfromtimestamp(timestamp)) / 27.99, 2), #'precipIntensity', None, #'precipIntensityMax', #'precipIntensityMaxTiome', 'precipProbability': None, #'precipType': None, 'temperatureHigh': None, 'temperatureHighTime': None, 'temperatureLow': None, 'temperatureLowTime': None, 'apparentTemperatureHigh': None, 'apparentTemperatureHighTime': None, 'apparentTemperatureLow': None, 'apparentTemperatureLowTime': None, 'dewPoint': None, 'humidity': None, #'pressure': None, 'windSpeed': None, 'windGust': None, 'windGustTime': None, 'windBearing': None, 'cloudCover': None, #'uvIndex': None, #'uvIndexTime', None, 'visibility': None, #'ozone': None, 'temperatureMin': None, 'temperatureMinTime': None, 'temperatureMax': None, 'temperatureMaxTime': None, 'apparentTemperatureMin': None, 'apparentTemperatureMinTime': None, 'apparentTemperatureMax': None, 'apparentTemperatureMaxTime': None }) timestamp = timestamp + (3600 * 24) # Fill in remaining daily data using the NOAA gridpoint forecast query props = functions.getKeyValue(noaa_griddata_obj, ['properties']) for i in range(len(daily_data)): start = daily_data[i]['time'] end = start + (3600 * 24) # Scan through each day's temperature data and get the highs and lows for value in props['temperature']['values']: timestamp = functions.getKeyValue(value, ['validTime'], lambda x: functions.parseInterval(x)['start']) if timestamp >= end: break; temp = functions.getKeyValue(value, ['value']); if temp != None: temp = round((temp * 9 / 5) + 32, 2) if timestamp >= start and timestamp < end: if not daily_data[i]['temperatureHigh'] or temp > daily_data[i]['temperatureHigh']: daily_data[i]['temperatureHigh'] = temp daily_data[i]['temperatureHighTime'] = timestamp daily_data[i]['temperatureMax'] = temp daily_data[i]['temperatureMaxTime'] = timestamp if not daily_data[i]['temperatureLow'] or temp < daily_data[i]['temperatureLow']: daily_data[i]['temperatureLow'] = temp daily_data[i]['temperatureLowTime'] = timestamp daily_data[i]['temperatureMin'] = temp daily_data[i]['temperatureMinTime'] = timestamp for value in props['apparentTemperature']['values']: timestamp = functions.getKeyValue(value, ['validTime'], lambda x: functions.parseInterval(x)['start']) if timestamp >= end: break; temp = functions.getKeyValue(value, ['value']); if temp != None: temp = round((temp * 9 / 5) + 32, 2) if timestamp >= start and timestamp < end: if not daily_data[i]['apparentTemperatureHigh'] or temp > daily_data[i]['apparentTemperatureHigh']: daily_data[i]['apparentTemperatureHigh'] = temp daily_data[i]['apparentTemperatureHighTime'] = timestamp daily_data[i]['apparentTemperatureMax'] = temp daily_data[i]['apparentTemperatureMaxTime'] = timestamp if not daily_data[i]['apparentTemperatureLow'] or temp < daily_data[i]['apparentTemperatureLow']: daily_data[i]['apparentTemperatureLow'] = temp daily_data[i]['apparentTemperatureLowTime'] = timestamp daily_data[i]['apparentTemperatureMin'] = temp daily_data[i]['apparentTemperatureMinTime'] = timestamp # Calculate the average dewpoint temperature for the day total = 0; total_hours = 0; for value in props['dewpoint']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; dp = functions.getKeyValue(value, ['value']) if dp != None: total = total + (dp * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['dewPoint'] = round((total / total_hours * 9 / 5) + 32, 2); # Calculate the average humidity for the day total = 0; total_hours = 0; for value in props['relativeHumidity']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; hum = functions.getKeyValue(value, ['value']) if hum != None: total = total + (hum * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['humidity'] = round(total / total_hours / 100, 2); # Calculate the average cloudCover percentage for the day total = 0; total_hours = 0; for value in props['skyCover']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; clo = functions.getKeyValue(value, ['value']) if clo != None: total = total + (clo * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['cloudCover'] = round(total / total_hours / 100, 2); # Calculate the average windDirection for the day total = 0; total_hours = 0; for value in props['windDirection']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; dir = functions.getKeyValue(value, ['value']) if dir != None: total = total + (dir * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['windBearing'] = round(total / total_hours); # Calculate the average windSpeed for the day total = 0; total_hours = 0; for value in props['windSpeed']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; spe = functions.getKeyValue(value, ['value']) if spe != None: total = total + (spe * hours); total_hours = total_hours + hours; total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['windSpeed'] = round(total / total_hours * 0.621371, 2); # Calculate the average precipitation probability for the day total = 0; total_hours = 0; for value in props['probabilityOfPrecipitation']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; pro = functions.getKeyValue(value, ['value']) if pro != None: total = total + (pro * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['precipProbability'] = round(total / total_hours / 100, 2); # Calculate the average visibility for the day (remember that # NOAA only provides this data for the first 24 hours (2 days.) total = 0; total_hours = 0; for value in props['visibility']['values']: interval = functions.parseInterval(functions.getKeyValue(value, ['validTime'])) if interval['end'] > start and interval['start'] <= end: if interval['start'] < start: interval['start'] = start if interval['end'] > end: interval['end'] = end hours = (interval['end'] - interval['start']) / 3600; vis = functions.getKeyValue(value, ['value']) if vis != None: total = total + (vis * hours); total_hours = total_hours + hours; if total_hours > 0: daily_data[i]['visibility'] = round(total / total_hours / 1609.34, 2); # Get the maximum windGust for the day and its associated timestamp for prop in props['windGust']['values']: timestamp = functions.getKeyValue(prop, ['validTime'], lambda x: functions.parseInterval(x)['start']); if timestamp >= end: break value = functions.getKeyValue(prop, ['value']) if value != None: value = round(value * 0.621371, 2) if timestamp >= start and timestamp < end: if not daily_data[i]['windGust'] or value > daily_data[i]['windGust']: daily_data[i]['windGust'] = value daily_data[i]['windGustTime'] = timestamp # Add the daily data array to the output dictionary if len(daily_data) > 0: output['daily'] = { 'summary': daily_data[0]['summary'], 'icon': daily_data[0]['icon'], 'data': daily_data } #----------------------------- A l e r t s -----------------------------# # Popluate the output dictionary with any alerts that pertain to # this location. if noaa_alerts_obj: alerts = functions.getKeyValue(noaa_alerts_obj, ['features']) if alerts: alert_data = [] for alert in alerts: props = functions.getKeyValue(alert, ['properties']) # Don't include expired alerts if functions.getKeyValue(props, ['expires'], lambda x: functions.parseInterval(x)['start']) > datetime.datetime.now().timestamp(): # Use the noaa_county_list dictionary we built at the # beginning to convert the county IDs listed in the # alert to county names. regions = [] for county_id in functions.getKeyValue(props, ['geocode', 'UGC']): regions.append(noaa_county_list[county_id]) # Populate the alert data array with the data from NOAA alert_data.append({ 'title': functions.getKeyValue(props, ['event']), 'regions': regions, 'severity': functions.getKeyValue(props, ['severity']), 'time': functions.getKeyValue(props, ['onset'], lambda x: functions.parseInterval(x)['start']), 'expires': functions.getKeyValue(props, ['expires'], lambda x: functions.parseInterval(x)['start']), 'description': functions.getKeyValue(props, ['description'], lambda x: re.sub(r'\s+', ' ', x)), 'url': functions.getKeyValue(props,['@id']) }) # Add the alerts data array to the output dictionary output['alerts'] = alert_data # Flag the output as including data from NOAA output['flags']['sources'] = 'noaa', return output
def get(latitude, longitude, apikey, input_dictionary=None, flask_app=None): ''' Use the weather data from the Climacell API. This data will overwrite and extend what we got from NOAA. Latitude longitude: geolocation obtained from the request URL apikey: the user's Climacell API key from a free account (or paid) input_dictionary: a DarkSky JSON structure created by another routine in this script like the getNOAAWeatherInfo rountine, for example. This is optional. If a JSON structure isn't passed through, then one will be created from scratch. flask_app : an object containing a Flask application's details. Used to allow us to write into the application log. Returns a DarkSky JSON structure that can be the output of this web service ''' cc_headers = {'Accept': 'application/json', 'apikey': apikey} # Do all of the Climacell API calls simultaneously, in seperate # threads, to save time with concurrent.futures.ThreadPoolExecutor() as executor: url = 'https://api.climacell.co/v3/weather/realtime?lat={}&lon={}&unit_system=us&fields=precipitation,precipitation%3Ain%2Fhr,precipitation_type,temp,feels_like,dewpoint,wind_speed,wind_gust,baro_pressure%3AhPa,visibility,humidity,wind_direction,cloud_cover,weather_code,o3'.format( latitude, longitude) get_current = executor.submit(functions.getURL, url, cc_headers, flask_app) minutely_starttime = ( datetime.datetime.utcnow() + datetime.timedelta(minutes=1)).strftime('%Y-%m-%dT%H:%M:%S.000Z') minutely_endtime = ( datetime.datetime.utcnow() + datetime.timedelta(minutes=61)).strftime('%Y-%m-%dT%H:%M:%S.000Z') url = 'https://api.climacell.co/v3/weather/nowcast?lat={}&lon={}&unit_system=us&fields=precipitation%3Ain%2Fhr,precipitation_type&start_time={}&end_time={}×tep=1'.format( latitude, longitude, minutely_starttime, minutely_endtime) get_minutely = executor.submit(functions.getURL, url, cc_headers, flask_app) url = 'https://api.climacell.co/v3/weather/forecast/hourly?lat={}&lon={}&unit_system=us&fields=precipitation,precipitation%3Ain%2Fhr,precipitation_type,precipitation_probability,temp,feels_like,dewpoint,wind_speed,wind_gust,baro_pressure%3AhPa,visibility,humidity,wind_direction,cloud_cover,weather_code,o3&start_time=now'.format( latitude, longitude) get_hourly = executor.submit(functions.getURL, url, cc_headers, flask_app) url = 'https://api.climacell.co/v3/weather/forecast/daily?lat={}&lon={}&start_time=now&unit_system=us&fields=temp,feels_like,wind_speed,wind_direction,baro_pressure%3AhPa,precipitation,precipitation%3Ain%2Fhr,precipitation_probability,visibility,humidity,sunrise,sunset,weather_code'.format( latitude, longitude) get_daily = executor.submit(functions.getURL, url, cc_headers, flask_app) try: cc_current_obj = get_current.result() except: print( 'Exception occurred during cc_current_obj API call: {}'.format( sys.exc_info())) if flask_app: flask_app.logger.error( 'Exception occurred during cc_current_obj API call: {}'. format(sys.exc_info()[0])) cc_current_obj = None # If this request failed, return an empty dictionary if not cc_current_obj: if input_dictionary: return input_dictionary else: return False try: cc_minutely_obj = get_minutely.result() except: print('Exception occurred during cc_minutely_obj API call: {}'. format(sys.exc_info())) if flask_app: flask_app.logger.error( 'Exception occurred during cc_minutely_obj API call: {}'. format(sys.exc_info()[0])) cc_minutely_obj = None # If this request failed, return an empty dictionary if not cc_minutely_obj: if input_dictionary: return input_dictionary else: return False try: cc_hourly_obj = get_hourly.result() except: print( 'Exception occurred during cc_hourly_obj API call: {}'.format( sys.exc_info())) if flask_app: flask_app.logger.error( 'Exception occurred during cc_hourly_obj API call: {}'. format(sys.exc_info()[0])) cc_hourly_obj = None # If this request failed, return an empty dictionary if not cc_hourly_obj: if input_dictionary: return input_dictionary else: return False try: cc_daily_obj = get_daily.result() except: print('Exception occurred during cc_daily_obj API call: {}'.format( sys.exc_info())) if flask_app: flask_app.logger.error( 'Exception occurred during cc_daily_obj API call: {}'. format(sys.exc_info()[0])) cc_daily_obj = None # If this request failed, return an empty dictionary if not cc_daily_obj: if input_dictionary: return input_dictionary else: return False # Use the input dictionary or build a new one if we didn't get one if input_dictionary: output = input_dictionary else: # Create the output JSON structure tf = TimezoneFinder() output = { 'latitude': latitude, 'longitude': longitude, # Climacell does not provide the timezone 'timezone': tf.timezone_at(lat=latitude, lng=longitude), 'currently': {}, 'minutely': {}, 'hourly': {}, 'daily': {}, # Climacell does not provide alerts to free accounts #'alerts': [], 'flags': { 'sources': [], 'units': 'us' } } # Set the system timezone so that all times are local relative to # this lat/long os.environ['TZ'] = output['timezone'] # Calculate the timezone's offset from UTC in hours and add that # to the output tz_now = datetime.datetime.now(pytz.timezone(output['timezone'])) output['offset'] = tz_now.utcoffset().total_seconds() / 3600 #-------------------------- C u r r e n t l y --------------------------# # Populate the output dictionary with the current observations if cc_current_obj: output['currently'] = { 'time': functions.getKeyValue(cc_current_obj, ['observation_time', 'value'], lambda x: int(_epochTime(x))), 'summary': functions.getKeyValue(cc_current_obj, ['weather_code', 'value'], lambda x: _mapClimacellWeatherCode(x)), 'icon': functions.getKeyValue(cc_current_obj, ['weather_code', 'value'], lambda x: _mapIcons(x, flask_app)), #'nearestStormDistance': None, 'precipIntensity': functions.getKeyValue(cc_current_obj, ['precipitation', 'value']), #'precipIntensityError': None, #'precipProbability': None, 'precipType': functions.getKeyValue(cc_current_obj, ['precipitation_type', 'value'], lambda x: None if x == 'none' else x), 'temperature': functions.getKeyValue(cc_current_obj, ['temp', 'value']), 'apparentTemperature': functions.getKeyValue(cc_current_obj, ['feels_like', 'value']), 'dewPoint': functions.getKeyValue(cc_current_obj, ['dewpoint', 'value']), 'humidity': functions.getKeyValue(cc_current_obj, ['humidity', 'value'], lambda x: round(x / 100)), 'pressure': functions.getKeyValue(cc_current_obj, ['baro_pressure', 'value']), 'windSpeed': functions.getKeyValue(cc_current_obj, ['wind_speed', 'value']), 'windGust': functions.getKeyValue(cc_current_obj, ['wind_gust', 'value']), 'windBearing': functions.getKeyValue(cc_current_obj, ['wind_direction', 'value']), 'cloudCover': functions.getKeyValue(cc_current_obj, ['cloud_cover', 'value'], lambda x: round(x / 100)), #'uvIndex': None, 'visibility': functions.getKeyValue(cc_current_obj, ['visibility', 'value']), 'ozone': functions.getKeyValue(cc_current_obj, ['o3', 'value']), } #--------------------------- M i n u t e l y ---------------------------# # Add the minutely data from Climacell to the output dictionary if cc_minutely_obj: minutely_data = functions.getKeyValue(output, ['minutely', 'data']) # If we already have minutely data, we won't change it. We only # use our data when there isn't anything already in place. if not minutely_data or len(minutely_data) == 0: minutely_data = [] for minute in cc_minutely_obj: minutely_data.append({ 'time': _epochTime(minute['observation_time']['value']), 'precipIntensity': functions.getKeyValue(minute, ['precipitation', 'value']), # Climacell doesn't provide these next two elements # in their minute-by-minute forecast 'precipIntesityError': None, 'precipProbability': None, 'precipType': functions.getKeyValue(minute, ['precipitation_type', 'value'], lambda x: None if x == 'none' else x) }) output['minutely'] = { 'summary': None, 'icon': None, 'data': minutely_data } #----------------------------- H o u r l y -----------------------------# # Add the hourly data from Climacell to the output dictionary if cc_hourly_obj: hourly_data = functions.getKeyValue(output, ['hourly', 'data']) # If there's no hourly data then create an hourly data array # with just a timestamp for each day if not hourly_data or len(hourly_data) == 0: hourly_data = [] timestamp = int( datetime.datetime.combine( datetime.date.today(), datetime.time(datetime.datetime.now().hour)).timestamp()) for timestamp in range(timestamp + (timestamp + (3600 * 48)), 3600): hourly_data.append({'time': int(timestamp)}) # Add the Climacell data for each day for i in range(len(hourly_data)): hour = hourly_data[i] cc_hour = cc_hourly_obj[i] if hour['time'] == _epochTime( cc_hour['observation_time']['value']): hourly_data[i] = { 'time': hour['time'], 'summary': functions.getKeyValue( cc_hour, ['weather_code', 'value'], lambda x: _mapClimacellWeatherCode(x)), 'icon': functions.getKeyValue(cc_hour, ['weather_code', 'value'], lambda x: _mapIcons(x, flask_app)), #'nearestStormDistance': None, 'precipIntensity': functions.getKeyValue(cc_hour, ['precipitation', 'value']), #'precipIntensityError': None, 'precipProbability': functions.getKeyValue( cc_hour, ['precipitation_probability', 'value'], lambda x: round(x / 100, 2)), 'precipType': functions.getKeyValue(cc_hour, ['precipitation_type', 'value'], lambda x: None if x == 'none' else x), 'temperature': functions.getKeyValue(cc_hour, ['temp', 'value']), 'apparentTemperature': functions.getKeyValue(cc_hour, ['feels_like', 'value']), 'dewPoint': functions.getKeyValue(cc_hour, ['dewpoint', 'value']), 'humidity': functions.getKeyValue(cc_hour, ['humidity', 'value'], lambda x: round(x / 100, 2)), 'pressure': functions.getKeyValue(cc_hour, ['baro_pressure', 'value']), 'windSpeed': functions.getKeyValue(cc_hour, ['wind_speed', 'value']), 'windGust': functions.getKeyValue(cc_hour, ['wind_gust', 'value']), 'windBearing': functions.getKeyValue(cc_hour, ['wind_direction', 'value']), 'cloudCover': functions.getKeyValue(cc_hour, ['cloud_cover', 'value'], lambda x: round(x / 100, 2)), #'uvIndex': None, 'visibility': functions.getKeyValue(cc_hour, ['visibility', 'value']), 'ozone': functions.getKeyValue(cc_hour, ['o3', 'value']) } # Use data from the first hour as the summary values for this # section and the minutely section output['hourly']['summary'] = hourly_data[0]['summary'] output['hourly']['icon'] = hourly_data[0]['icon'] output['minutely']['summary'] = hourly_data[0]['summary'] output['minutely']['icon'] = hourly_data[0]['icon'] # Put the hourly data into the output dictionary output['hourly']['data'] = hourly_data #------------------------------ D a i l y ------------------------------# # Add the daily data from Climacell to the output directory if cc_daily_obj: daily_data = functions.getKeyValue(output, ['daily', 'data']) # If there's no daily data then create a daily data array # with just a timestamp for each day if not daily_data or len(daily_data) == 0: daily_data = [] d = datetime.datetime.utcnow() timestamp = int( datetime.datetime(d.year, d.month, d.day, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp()) for timestamp in range(timestamp, timestamp + (86400 * 8), 86400): daily_data.append({'time': int(timestamp)}) # If there are less than 8 days in the array, add the missing # days with just a timestamp value if len(daily_data) < 8: timestamp = daily_data[len(daily_data) - 1]['time'] for i in range(8 - len(daily_data)): timestamp = timestamp + 86400 daily_data.append({'time': timestamp}) # Align the dates in the two arrays ctr = 0 for i in range(len(cc_daily_obj)): if _dailyEpochTime(cc_daily_obj[ctr]['observation_time'] ['value']) < daily_data[0]['time']: ctr = ctr + 1 # Update the data for each day for i in range(len(daily_data)): day = daily_data[i] cc_day = cc_daily_obj[ctr] daily_data[i] = { 'time': day['time'], 'summary': functions.getKeyValue(cc_day, ['weather_code', 'value'], lambda x: _mapClimacellWeatherCode(x)), 'icon': functions.getKeyValue(cc_day, ['weather_code', 'value'], lambda x: _mapIcons(x, flask_app)), #'nearestStormDistance': None, 'sunriseTime': functions.getKeyValue( cc_day, ['sunrise', 'value'], lambda x: round(isodate.parse_datetime(x).timestamp())), 'sunsetTime': functions.getKeyValue( cc_day, ['sunset', 'value'], lambda x: round(isodate.parse_datetime(x).timestamp())), # Climacell provides text moon phase names and we want # the fractional part of the lunation number instead # so we'll calculate this outselves using Astral 'moonPhase': round( moon.phase(datetime.datetime.utcfromtimestamp(day['time'])) / 27.99, 2), 'precipIntensity': functions.getKeyValue(cc_day, ['precipitation', 0, 'max', 'value']), 'precipIntensityMax': functions.getKeyValue(cc_day, ['precipitation', 0, 'max', 'value']), 'precipIntensityMaxTime': functions.getKeyValue( cc_day, ['precipitation', 'observation_time'], lambda x: int(isodate.parse_datetime(x).timestamp())), 'precipProbability': functions.getKeyValue(cc_day, ['precipitation_probability', 'value']), #'precipType': None, 'temperatureHigh': None, 'temperatureHighTime': None, 'temperatureLow': None, 'temperatureLowTime': None, 'apparentTemperatureHigh': None, 'apparentTemperatureHighTime': None, 'apparentTemperatureLow': None, 'apparentTemperatureLowTime': None, 'dewPoint': None, 'humidity': None, 'pressure': None, # Climacell does not provide, using data from input array, if any 'windSpeed': functions.getKeyValue(day, ['windSpeed']), 'windGust': None, 'windGustTime': None, # Climacell does not provide, using data from input array, if any 'windBearing': functions.getKeyValue(day, ['windBearing']), # Climacell does not provide, using data from input array, if any 'cloudCover': functions.getKeyValue(day, ['cloudCover']), #'uvIndex': None, #'uvIndexTime', None, # Climacell does not provide, using data from input array, if any 'visibility': functions.getKeyValue(day, ['visibility']), #'ozone': None, 'temperatureMin': None, 'temperatureMinTime': None, 'temperatureMax': None, 'temperatureMaxTime': None, 'apparentTemperatureMin': None, 'apparentTemperatureMinTime': None, 'apparentTemperatureMax': None, 'apparentTemperatureMaxTime': None } # Get min and max temperatures from the Climacell data array and plug them in for t in cc_day['temp']: if 'min' in t: daily_data[i]['temperatureLow'] = t['min']['value'] daily_data[i]['temperatureLowTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) daily_data[i]['temperatureMin'] = t['min']['value'] daily_data[i]['temperatureMinTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) if 'max' in t: daily_data[i]['temperatureHigh'] = t['max']['value'] daily_data[i]['temperatureHighTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) daily_data[i]['temperatureMax'] = t['max']['value'] daily_data[i]['temperatureMaxTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) for t in cc_day['feels_like']: if 'min' in t: daily_data[i]['apparentTemperatureLow'] = t['min']['value'] daily_data[i]['apparentTemperatureLowTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) daily_data[i]['apparentTemperatureMin'] = t['min']['value'] daily_data[i]['apparentTemperatureMinTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) if 'max' in t: daily_data[i]['apparentTemperatureHigh'] = t['max'][ 'value'] daily_data[i]['apparentTemperatureHighTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) daily_data[i]['apparentTemperatureMax'] = t['max']['value'] daily_data[i]['apparentTemperatureMaxTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) # Get the average of the min and max humidity and use # that for the daily value for t in cc_day['humidity']: total = 0 if 'min' in t: total = total + t['min']['value'] if 'max' in t: total = total + t['max']['value'] daily_data[i]['humidity'] = round( total / len(cc_day['humidity']), 2) # Get the average of the min and max barometric pressure # and use that for the daily value for t in cc_day['baro_pressure']: total = 0 if 'min' in t: total = total + t['min']['value'] if 'max' in t: total = total + t['max']['value'] daily_data[i]['pressure'] = round( total / len(cc_day['baro_pressure']), 2) # Get the max wind speed and plug that in as windGust for t in cc_day['wind_speed']: if 'max' in t: daily_data[i]['windGust'] = t['max']['value'] daily_data[i]['windGustTime'] = int( isodate.parse_datetime( t['observation_time']).timestamp()) ctr = ctr + 1 # Use data from the first day as the summary values for this # section output['daily']['summary'] = daily_data[0]['summary'] output['daily']['icon'] = daily_data[0]['icon'] # Put the daily data into the output dictionary output['daily']['data'] = daily_data #----------------------------- A l e r t s -----------------------------# ''' Climacell's alerts are a proprietary messaging system, not weather alerts. If the input data structure included NOAA weather alerts, those will be passed through untouched. ''' # Add a source flag for this source sources = [] if functions.getKeyValue(input_dictionary, ['flags', 'sources']): for source in functions.getKeyValue(input_dictionary, ['flags', 'sources']): sources.append(source) sources.append('climacell') output['flags']['sources'] = sources return output
async def async_update(self): """Get the time and updates the states.""" today = dt_util.as_local(dt_util.utcnow()).date() self._state = moon.phase(today)