// //---------------------------------------------------------------------------- // // ElveDarkSky // // Copyright (C) 2019 by Robert Paauwe // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //---------------------------------------------------------------------------- // // A driver that queries the DarkSky servers for weather data. Pulls // current condition data at a user defined interval. Forecast data is queried // twice a day for the location of the defined weather station. // // The data will be in english or metric units as defined by the Units // configuration option. // // The properties exposed are: // // Credit Provides credit to Weather Underground for data feed // DewPoint Current dewpoint // HeatIndex Current heat index // Humidity Current humidity // Location Location of weather station // Precipitation Precipitation for today // PrecipitationChance Chance of Precipitation for today // BarometricPressure Barometric pressure // BarometricTrend Barometric pressure trend up/down/steady // BarometricUnits Barometric pressure units // SolarRadiation Solar Radiation if reported by station // Temperature Current temperature // UVIndex Current UV Index // WindDirectionDegrees Wind direction in degrees (numeric) // WindDirectionText Wind direction (N, NW, S, etc.) // WindGustSpeed Highest wind speed reported // WindSpeed Current wind speed // Windchill Current windchill // ApparentTemperature Calculated apparent temperature (feels like) // LastUpdate Last time/date that the data was reported // Dates Array of dates for forecast data // WeekDayTexts Array of day names for forecast data // Highs Array of expected high temperatures // Lows Array of expected low temperatures // Conditions Array of expected conditions (text) // Sunrises Array of sunrise times // Sunsets Array of sunset times // DayIconIDs Array of icon id numbers for forecast conditions // NightIconIDs Array of icon id numbers for forecast conditions // DayDescriptions Array of daily forecast text // NightDescriptions Array of nightly forecast text // NightConditions Array of nightly short condition text // DayPrecipitations Array of daily chance of precipitation // ConditionIconUrls Array of URL's pointing to the condition icon // --- URL's to the various icon sets supported by Weather Underground // SmileyConditionIconUrls // GenericConditionIconUrls // OldSchoolConditionIconUrls // CartoonConditionIconUrls // MobileConditionIconUrls // SimpleConditionIconUrls // ContemporaryConditionIconUrls // HelenConditionIconUrls // // The methods exposed are: // none // // using System; using System.Collections; using System.Collections.Generic; using System.Text; using CodecoreTechnologies.Elve.DriverFramework; using CodecoreTechnologies.Elve.DriverFramework.Communication; using CodecoreTechnologies.Elve.DriverFramework.DeviceSettingEditors; using CodecoreTechnologies.Elve.DriverFramework.DriverInterfaces; using CodecoreTechnologies.Elve.DriverFramework.Scripting; using System.Threading; using System.Timers; using System.IO; using System.Xml; using System.Net; using System.Net.Sockets; using System.Linq; using System.Web; using System.Web.Script.Serialization; namespace ElveDarkSky { [Driver( "DarkSky Weather", "This driver pulls weather data from the DarkSkey weather service. " + "http://www.darksky.net/", "Robert Paauwe", "Weather", "", "weather", DriverCommunicationPort.Network, DriverMultipleInstances.MultiplePerDriverService, 1, // Major version 7, // Minor version DriverReleaseStages.Production, "DarkSky", "http://www.darksky.net/", null )] public class DarkSkyDriver : Driver, IWeatherDriver { private System.Timers.Timer m_poll_timer; private string m_station_location = ""; private Rest rest; private int m_device_poll; private string m_api_key = ""; private bool m_metric; private WeatherData m_weather; private Forecast[] m_forecasts = new Forecast[8]; // Currently only 6 days of info. // The constructor initializes some variables. public DarkSkyDriver() { } public static readonly DateTime epoch = new DateTime(1970,1,1,0,0,0, DateTimeKind.Utc); public static DateTime FromUnixTime(long ut) { // Is there a way to get local time here? return epoch.ToLocalTime().AddSeconds(ut); } // // Driver user configuration settings // // Weather station identification string // Units (english or metric) // Polling interval // [DriverSettingAttribute("API Key", "The DarkSky API key assigned to your account.", null, true)] public string APIKeySetting { set { m_api_key = value; } } [DriverSettingAttribute("Location", "The location to query for weather forecast data. This can be a zip code or city, state, or " + "latitude and longitude.", null, false)] public string StationLocationSetting { set { m_station_location = value; } } [DriverSettingAttribute("Units", "Use English or Metric units.", new string[] { "English", "Metric" }, "English", true)] public string UnitsSetting { set { if (value == "Metric") { m_metric = true; } else { m_metric = false; } } } [DriverSettingAttribute("Polling Interval", "The interval used to query current condition information from " + "the DarkSky server, in seconds.", 60, 3600, "300", true)] public int PollIntervalSetting { set { m_device_poll = value; } } // // ---------------------------------------------------------------- // Driver start and stop methods // ---------------------------------------------------------------- // public override bool StartDriver( Dictionary configFileData) { DirectoryInfo localpath = new DirectoryInfo(LocalDeviceDataDirectoryPath); Logger.Info("DarkSky Driver version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + " starting."); Logger.Debug("Creating REST interface"); rest = new Rest("https://api.darksky.net", Logger); // Allocate data classes for (int i = 0; i < m_forecasts.Length; i++) { m_forecasts[i] = new Forecast(m_metric); } m_weather = new WeatherData(m_metric); // Attempt connection to the Weather Underground server and pull current values try { PollCurrentConditions(this, new EventArgs()); } catch (Exception ex) { Logger.Error("Failed to get current conditions on start"); Logger.Error(ex.Message); return false; } // Start a timer to pull condition data at polling frequency. m_poll_timer = new System.Timers.Timer(); m_poll_timer.Elapsed += new ElapsedEventHandler(PollCurrentConditions); m_poll_timer.Interval = m_device_poll * 1000; m_poll_timer.Enabled = true; return true; } public override void StopDriver() { Logger.Info("DarkSky Driver finished."); m_poll_timer.Enabled = false; m_poll_timer.Dispose(); } // // ---------------------------------------------------------------- // Driver public properties // ---------------------------------------------------------------- // // // The following properties represent the current weather conditions. For // the most part, they get updated at the polling interval. See // ReadWeatherData() for the URL that is used and XML parsing of the returned // data. // [ScriptObjectPropertyAttribute("Location", "Gets the location for the weather.", "the {NAME} weather location", null)] [SupportsDriverPropertyBinding] public ScriptString Location { get { // return text description of the weather location, such as city, state, etc. return new ScriptString(m_weather.Location); } } [ScriptObjectPropertyAttribute("Temperature", "Gets the current temperature.", "the {NAME} temperature", null)] [SupportsDriverPropertyBinding] public ScriptNumber Temperature { get { return new ScriptNumber(m_weather.Temperature); } } [ScriptObjectPropertyAttribute("Apparent Temperature", "Gets the apparent temperature.", "the {NAME} apparent temperature", null)] [SupportsDriverPropertyBinding] public ScriptNumber ApparentTemperature { get { return new ScriptNumber(m_weather.ApparentTemp); } } [ScriptObjectPropertyAttribute("Current Condition", "Gets the current condition.", "the {NAME} current condition text", null)] [SupportsDriverPropertyBinding] public ScriptString CurrentCondition { get { return new ScriptString(m_forecasts[0].Condition); } } [ScriptObjectPropertyAttribute("Wind Speed", "Gets the windspeed in miles/hour.", "the {NAME} windspeed in miles/hour", null)] [SupportsDriverPropertyBinding] public ScriptNumber WindSpeed { get { return new ScriptNumber(m_weather.WindSpeed); } } [ScriptObjectPropertyAttribute("Wind Gust Speed", "Gets the gust windspeed in miles/hour.", "the {NAME} gust windspeed in miles/hour", null)] [SupportsDriverPropertyBinding] public ScriptNumber WindGustSpeed { get { return new ScriptNumber(m_weather.WindGust); } } [ScriptObjectPropertyAttribute("Wind Direction Degrees", "Gets the wind direction in degrees.", "the {NAME} wind direction in degrees", null)] [SupportsDriverPropertyBinding] public ScriptNumber WindDirectionDegrees { get { return new ScriptNumber(m_weather.WindDegrees); } } [ScriptObjectPropertyAttribute("Wind Direction Text", "Gets the wind direction as text. Ex: NW or E.", "the {NAME} wind direction abbreviation", null)] [SupportsDriverPropertyBinding] public ScriptString WindDirectionText { get { return new ScriptString(m_weather.WindDirection); } } [ScriptObjectPropertyAttribute("Humidity", "Gets the percent relative humidity.", "the {NAME} percent relative humidity", null)] [SupportsDriverPropertyBinding] public ScriptNumber Humidity { get { return new ScriptNumber(m_weather.Humidity); } } [ScriptObjectPropertyAttribute("Dew Point", "Gets the dew point temperature.", "the {NAME} dewpoint temperature", null)] [SupportsDriverPropertyBinding] public ScriptNumber DewPoint { get { return new ScriptNumber(m_weather.Dewpoint); } } [ScriptObjectPropertyAttribute("Barometric Pressure", "Gets the barometric pressure.", "the {NAME} barometric pressure", null)] [SupportsDriverPropertyBinding] public ScriptNumber BarometricPressure { get { return new ScriptNumber(m_weather.Pressure); } } [ScriptObjectPropertyAttribute("Barometric Pressure Trend", "Gets the barometric pressure trend.", "the {NAME} barometric pressure trend", null)] [SupportsDriverPropertyBinding] public ScriptString BarometricTrend { get { return new ScriptString(m_weather.PressureTrend); } } [ScriptObjectPropertyAttribute("Barometric Pressure Units", "Gets the barometric pressure units of measure.", "the {NAME} barometric pressure units of measure", null)] [SupportsDriverPropertyBinding] public ScriptString BarometricUnits { get { return new ScriptString(m_weather.PressureUnits); } } [ScriptObjectPropertyAttribute("Heat Index", "Gets the heat index temperature.", "the {NAME} heat index", null)] [SupportsDriverPropertyBinding] public ScriptNumber HeatIndex { get { return new ScriptNumber(m_weather.HeatIndex); } } [ScriptObjectPropertyAttribute("Windchill", "Gets the windchill temperature.", "the {NAME} windchill", null)] [SupportsDriverPropertyBinding] public ScriptNumber Windchill { get { return new ScriptNumber(m_weather.WindChill); } } [ScriptObjectPropertyAttribute("Precipitation", "Gets the daily amount of precipitation.", "the {NAME} precipitation", null)] [SupportsDriverPropertyBinding] public ScriptNumber Precipitation { get { return new ScriptNumber(m_weather.Precipitation); } } [ScriptObjectPropertyAttribute("Solar Radiation", "Gets the solar radiation.", "the {NAME} solar radiation", null)] [SupportsDriverPropertyBinding] public ScriptNumber SolarRadiation { get { return new ScriptNumber(m_weather.SolarRadiation); } } [ScriptObjectPropertyAttribute("UV Index", "Gets the UV Index.", "the {NAME} UV index", null)] [SupportsDriverPropertyBinding] public ScriptNumber UVIndex { get { return new ScriptNumber(m_weather.UV); } } [ScriptObjectPropertyAttribute("Credit", "Provide credit for using Weather Underground data feeds.", "the {NAME} data feed credit", null)] [SupportsDriverPropertyBinding] public ScriptString Credit { get { return new ScriptString("Data source provided by " + m_weather.Credit); } } [ScriptObjectPropertyAttribute("Credit URL", "Provide a link to the Weather Underground site.", "the {NAME} URL", null)] [SupportsDriverPropertyBinding] public ScriptString CreditURL { get { return new ScriptString(m_weather.CreditURL); } } [ScriptObjectPropertyAttribute("Last Update", "Date and Time that last update was recieved.", "the {NAME} data feed observation time", null)] [SupportsDriverPropertyBinding] public ScriptString LastUpdate { get { return new ScriptString(m_weather.LastUpdate); } } [ScriptObjectPropertyAttribute("Condition Icon Urls", "Get the condition icon URL for each day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray ConditionIconUrls { get { // return a 0 based array of confition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURL), 0); } } [ScriptObjectPropertyAttribute("Precipitation Type", "Current preciptation type.", "the precipitation type is {NAME}", null)] [SupportsDriverPropertyBinding] public ScriptString PrecipitationType { get { Logger.Debug("Getting current condition preciptitation type."); try { return new ScriptString(m_weather.PrecipitationType); } catch { return new ScriptString("none"); } } } [ScriptObjectPropertyAttribute("Probability of Precipitation", "Gets the probablility of precipitation.", "the {NAME} probability of precipitation", null)] [SupportsDriverPropertyBinding] public ScriptNumber ProbabilityOfPrecipitation { get { return new ScriptNumber(m_weather.PrecipitationProbablilty); } } [ScriptObjectPropertyAttribute("Precipitation Chance", "Gets the percent precipitation chance.", "the percent precipitation chance", null)] [SupportsDriverPropertyBinding] public ScriptNumber PrecipitationChance { get { return new ScriptNumber(m_weather.PrecipitationProbablilty); } } [ScriptObjectPropertyAttribute("Cloud Coverage", "Gets the cloud coverage percent.", "the {NAME} percent of cloud coverage", null)] [SupportsDriverPropertyBinding] public ScriptNumber CloudCoverage { get { return new ScriptNumber(m_weather.CloudCover); } } [ScriptObjectPropertyAttribute("Visibility", "Gets the visibility.", "the {NAME} visibility", null)] [SupportsDriverPropertyBinding] public ScriptNumber Visibility { get { return new ScriptNumber(m_weather.Visibility); } } [ScriptObjectPropertyAttribute("Ozone", "Gets the ozone.", "the {NAME} ozone", null)] [SupportsDriverPropertyBinding] public ScriptNumber Ozone { get { return new ScriptNumber(m_weather.Ozone); } } // // The following properties represent Forecasted weather data. This data will get // updated hourly. See ReadWeatherForecast() for the URL and XML parsing. // // Weather Underground provides 6 days worth of forecast data. Day 0 is the // current day. // [ScriptObjectPropertyAttribute("Highs", "Gets an array of daily maximum temperatures.", "the {NAME} maximum temperature for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Highs { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.High), 0); } } [ScriptObjectPropertyAttribute("Lows", "Gets an array of daily minimum temperatures.", "the {NAME} minimum temperature for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Lows { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Low), 0); } } [ScriptObjectPropertyAttribute("Conditions", "Gets an array of daily weather conditions.", "the {NAME} weather condition for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Conditions { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Condition), 0); } } // Chance of precipitation by day [ScriptObjectPropertyAttribute("Daily Precipitation Chance", "Gets an array of percent precipitation chance.", "the percent precipitation chance for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray DayPrecipitations { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Pop), 0); } } [ScriptObjectPropertyAttribute("Dates", "Gets the dates for all the forecast days.", "the {NAME} date for forecast day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Dates { get { // return an array of ScriptDateTime elements for the dates, 0 based. ScriptArrayMarshalByValue array = new ScriptArrayMarshalByValue(); for (int i = 0; i < m_forecasts.Length; i++) { array.Add(new ScriptDateTime(m_forecasts[i].Time)); } return array; } } [ScriptObjectPropertyAttribute("DatesText", "Gets the dates for all the forecast days as a text string.", "the {NAME} date for forecast day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray DatesText { get { // return an array of ScriptString elements for the dates, 0 based. return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.DateText), 0); } } [ScriptObjectPropertyAttribute("Week Days", "Gets the day names for all the forecast days.", "the {NAME} day of week for forecast day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray WeekDayTexts { get { // return an array of ScriptString elements for the week day name, 0 based. return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.WeekDay), 0); } } [ScriptObjectPropertyAttribute("Day Descriptions", "Gets an array of daily forecasts by day.", "the {NAME} forecast text day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray DayDescriptions { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.DayForecastText), 0); } } [ScriptObjectPropertyAttribute("Precipitation Types", "Gets an array of daily preciptiation types by day.", "the {NAME} forecast preciptiation type {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray PrecipitationTypes { get { Logger.Debug("Getting forecast array of preciptitation types."); return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.PrecipitationType), 0); } } [ScriptObjectPropertyAttribute("ApparentHighs", "Gets an array of daily maximum apparent temperatures.", "the {NAME} maximum temperature for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray ApparentHighs { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.ApparentTemperatureHigh), 0); } } [ScriptObjectPropertyAttribute("ApparentLows", "Gets an array of daily minimum apparent temperatures.", "the {NAME} minimum temperature for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray ApparentLows { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.ApparentTemperatureLow), 0); } } [ScriptObjectPropertyAttribute("Moon Phases", "Gets an array of daily moon phase.", "the {NAME} moon phase for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray MoonPhases { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.MoonPhase), 0); } } [ScriptObjectPropertyAttribute("DewPoints", "Gets an array of daily dewpoint.", "the {NAME} dewoint for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray DewPoints { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.DewPoint), 0); } } [ScriptObjectPropertyAttribute("Humidities", "Gets an array of daily humidity.", "the {NAME} humidity for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Humidities { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Humidity), 0); } } [ScriptObjectPropertyAttribute("Pressures", "Gets an array of daily pressure.", "the {NAME} pressure for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Pressures { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Pressure), 0); } } [ScriptObjectPropertyAttribute("Wind Speeds", "Gets an array of daily windspeed.", "the {NAME} windspeed for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray WindSpeeds { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.WindSpeed), 0); } } [ScriptObjectPropertyAttribute("Wind Gusts", "Gets an array of daily windgust.", "the {NAME} windgust for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray WindGusts { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.WindGust), 0); } } [ScriptObjectPropertyAttribute("Wind Directions", "Gets an array of daily wind direction.", "the {NAME} wind direction for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray WindDirections { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.WindDir), 0); } } [ScriptObjectPropertyAttribute("Cloud Coverages", "Gets an array of daily cloud cover.", "the {NAME} cloud cover for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray CloudCoverages { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.CloudCover), 0); } } [ScriptObjectPropertyAttribute("Visibilities", "Gets an array of daily visibility.", "the {NAME} visibility for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Visibilities { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Visibility), 0); } } [ScriptObjectPropertyAttribute("UV Indexes", "Gets an array of daily UV index.", "the {NAME} UV index for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray UVIndexes { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.UV), 0); } } [ScriptObjectPropertyAttribute("Ozones", "Gets an array of daily ozone.", "the {NAME} ozone for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Ozones { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Ozone), 0); } } [ScriptObjectPropertyAttribute("Day Icon IDs", "Gets the day icon ID for all days.", 0, 47, "the {NAME} condition icon id for day {INDEX|0}", "Set {NAME} condition icon id {INDEX|0} to")] [SupportsDriverPropertyBinding("Day Icon Number", "Occurs when the Icon number changes")] public IScriptArray DayIconIDs { get { // return a 0 based array of day icon ids return new ScriptArrayMarshalByValue(m_forecasts.Select(f => IconID(f.Icon)), 0); } } [ScriptObjectPropertyAttribute("Night Icon IDs", "Gets the night icon id for all days.", 0, 47, "the {NAME} night time condition icon id for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray NightIconIDs { get { // return a 0 based array of night icon ids return new ScriptArrayMarshalByValue(m_forecasts.Select(f => IconID(f.Icon)), 0); } } #if false [ScriptObjectPropertyAttribute("Condition Icon Urls", "Gets the condition icon URLs for all days.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray ConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURL), 0); } } [ScriptObjectPropertyAttribute("Smiley Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray SmileyConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLSmiley), 0); } } [ScriptObjectPropertyAttribute("Generic Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray GenericConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLGeneric), 0); } } [ScriptObjectPropertyAttribute("Old School Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray OldSchoolConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLOldSchool), 0); } } [ScriptObjectPropertyAttribute("Cartoon Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray CartoonConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLCartoon), 0); } } [ScriptObjectPropertyAttribute("Mobile Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray MobileConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLMobile), 0); } } [ScriptObjectPropertyAttribute("Simple Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray SimpleConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLSimple), 0); } } [ScriptObjectPropertyAttribute("Contemporary Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray ContemporaryConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLContemporary), 0); } } [ScriptObjectPropertyAttribute("Helen Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray HelenConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLHelen), 0); } } [ScriptObjectPropertyAttribute("Incredible Condition Icon Urls", "Gets an array of condition icons urls by day.", "the {NAME} condition icon url for day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray IncredibleConditionIconUrls { get { // return a 0 based array of condition icon urls return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.IconURLIncredible), 0); } } [ScriptObjectPropertyAttribute("Night Descriptions", "Gets an array of nightly forecasts by day.", "the {NAME} night forecast text day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray NightDescriptions { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.NightForecastText), 0); } } [ScriptObjectPropertyAttribute("Night Conditions", "Gets an array of nightly conditions by day.", "the {NAME} night condition text day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray NightConditions { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.NightCondition), 0); } } #endif [ScriptObjectPropertyAttribute("Last Forecast Update", "Date and Time that last update was recieved.", "the {NAME} forecast data feed observation time", null)] [SupportsDriverPropertyBinding] public ScriptString LastForecastUpdate { get { return new ScriptString(m_forecasts[0].LastUpdate); } } [ScriptObjectPropertyAttribute("Sunrise", "Gets an array of sunrise times by day.", "the {NAME} sunrise day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Sunrise { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Sunrise), 0); } } [ScriptObjectPropertyAttribute("Sunset", "Gets an array of sunset times by day.", "the {NAME} sunset day {INDEX|0}", null)] [SupportsDriverPropertyBinding] public IScriptArray Sunset { get { return new ScriptArrayMarshalByValue(m_forecasts.Select(f => f.Sunset), 0); } } // // New properties for Elve 2.0 // [ScriptObjectPropertyAttribute("Paged List Forecast", "Provides the list of daily forcasts to be shown in a Touch Screen Interface's Paged List control. The item value has the following properties: Index. Index is the index for the weather driver property arrays.")] [SupportsDriverPropertyBinding] public ScriptPagedListCollection PagedListForecast { get { var dates = this.Dates; var highs = this.Highs; var lows = this.Lows; var conditions = this.Conditions; var list = new List(); // Add dates. for (int i = dates.PrimitiveLowestIndex; i <= dates.PrimitiveHighestIndex; i++) { string title = ((DateTime)(ScriptDateTime)dates[i]).ToString("dddd"); // full day of week name. ex: "Monday" string subtitle = (int)(ScriptNumber)highs[i] + "\u00B0 / " + (int)(ScriptNumber)lows[i] + "\u00B0 " + (string)(ScriptString)conditions[i]; ScriptExpandoObject value = new ScriptExpandoObject(); value.SetProperty("Index", new ScriptNumber(i)); list.Add(new ScriptPagedListItem(title, subtitle, value)); } return new ScriptPagedListCollection(list); } } [ScriptObjectPropertyAttribute("Paged List Forecast With Current Condition", "Provides the current condition and a list of daily forcasts to be shown in a Touch Screen Interface's Paged List control. The item value has the following properties: Index. Index is the index for the weather driver property arrays.")] [SupportsDriverPropertyBinding] public ScriptPagedListCollection PagedListForecastWithCurrentCondition { get { var list = PagedListForecast; // Insert current condition string title = "Current"; string subtitle = (int)this.Temperature + "\u00B0" + " " + (string)this.CurrentCondition; ScriptExpandoObject value = new ScriptExpandoObject(); value.SetProperty("Index", new ScriptNumber(0)); list.Insert(0, new ScriptPagedListItem(title, subtitle, value)); return list; } } // // ---------------------------------------------------------------- // Driver private methods // ---------------------------------------------------------------- // // // Pull data from the DarkSky web site. // private bool ReadWeatherData() { JavaScriptSerializer serializer = new JavaScriptSerializer(); string resp; DarkSky root; WeatherData wd = new WeatherData(m_metric); string url; Logger.Debug("Read data from web site."); url = "/forecast/" + m_api_key + "/" + m_station_location; // +"?units=" + m_metric; // Data is JSON so we need to deserialize this into an object. Logger.Info("URL = " + url); resp = rest.Request(url); if (resp != "") { try { root = serializer.Deserialize(resp); m_weather.ApparentTemp = root.currently.apparentTemperature; m_weather.Dewpoint = root.currently.dewPoint; m_weather.Humidity = root.currently.humidity * 100; m_weather.Pressure = root.currently.pressure; m_weather.Precipitation = root.currently.precipIntensity; //wd.SolarRadiation = root.currently. m_weather.Temperature = root.currently.temperature; m_weather.UV = root.currently.uvIndex; m_weather.WindDegrees = root.currently.windBearing; m_weather.WindGust = root.currently.windGust; m_weather.WindSpeed = root.currently.windSpeed; m_weather.PrecipitationProbablilty = root.currently.precipProbability * 100; if (root.currently.precipType != null) m_weather.PrecipitationType = root.currently.precipType; else m_weather.PrecipitationType = "none"; m_weather.CloudCover = root.currently.cloudCover * 100; m_weather.Visibility = root.currently.visibility; m_weather.Ozone = root.currently.ozone; m_weather.Summary = root.currently.summary; m_weather.Latitude = root.latitude; m_weather.Longitude = root.longitude; m_weather.Sunrise = FromUnixTime(root.daily.data[0].sunriseTime).ToString(); m_weather.Sunset = FromUnixTime(root.daily.data[0].sunsetTime).ToString(); m_weather.MoonAge = root.daily.data[0].moonPhase; m_weather.Weather = root.daily.summary; m_weather.Credit = "DarkSky.net"; m_weather.CreditURL = "http://darksky.net/"; m_weather.Icon = root.currently.icon; // TODO: Calculate HeatIndex and WindChill // We can fill out forecast data for X days int day = 0; foreach (Datum3 forecast in root.daily.data) { // First forecast is for today? m_forecasts[day].Time = FromUnixTime(forecast.time); Logger.Debug("Getting forecast data for day = " + day.ToString() + " " + m_forecasts[day].Time.ToString()); m_forecasts[day].Condition = forecast.summary; m_forecasts[day].DayForecastText = forecast.summary; m_forecasts[day].High = forecast.temperatureHigh; m_forecasts[day].Low = forecast.temperatureLow; m_forecasts[day].Pop = forecast.precipProbability * 100; m_forecasts[day].Sunrise = FromUnixTime(forecast.sunriseTime).ToString(); m_forecasts[day].Sunset = FromUnixTime(forecast.sunsetTime).ToString(); m_forecasts[day].WeekDay = m_forecasts[day].Time.DayOfWeek.ToString(); m_forecasts[day].DateText = m_forecasts[day].Time.ToShortDateString(); m_forecasts[day].MoonPhase = forecast.moonPhase; if (forecast.precipType != null) m_forecasts[day].PrecipitationType = forecast.precipType; else m_forecasts[day].PrecipitationType = "none"; m_forecasts[day].ApparentTemperatureHigh = forecast.apparentTemperatureHigh; m_forecasts[day].ApparentTemperatureLow = forecast.apparentTemperatureLow; m_forecasts[day].DewPoint = forecast.dewPoint; m_forecasts[day].Humidity = forecast.humidity * 100; m_forecasts[day].Pressure = forecast.pressure; m_forecasts[day].WindSpeed = forecast.windSpeed; m_forecasts[day].WindGust = forecast.windGust; m_forecasts[day].WindDir = forecast.windBearing; m_forecasts[day].CloudCover = forecast.cloudCover * 100; m_forecasts[day].Visibility = forecast.visibility; m_forecasts[day].UV = forecast.uvIndex; m_forecasts[day].Ozone = forecast.ozone; m_forecasts[day].Icon = forecast.icon; day++; } } catch (Exception ex) { Logger.Error("Failed to parse response: " + ex.Message); return false; } } else { Logger.Error("No response from DarkSky servers"); return false; } return true; } // // Thread to read data from the Brultech energy monitor // This should poll the monitor and then do any necessary // processing of the data. // private void PollCurrentConditions(Object sender, EventArgs e) { WeatherData old; old = CopyWeatherData(m_weather); if (ReadWeatherData()) { DevicePropertyChangeNotification("BarometricPressure", m_weather.Pressure); DevicePropertyChangeNotification("BarometricTrend", m_weather.PressureTrend); DevicePropertyChangeNotification("BarometricUnits", m_weather.PressureUnits); DevicePropertyChangeNotification("DewPoint", m_weather.Dewpoint); DevicePropertyChangeNotification("HeatIndex", m_weather.HeatIndex); DevicePropertyChangeNotification("Humidity", m_weather.Humidity); DevicePropertyChangeNotification("LastUpdate", m_weather.LastUpdate); DevicePropertyChangeNotification("Location", m_weather.Location); DevicePropertyChangeNotification("Precipitation", m_weather.Precipitation); DevicePropertyChangeNotification("SolarRadiation", m_weather.SolarRadiation); DevicePropertyChangeNotification("Sunset", m_weather.Sunset); DevicePropertyChangeNotification("Sunrise", m_weather.Sunrise); DevicePropertyChangeNotification("Temperature", m_weather.Temperature); DevicePropertyChangeNotification("UVIndex", m_weather.UV); DevicePropertyChangeNotification("WindDirectionDegrees", m_weather.WindDegrees); DevicePropertyChangeNotification("WindDirectionText", m_weather.WindDirection); DevicePropertyChangeNotification("WindGustSpeed", m_weather.WindGust); DevicePropertyChangeNotification("WindSpeed", m_weather.WindSpeed); DevicePropertyChangeNotification("Windchill", m_weather.WindChill); DevicePropertyChangeNotification("ApparentTemperature", m_weather.ApparentTemp); DevicePropertyChangeNotification("PrecipitationType", m_weather.PrecipitationType); DevicePropertyChangeNotification("ProbabilityOfPrecipitation", m_weather.PrecipitationProbablilty); DevicePropertyChangeNotification("CloudCoverage", m_weather.CloudCover); DevicePropertyChangeNotification("Visibility", m_weather.Visibility); DevicePropertyChangeNotification("Ozone", m_weather.Ozone); TriggerHandler(old, m_weather); Logger.Debug("Retrieve Forecast data."); // Not an array DevicePropertyChangeNotification("PrecipitationChance", m_forecasts[0].Pop); DevicePropertyChangeNotification("LastForecastUpdate", m_forecasts[0].LastUpdate); for (int i = 0; i < m_forecasts.Length; i++) { DevicePropertyChangeNotification("Highs", i, m_forecasts[i].High); DevicePropertyChangeNotification("Lows", i, m_forecasts[i].Low); DevicePropertyChangeNotification("Conditions", i, m_forecasts[i].Condition); DevicePropertyChangeNotification("Dates", i, m_forecasts[i].Time); DevicePropertyChangeNotification("DayDescriptions", i, m_forecasts[i].DayForecastText); //DevicePropertyChangeNotification("NightDescriptions", i, m_forecasts[i].NightForecastText); DevicePropertyChangeNotification("DayPrecipitations", i, m_forecasts[i].Pop); //DevicePropertyChangeNotification("NightConditions", i, m_forecasts[i].NightCondition); DevicePropertyChangeNotification("Sunrise", i, m_forecasts[i].Sunrise); DevicePropertyChangeNotification("Sunset", i, m_forecasts[i].Sunset); DevicePropertyChangeNotification("DatesText", i, m_forecasts[i].DateText); DevicePropertyChangeNotification("PrecipitationTypes", i, m_forecasts[i].PrecipitationType); DevicePropertyChangeNotification("MoonPhases", i, m_forecasts[i].MoonPhase); DevicePropertyChangeNotification("ApparentHighs", i, m_forecasts[i].ApparentTemperatureHigh); DevicePropertyChangeNotification("ApparentLows", i, m_forecasts[i].ApparentTemperatureLow); DevicePropertyChangeNotification("Humidities", i, m_forecasts[i].Humidity); DevicePropertyChangeNotification("DewPoints", i, m_forecasts[i].DewPoint); DevicePropertyChangeNotification("Pressures", i, m_forecasts[i].Pressure); DevicePropertyChangeNotification("WindSpeeds", i, m_forecasts[i].WindSpeed); DevicePropertyChangeNotification("WindGusts", i, m_forecasts[i].WindGust); DevicePropertyChangeNotification("WindDirections", i, m_forecasts[i].WindDir); DevicePropertyChangeNotification("CloudCoverages", i, m_forecasts[i].CloudCover); DevicePropertyChangeNotification("Visibilities", i, m_forecasts[i].Visibility); DevicePropertyChangeNotification("UVIndexes", i, m_forecasts[i].UV); DevicePropertyChangeNotification("Ozones", i, m_forecasts[i].Ozone); } } } // // Formula: // water_vapor_pressure = relative_humidity / 100 * 6.105 * math.exp(17.27 * temp_c / (237.7 + temp_c)) // at = temp_c + (0.33 * water_vapor_pressure) - (0.70 * wind speed) - 4 // wind speed is in meter/s // // Convert icon names to icon ID's // // sunny/cloudy icons from clear to overcast: // day 32 34 30 28 26 // night 31 33 29 27 26 // // sunny d32 n31 // mostly sunny d34 n33 // partly sunny d30 n29 // partly cloudy d30 n29 // mostly cloudy d28 n27 // cloudy d26 n26 internal int IconID(string icon) { switch (icon) { case "clear-day": return 32; case "clear-night": return 31; case "rain": return 40; case "snow": return 42; case "sleet": return 5; case "wind": return 44; // FIXME case "fog": return 20; case "cloudy": return 26; case "partly-cloudy-day": return 30; case "partly-cloudy-night": return 29; // Not currently defined but may be at some point case "flurries": return 14; case "hazy": return 21; case "mostly-cloudy-day": return 28; case "partly-sunny-day": return 30; case "mostly-sunny-day": return 34; case "sunny": return 32; case "tstorms": return 17; case "chance-flurries": return 13; case "chance-rain": return 39; case "chance-sleet": return 5; case "chance-snow": return 41; case "chance-tstorms": return 17; case "cloudy-night": return 26; case "flurries-night": return 46; case "fog-night": return 20; case "hazy-night": return 21; case "mostly-cloudy-night": return 27; case "partly-sunny-night": return 29; case "mostly-sunny-night": return 33; case "rain-night": return 45; case "sleet-night": return 5; case "snow-night": return 42; case "sunny-night": return 31; case "tstorms-night": return 47; case "N/A": case "unknown": default: Logger.Debug("No IconID match for icon named [" + icon + "]"); return 44; // 44 = Unknown } } internal string IconNameToCondition(string icon) { switch (icon) { case "clear": return "Clear"; case "flurries": return "Snow Flurries"; case "fog": return "Foggy"; case "hazy": return "Hazy"; case "cloudy": return "Cloudy"; case "mostlycloudy": return "Mostly Cloudy"; case "partlycloudy": return "Partly Cloudy"; case "partlysunny": return "Partly Sunny"; case "mostlysunny": return "Mostly Sunny"; case "rain": return "Rain"; case "sleet": return "Sleet"; case "snow": return "Snow"; case "sunny": return "Clear"; case "tstorms": return "Thunderstorms"; case "chanceflurries": return "Chance of Flurries"; case "chancerain": return "Chance of Rain"; case "chancesleet": return "Chance of Sleet"; case "chancesnow": return "Chance of Snow"; case "chancetstorms": return "Chance of Thunderstorms"; case "unknown": return "Unknown"; case "nt_clear": return "Clear"; case "nt_cloudy": return "Cloudy"; case "nt_flurries": return "Flurries"; case "nt_fog": return "Foggy"; case "nt_hazy": return "Hazy"; case "nt_mostlycloudy": return "Mostly Cloudy"; case "nt_partlycloudy": return "Partly Cloudy"; case "nt_partlysunny": return "Partly Clear"; case "nt_mostlysunny": return "Mostly Clear"; case "nt_rain": return "Rain"; case "nt_sleet": return "Sleet"; case "nt_snow": return "Snow"; case "nt_sunny": return "Clear"; case "nt_tstorms": return "Thunderstorms"; default: Logger.Debug("No match for icon named [" + icon + "]"); return "Unknown"; } } #region "Driver Events" private WeatherData CopyWeatherData(WeatherData old) { WeatherData copy = new WeatherData(m_metric); copy.Temperature = old.Temperature; copy.Humidity = old.Humidity; copy.WindSpeed = old.WindSpeed; copy.WindGust = old.WindGust; copy.PressureString = old.PressureString; copy.Precipitation = old.Precipitation; return copy; } [DriverEvent("Weather Trigger", "Occurs when there is a change in weather conditions.")] [DriverEventParameterAttribute("Condition", "The weather condition that changed.", false)] [DriverEventArg("Value", "The value of the condition that changed.", typeof(ScriptNumber))] public DriverEvent WeatherTrigger; private void TriggerHandler(WeatherData old, WeatherData now) { DriverEventArgDictionary eventArgs; DriverEventParameterDictionary eventParameters; if (old.Temperature != now.Temperature) { eventParameters = new DriverEventParameterDictionary(); eventArgs = new DriverEventArgDictionary(); eventParameters.Add("Condition", "Temperature", true); eventArgs.Add("Value", new ScriptNumber(now.Temperature)); Logger.DebugFormat("{0}: Raise Trigger for new temperature {1}", DeviceName, now.Temperature); RaiseDeviceEvent(WeatherTrigger, eventParameters, eventArgs); } if (old.Humidity != now.Humidity) { eventParameters = new DriverEventParameterDictionary(); eventArgs = new DriverEventArgDictionary(); eventParameters.Add("Condition", "Humidity", true); eventArgs.Add("Value", new ScriptNumber(now.Humidity)); Logger.DebugFormat("{0}: Raise Trigger for new humidity {1}", DeviceName, now.Humidity); RaiseDeviceEvent(WeatherTrigger, eventParameters, eventArgs); } if (old.WindSpeed != now.WindSpeed) { eventParameters = new DriverEventParameterDictionary(); eventArgs = new DriverEventArgDictionary(); eventParameters.Add("Condition", "WindSpeed", true); eventArgs.Add("Value", new ScriptNumber(now.WindSpeed)); Logger.DebugFormat("{0}: Raise Trigger for new wind speed {1}", DeviceName, now.WindSpeed); RaiseDeviceEvent(WeatherTrigger, eventParameters, eventArgs); } if (old.WindGust != now.WindGust) { eventParameters = new DriverEventParameterDictionary(); eventArgs = new DriverEventArgDictionary(); eventParameters.Add("Condition", "WindGust", true); eventArgs.Add("Value", new ScriptNumber(now.WindGust)); Logger.DebugFormat("{0}: Raise Trigger for new wind gust {1}", DeviceName, now.WindGust); RaiseDeviceEvent(WeatherTrigger, eventParameters, eventArgs); } if (old.Precipitation != now.Precipitation) { eventParameters = new DriverEventParameterDictionary(); eventArgs = new DriverEventArgDictionary(); eventParameters.Add("Condition", "Precipitation", true); eventArgs.Add("Value", new ScriptNumber(now.Precipitation)); Logger.DebugFormat("{0}: Raise Trigger for new precipitation {1}", DeviceName, now.Precipitation); RaiseDeviceEvent(WeatherTrigger, eventParameters, eventArgs); } // TODO: Add other values } #endregion } // // Class to hold current weather data internal class WeatherData { internal string ForecastLoc { get; set; } internal string Location {get; set;} internal string LocationElevation {get; set;} internal string LocationNeighborhood {get; set;} internal string LocationCity {get; set;} internal string LocationState {get; set;} internal string LocationZip {get; set;} internal double Latitude { get; set; } internal double Longitude { get; set; } internal string Station {get; set;} internal string LastUpdate {get; set;} internal double Temperature {get; set;} internal double Humidity {get; set;} internal double WindSpeed {get; set;} internal double WindGust {get; set;} internal string WindDirection {get; set;} internal int WindDegrees {get; set;} internal double Dewpoint {get; set;} internal double Precipitation {get; set;} internal double SolarRadiation {get; set;} internal double UV {get; set;} internal string TemperatureString {get; set;} internal string PressureString {get; set;} internal string DewpointString {get; set;} internal string HeatIndexString {get; set;} internal string WindChillString {get; set;} internal string Credit { get; set; } internal string CreditURL { get; set; } internal double MoonAge { get; set; } internal string MoonLight { get; set; } internal string Sunset { get; set; } internal string Sunrise { get; set; } internal double ApparentTemp { get; set; } internal double m_pressure; internal double m_old_pressure; internal string Weather { get; set; } internal string StationID { get; set; } internal string StationType { get; set; } internal string ForecastURL { get; set; } internal string Summary { get; set; } internal string PrecipitationType { get; set; } internal double PrecipitationProbablilty { get; set; } internal double CloudCover { get; set; } internal double Visibility { get; set; } internal double Ozone { get; set; } internal string Icon { get; set; } private Boolean metric; internal WeatherData(bool units) { // Initialize data structure metric = units; ForecastLoc = ""; Location = ""; LocationElevation = ""; LocationNeighborhood = ""; LocationCity = ""; LocationState = ""; LocationZip = ""; Latitude = 0.0; Longitude = 0.0; Station = ""; LastUpdate = ""; Temperature = 0.0; Humidity = 0.0; WindSpeed = 0.0; WindGust = 0.0; WindDirection = ""; WindDegrees = 0; Dewpoint = 0.0; Precipitation = 0.0; SolarRadiation = 0.0; UV = 0.0; TemperatureString = ""; PressureString = ""; DewpointString = ""; HeatIndexString = ""; WindChillString = ""; Credit = ""; CreditURL = ""; MoonAge = 0.0; MoonLight = ""; Sunset = ""; Sunrise = ""; ApparentTemp = 0.0; Weather = ""; StationID = ""; StationType = ""; ForecastURL = ""; PrecipitationType = ""; } internal double Pressure { set { m_old_pressure = m_pressure; m_pressure = value; } get { return m_pressure; } } internal string PressureTrend { get { // TODO: This should check multiple pressure readings, not just the last // one. There should be an array of readings. if (m_old_pressure > 0) { if (m_old_pressure < Pressure) { return "raising"; } else if (m_old_pressure > Pressure) { return "falling"; } else { return "steady"; } } else { return "N/A"; } } } internal string PressureUnits { get { return "Millibars"; } } internal double HeatIndex { get { double t = Temperature; double h = Humidity; double c1 = -42.379; double c2 = 2.04901523; double c3 = 10.14333127; double c4 = -0.22475541; double c5 = -6.83783 * Math.Pow(10, -3); double c6 = -5.481717 * Math.Pow(10, -2); double c7 = 1.22874 * Math.Pow(10, -3); double c8 = 8.5282 * Math.Pow(10, -4); double c9 = -1.99 * Math.Pow(10, -6); if ((t < 80.0) || (h < 40.0)) return t; else return (c1 + (c2 * t) + (c3 * h) + (c4 * t * h) + (c5 * t * t) + (c6 * h * h) + (c7 * t * t * h) + (c8 * t * h * h) + (c9 * t * t * h * h)); } } internal double WindChill { get { double t = Temperature; double v = WindSpeed; if ((t < 50.0) && (v > 5.0)) return 35.74 + (0.6215 * t) - (35.75 * Math.Pow(v, 0.16)) + (0.4275 * t * Math.Pow(v, 0.16)); else return t; } } } public class Currently { public int time { get; set; } public string summary { get; set; } public string icon { get; set; } public double nearestStormDistance { get; set; } public int nearestStormBearing { get; set; } public double precipIntensity { get; set; } public double precipProbability { get; set; } public string precipType { get; set; } public double temperature { get; set; } public double apparentTemperature { get; set; } public double dewPoint { get; set; } public double humidity { get; set; } public double pressure { get; set; } public double windSpeed { get; set; } public double windGust { get; set; } public int windBearing { get; set; } public double cloudCover { get; set; } public int uvIndex { get; set; } public double visibility { get; set; } public double ozone { get; set; } } public class Datum { public int time { get; set; } public double precipIntensity { get; set; } public double precipProbability { get; set; } } public class Minutely { public string summary { get; set; } public string icon { get; set; } public List data { get; set; } } public class Datum2 { public int time { get; set; } public string summary { get; set; } public string icon { get; set; } public double precipIntensity { get; set; } public double precipProbability { get; set; } public double temperature { get; set; } public double apparentTemperature { get; set; } public double dewPoint { get; set; } public double humidity { get; set; } public double pressure { get; set; } public double windSpeed { get; set; } public double windGust { get; set; } public int windBearing { get; set; } public double cloudCover { get; set; } public int uvIndex { get; set; } public double visibility { get; set; } public double ozone { get; set; } public string precipType { get; set; } } public class Hourly { public string summary { get; set; } public string icon { get; set; } public List data { get; set; } } public class Datum3 { public int time { get; set; } public string summary { get; set; } public string icon { get; set; } public int sunriseTime { get; set; } public int sunsetTime { get; set; } public double moonPhase { get; set; } public double precipIntensity { get; set; } public double precipIntensityMax { get; set; } public int precipIntensityMaxTime { get; set; } public double precipProbability { get; set; } public string precipType { get; set; } public double temperatureHigh { get; set; } public int temperatureHighTime { get; set; } public double temperatureLow { get; set; } public int temperatureLowTime { get; set; } public double apparentTemperatureHigh { get; set; } public int apparentTemperatureHighTime { get; set; } public double apparentTemperatureLow { get; set; } public int apparentTemperatureLowTime { get; set; } public double dewPoint { get; set; } public double humidity { get; set; } public double pressure { get; set; } public double windSpeed { get; set; } public double windGust { get; set; } public int windGustTime { get; set; } public int windBearing { get; set; } public double cloudCover { get; set; } public int uvIndex { get; set; } public int uvIndexTime { get; set; } public double visibility { get; set; } public double ozone { get; set; } public double temperatureMin { get; set; } public int temperatureMinTime { get; set; } public double temperatureMax { get; set; } public int temperatureMaxTime { get; set; } public double apparentTemperatureMin { get; set; } public int apparentTemperatureMinTime { get; set; } public double apparentTemperatureMax { get; set; } public int apparentTemperatureMaxTime { get; set; } } public class Daily { public string summary { get; set; } public string icon { get; set; } public List data { get; set; } } public class Alert { public string title { get; set; } public List regions { get; set; } public string severity { get; set; } public int time { get; set; } public int expires { get; set; } public string description { get; set; } public string uri { get; set; } } public class Flags { public List sources { get; set; } public double nearest_station { get; set; } public string units { get; set; } } public class DarkSky { public double latitude { get; set; } public double longitude { get; set; } public string timezone { get; set; } public Currently currently { get; set; } public Minutely minutely { get; set; } public Hourly hourly { get; set; } public Daily daily { get; set; } public List alerts { get; set; } public Flags flags { get; set; } public int offset { get; set; } } internal class Forecast { internal string DateText { get; set; } internal double Pop { get; set; } internal double High { get; set; } internal double Low { get; set; } internal string Condition { get; set; } internal string DayForecastTitle { get; set; } internal string DayForecastText { get; set; } internal string NightForecastTitle { get; set; } internal string NightForecastText { get; set; } internal string NightCondition { get; set; } internal string Sunrise { get; set; } internal string Sunset { get; set; } internal string LastUpdate { get; set; } internal string WeekDay { get; set; } internal string IconURL { get; set; } internal DateTime Time { get; set; } internal double MoonPhase { get; set; } internal string PrecipitationType { get; set; } internal double ApparentTemperatureHigh { get; set; } internal double ApparentTemperatureLow { get; set; } internal double DewPoint { get; set; } internal double Humidity { get; set; } internal double Pressure { get; set; } internal double WindSpeed { get; set; } internal double WindGust { get; set; } internal double WindDir { get; set; } internal double CloudCover { get; set; } internal double Visibility { get; set; } internal double UV { get; set; } internal double Ozone { get; set; } internal string Icon { get; set; } private bool metric; internal Forecast(bool use_metric) { metric = use_metric; DateText = "N/A"; Pop = 0; Condition = "N/A"; High = 0.0; Low = 0.0; DayForecastTitle = "N/A"; DayForecastText = "N/A"; NightForecastTitle = "N/A"; NightForecastText = "N/A"; NightCondition = "N/A"; Sunrise = "N/A"; Sunset = "N/A"; LastUpdate = "Never"; WeekDay = ""; IconURL = ""; PrecipitationType = ""; ApparentTemperatureHigh = 0; ApparentTemperatureLow = 0; DewPoint = 0; Humidity = 0; Pressure = 0; WindSpeed = 0; WindGust = 0; WindDir = 0; CloudCover = 0; Visibility = 0; UV = 0; Ozone = 0; } } #region "SolarInfo sunrise/sunset calculations" // // SolarInfo // // Calculate sunrise and sunset times for a specific latitude and // longitude. // // Acknowledgements: // Based on a 'C' program by Robert Bond // The GST algorithms are from Sky and Telescope, June 1984 by Roger W. Sinnott // Adapted from algorithms presented in "Pratical Astronomy With Your Calculator" by Peter Duffet-Smith // internal class SolarInfo { internal DateTime Sunrise { get; private set; } internal DateTime Sunset { get; private set; } internal static ILogger Logger { get; set; } private SolarInfo() { } internal static SolarInfo ForDate(double latitude, double longitude, DateTime date) { TimeZone localzone = TimeZone.CurrentTimeZone; double JDE = 2444238.5; // Julian date of EPOCH SolarInfo info = new SolarInfo(); int year = date.Year; int month = date.Month; int day = date.Day; int tzl; double jd = JulianDate(month, day, year); double ed = jd - JDE; double lambda1 = solar_lon(ed); double lambda2 = solar_lon(ed + 1.0); double alpha1; double alpha2; double delta1; double delta2; // For some reason, this code thinks that west longitudes should // be positive, not negative. Reverse the sign. longitude *= -1.0; // Again, the code seems to think that the timezone offset // is backwards I.E. PST is 8 hrs, not -8 hrs from GMT. tzl = localzone.GetUtcOffset(date).Hours * -1; alpha1 = atan_q_deg((Math.Sin(deg2rad(lambda1))) * Math.Cos(deg2rad(23.441884)), Math.Cos(deg2rad(lambda1))) / 15.0; delta1 = asin_deg(Math.Sin(deg2rad(23.441884)) * Math.Sin(deg2rad(lambda1))); alpha2 = atan_q_deg((Math.Sin(deg2rad(lambda2))) * Math.Cos(deg2rad(23.441884)), Math.Cos(deg2rad(lambda2))) / 15.0; delta2 = asin_deg(Math.Sin(deg2rad(23.441884)) * Math.Sin(deg2rad(lambda2))); //Logger.Debug("Right ascension, declination for lon " + lambda1.ToString() + "is " + alpha1.ToString() + ", " + delta1.ToString()); //Logger.Debug("Right ascension, declination for lon " + lambda2.ToString() + "is " + alpha2.ToString() + ", " + delta2.ToString()); double st1r = rise(alpha1, delta1, latitude); double st1s = set(alpha1, delta1, latitude); double st2r = rise(alpha2, delta2, latitude); double st2s = set(alpha2, delta2, latitude); double m1 = adj24(gmst(jd - 0.5, 0.5 + tzl / 24.0) - longitude / 15); //Logger.Debug("local sidreal time of midnight is " + m1.ToString() + " lon = " + longitude.ToString()); double hsm = adj24(st1r - m1); //Logger.Debug("about " + hsm.ToString() + " hourse from midnight to dawn"); double ratio = hsm / 24.07; //Logger.Debug(ratio.ToString() + " is how far dawn is into the day"); if (Math.Abs(st2r - st1r) > 1.0) { st2r += 24.0; //Logger.Debug("st2r corrected from " + (st2r-24.0).ToString() + " to " + st2r.ToString()); } double trise = adj24((1.0 - ratio) * st1r + ratio * st2r); hsm = adj24(st1s - m1); //Logger.Debug("about " + hsm.ToString() + " hours from midnight to sunset"); ratio = hsm / 24.07; //Logger.Debug(ratio.ToString() + " is ho far sunset is into the day"); if (Math.Abs(st2s - st1s) > 1.0) { st2s += 24.0; //Logger.Debug("st2s corrected from " + (st2s-24.0).ToString() + " to " + st2s.ToString()); } double tset = adj24((1.0 - ratio) * st1s + ratio * st2s); //Logger.Debug("Uncorrected rise = " + trise.ToString() + ", set = " + tset.ToString()); //$ar = $a1r * 360.0 / (360.0 + $a1r - $a2r); //$as = $a1s * 360.0 / (360.0 + $a1s - $a2s); double delta = (delta1 + delta2) / 2.0; double tri = acos_deg(sin_deg(latitude) / cos_deg(delta)); double x = 0.835608; // correction for refraction, parallax double y = asin_deg(sin_deg(x) / sin_deg(tri)); // $da = &asin_deg(&tan_deg($x)/&tan_deg($tri)); double dt = 240.0 * y / cos_deg(delta) / 3600; //Logger.Debug("Corrections: dt = " + dt.ToString()); info.Sunrise = date.Date.AddMinutes( lst_to_hm(trise - dt, jd, tzl, longitude, year)); info.Sunset = date.Date.AddMinutes( lst_to_hm(tset + dt, jd, tzl, longitude, year)); return info; } // end of ForDate // // rtod // // radains to degrees conversion private static double rtod(double deg) { return (deg * 180.0 / Math.PI); } // // adj360 // // convert to number between 0 and 360 private static double adj360(double deg) { while (deg < 0.0) { deg += 360.0; } while (deg > 360.0) { deg -= 360.0; } return deg; } // // adj24 // // convert to a number between 0 and 24 private static double adj24(double hrs) { while (hrs < 0.0) { hrs += 24.0; } while (hrs > 24.0) { hrs -= 24.0; } return hrs; } // // JulianDate // // Given a month, day, year, calculate the julian date and return // it. private static double JulianDate(int m, int d, int y) { int a; int b; double jd; if ((m == 1) || (m == 2)) { y--; m += 12; } // Can't handle dates before 1583 if (y < 1583) { return 0; } a = (int)(y / 100); b = (int)(2 - a + a / 4); b += (int)(y * 365.25); b += (int)((30.6001 * (m + 1.0))); jd = (double)d + (double)b + 1720994.5; //Logger.Debug("Julian date for " + m.ToString() + "/" + d.ToString() + "/" + y.ToString() + " is " + jd.ToString()); return jd; } // // solar_lon // // ??? private static double solar_lon(double ed) { double n; double m; double e; double ect; double errt; double v; n = 360.0 * ed / 365.2422; n = adj360(n); m = n + 278.83354 - 282.596403; m = adj360(m); m = deg2rad(m); e = m; ect = 0.016718; while ((errt = e - ect * Math.Sin(e) - m) > 0.0000001) { e = e - errt / (1 - ect * Math.Cos(e)); } v = 2 * Math.Atan(1.0168601 * Math.Tan(e / 2)); v = adj360(((v * 180.0) / Math.PI) + 282.596403); //Logger.Debug("Solar Longitude for " + ed.ToString() + " days is " + v.ToString()); return v; } // // acos_deg // // returns the arc cosin in degrees private static double acos_deg(double x) { return rtod(Math.Acos(x)); } // // asin_deg // // returns the arc sin in degrees private static double asin_deg(double x) { return rtod(Math.Asin(x)); } // // atan_q_deg // // returns the arc tangent in degrees and does what to it? private static double atan_q_deg(double y, double x) { double rv; if (y == 0) { rv = 0; } else if (x == 0) { rv = (y > 0) ? 90.0 : -90.0; } else { rv = atan_deg(y / x); } if (x < 0) { return rv + 180.0; } if (y < 0) { return rv + 360.0; } return rv; } // // atan_q_deg // // returns the arc tangent in degrees private static double atan_deg(double x) { return rtod(Math.Atan(x)); } // // sin_deg // // returns the sin in degrees private static double sin_deg(double x) { return Math.Sin(deg2rad(x)); } // // cos_deg // // returns the cos in degrees private static double cos_deg(double x) { return Math.Cos(deg2rad(x)); } // // tan_deg // // returns the tangent in degrees private static double tan_deg(double x) { return Math.Tan(deg2rad(x)); } // // rise_set // // Calculates the uncorrected sun rise and set times private static double rise(double alpha, double delta, double lat) { double tar = sin_deg(delta) / cos_deg(lat); if ((tar < -1.0) || (tar > 1.0)) { return 0.0; } double h = acos_deg(-tan_deg(lat) * tan_deg(delta)) / 15.0; double lstr = 24.0 + alpha - h; if (lstr > 24.0) { lstr -= 24.0; } return lstr; } private static double set(double alpha, double delta, double lat) { double tar = sin_deg(delta) / cos_deg(lat); if ((tar < -1.0) || (tar > 1.0)) { return 0.0; } double h = acos_deg(-tan_deg(lat) * tan_deg(delta)) / 15.0; double lsts = alpha + h; if (lsts > 24.0) { lsts -= 24.0; } return lsts; } // // lst_to_hm // // converts a time value and juilan date to minutes past midnight private static int lst_to_hm(double lst, double jd, double tzl, double lon, int yr) { double gst = lst + lon / 15.0; if (gst > 24.0) { gst -= 24.0; } double jzjd = JulianDate(1, 0, yr); double ed = jd - jzjd; double t = (jzjd - 2415020.0) / 36525.0; double r = 6.6460656 + 2400.05126 * t + 2.58E-05 * t * t; double b = 24.0 - (r - 24.0 * (yr - 1900)); double t0 = ed * 0.0657098 - b; if (t0 < 0.0) { t0 += 24; } double gmt = gst - t0; if (gmt < 0) { gmt += 24.0; } gmt = gmt * 0.99727 - tzl; if (gmt < 0) { gmt += 24.0; } // gmt is decimal hours past midnight. The DateTime // AddMinutes() method needs minutes. Convert gmt // to minutes and return. return (int)((gmt * 60) + 0.5); } // // gmst // // calculates some kind of time value ??? private static double gmst(double j, double f) { double d = j - 2451545.0; double t = d / 36525.0; double t1 = Math.Floor(t); double j0 = t1 * 36525.0 + 2451545.0; double t2 = (j - j0 + 0.5) / 36525.0; double s = 24110.54841 + 184.812866 * t1; s += 8640184.812866 * t2; s += 0.093104 * t * t; s -= 0.0000062 * t * t * t; s /= 86400.0; s -= Math.Floor(s); s = 24 * (s + (f - 0.5) * 1.002737909); if (s < 0) { s += 24.0; } if (s > 24.0) { s -= 24.0; } //Logger.Debug("For jd = " + j.ToString() + ", f = " + f.ToString() + " , gst = " + s.ToString()); return s; } // // deg2rad // // converts degrees to radians private static double deg2rad(double deg) { return deg * Math.PI / 180.0; } } // End of SolarInfo class #endregion }