/* * WSM4Hubitat * Weather Service Monitor (WSM) for Hubitat that can use a few different services. * * Description: * This Hubitat driver polls Weather Service APIs primarily for forecast data but in some cases also gets current conditions. * * Required information depends on service: * US - No information is required for the US weather.gov service * UK - An API Key must be hard coded on line # 412 of the driver for the UK's Met Office API (Includes United Kingdom & Australia) * Legacy UK - A ClientID and ClientSecret are needed for the UK's Met Office API (Includes United Kingdom & Australia) * - These can be obtained at https://metoffice.apiconnect.ibmcloud.com/metoffice/production/ * OW - An API Key is needed for OpenWeather's API * WA - An API Key is needed for Weather API * WU - An API Key is needed for Weather Underground API * WWO - An API Key is needed for World Weather Online's API * * Features List: * Ability to have current conditions for OW, WA, WWO, & WU * Ability to have child devices be for each forecasted day for OW, US NWS, & WU * Ability to check min/max temp for today and tomorrow from Weather API, World Weather Online API, OpenWeather API, * and UK Met Office API * Ability to have current conditions for UK Met Office, US NWS, OW, WA, WWO, & WU * Ability to check a website (mine) to notify user if there is a newer version of the driver available * * Licensing: * Copyright 2024 David Snell * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Version Control: * 0.9.7 - Correction to ProcessEvent function and removal of old driver-specific attributes when Preferences are saved * 0.9.6 - Initial changes to support UK Met Office migration to new API and API Keys, also updated driver specific attribute names * 0.9.5 - Made a check if child devices for week were already created * 0.9.4 - Correction to OpenWeather so rain.1h is no longer required * 0.9.3 - Correction so UK is no longer fix in Uxbridge, changes to newer method for version checking * 0.9.2 - Additional current conditions applicable to WU * 0.9.1 - Added attributes for current conditions from OW, WA, WU & WWO * 0.9.0 - Added child devices for forecasts for US NWS, OW, & WU, requires the WeatherSensorChild.groovy driver to also be installed * 0.8.4 - Added a "Data as of" attribute to identify the date/time the Hubitat received a successful data return, * replaced ProcessData with ProcessEvent and added ProcessState (consistency between my drivers) * 0.8.3 - Added ValidTime attributes to indicate when the API said the weather was valid as of, * added 5 minute refresh option, changed logging at data retrieval to be info level * 0.8.2 - Added additional refresh rates * 0.8.1 - Corrected error in refresh times and added support for Weather tile using "Today's" attributes when available * 0.8.0 - Addition of Weather Underground API and some corrections to other areas * 0.7.0 - Rework to match semver, changes to logging, updating, and other areas * 0.06 - Major rework of how data is processed * 0.05 - Additional weather services added (World Weather Online & Weather API) * 0.04 - More attributes including URLs for icons and parsing US detailed forecast for unreported fields. * 0.03 - Addition of more attributes and checks * 0.02 - Inclusion of OpenWeather API * 0.01 - Initial design based on my Ambient Weather driver * * Thank you(s): * Thank you to @Cobra for inspiration of how I perform driver version checking. */ // Returns the driver name def DriverName(){ return "WSM4Hubitat" } // Returns the driver version def DriverVersion(){ return "0.9.7" } // Driver Metadata metadata{ definition( name: "WSM4Hubitat", namespace: "Snell", author: "David Snell", importUrl: "https://www.drdsnell.com/projects/hubitat/drivers/WSM4Hubitat.groovy" ) { // Indicate what capabilities the device should be capable of capability "Sensor" capability "Refresh" capability "PressureMeasurement" capability "RelativeHumidityMeasurement" capability "TemperatureMeasurement" capability "UltravioletIndex" capability "IlluminanceMeasurement" // Commands //command "UK_API_Key_Submission", [ // [ name: "UKAPIKEY*", type: "STRING", description: "Enter the API Key to use (UK Met Office Only)" ] //] // Field to submit the API Key needed for the UK's Met Office // Attributes for the driver itself attribute "DriverName", "string" // Identifies the driver being used for update purposes attribute "DriverVersion", "string" // Handles version for driver attribute "DriverStatus", "string" // Handles version notices for driver // Attributes needed for US NWS attribute "CWA", "string" attribute "GridX", "string" attribute "GridY", "string" // Attributes for the values returned that Hubitat supports normally attribute "temperature", "number" attribute "humidity", "number" attribute "pressure", "number" attribute "windSpeed", "number" attribute "windDir", "number" attribute "windDirection", "number" // Attributes for general purpose attribute "Updated", "string" // AttributeID = 0 - Date/time that the API says the data was "as of" attribute "Data as of", "string" // Date/time that the data was successfully retrieved as of Hubitat's Date() function // Attributes for Today's forecast attribute "Today_Temperature", "number" // AttributeID = 1 - US NWS, UK Met Office, & OpenWeather attribute "Today_WindSpeed", "number" // AttributeID = 11 - US NWS, UK Met Office, & OpenWeather attribute "Today_WindDirection", "number" // AttributeID = 21 - US NWS, UK Met Office, & OpenWeather attribute "Today_WindDirectionString", "string" // AttributeID = 91 - This attribute was created to use words for the direction of the wind attribute "Today_WindGust", "number" // AttributeID = 31 - UK Met Office & Parsed from US NWS Detailed Forecast attribute "Today_ShortForecast", "string" // AttributeID = 41 - US NWS, UK Met Office "Significant Weather Code" attribute "Today_DetailedForecast", "string" // AttributeID = 51 - US NWS attribute "Today_ChanceRain", "number" // AttributeID = 61 - OpenWeather, UK Met Office, & Parsed from US NWS Detailed Forecast attribute "Today_ChanceClouds", "number" // AttributeID = 71 - OpenWeather attribute "Today_ChanceSnow", "number" // AttributeID = 81 - OpenWeather, UK Met Office, & Parsed from US NWS Detailed Forecast attribute "Today_FeelsLike", "number" // AttributeID = 101 - OpenWeather attribute "Today_SunriseEpoch", "number" // AttributeID = 111 - OpenWeather attribute "Today_SunsetEpoch", "string" // AttributeID = 121 - OpenWeather attribute "Today_Sunrise", "string" // AttributeID = 131 - Calculated from OpenWeather attribute "Today_Sunset", "string" // AttributeID = 141 - Calculated from OpenWeather attribute "Today_IconURL", "string" // AttributeID = 151 - US NWS & OpenWeather attribute "Today_Humidity", "number" // AttributeID = 161 - OpenWeather attribute "Today_Pressure", "number" // AttributeID = 171 - OpenWeather attribute "Today_UVI", "number" // AttributeID = 181 - OpenWeather attribute "Today_MinTemp", "number" // AttributeID = 191 - attribute "Today_MaxTemp", "number" // AttributeID = 201 - attribute "Today_ValidTime", "string" // AttributeID = 211 - WeatherUnderground "validTimeLocal[ 0 ]" // Attributes for Tonight's forecast attribute "Tonight_Temperature", "number" // AttributeID = 2 - US NWS, UK Met Office, & OpenWeather attribute "Tonight_WindSpeed", "number" // AttributeID = 12 - US NWS & UK Met Office attribute "Tonight_WindDirection", "number" // AttributeID = 22 - US NWS & UK Met Office attribute "Tonight_WindDirectionString", "string" // AttributeID = 92 - This attribute was created to use words for the direction of the wind attribute "Tonight_WindGust", "number" // AttributeID = 32 - UK Met Office & Parsed from US NWS Detailed Forecast attribute "Tonight_ShortForecast", "string" // AttributeID = 42 - US NWS attribute "Tonight_DetailedForecast", "string" // AttributeID = 52 - US NWS attribute "Tonight_ChanceRain", "number" // AttributeID = 62 - UK Met Office "ProbabilityOfRain" & Parsed from US NWS Detailed Forecast attribute "Tonight_ChanceSnow", "number" // AttributeID = 82 - Parsed from US NWS Detailed Forecast attribute "Tonight_FeelsLike", "number" // AttributeID = 102 - OpenWeather attribute "Tonight_SunriseEpoch", "number" // AttributeID = 112 - OpenWeather attribute "Tonight_SunsetEpoch", "string" // AttributeID = 122 - OpenWeather attribute "Tonight_Sunrise", "string" // AttributeID = 132 - Calculated from OpenWeather attribute "Tonight_Sunset", "string" // AttributeID = 142 - Calculated from OpenWeather attribute "Tonight_IconURL", "string" // AttributeID = 152 - US NWS & OpenWeather attribute "Tonight_Humidity", "number" // AttributeID = 162 - OpenWeather attribute "Tonight_Pressure", "number" // AttributeID = 172 - OpenWeather //attribute "Tonight_UVI", "number" // AttributeID = 182 - OpenWeather // Attributes for Tomorrow's forecast attribute "Tomorrow_Temperature", "number" // AttributeID = 3 - US NWS, UK Met Office, & OpenWeather attribute "Tomorrow_WindSpeed", "number" // AttributeID = 13 - US NWS, UK Met Office, & OpenWeather attribute "Tomorrow_WindDirection", "number" // AttributeID = 23 - US NWS, UK Met Office, & OpenWeather attribute "Tomorrow_WindDirectionString", "string" // AttributeID = 93 - This attribute was created to use words for the direction of the wind attribute "Tomorrow_WindGust", "number" // AttributeID = 33 - UK Met Office & Parsed from US NWS Detailed Forecast attribute "Tomorrow_ShortForecast", "string" // AttributeID = 43 - US NWS, UK Met Office "Significant Weather Code" attribute "Tomorrow_DetailedForecast", "string" // AttributeID = 53 - US NWS attribute "Tomorrow_ChanceRain", "number" // AttributeID = 63 - OpenWeather, UK Met Office, & Parsed from US NWS Detailed Forecast attribute "Tomorrow_ChanceClouds", "number" // AttributeID = 73 - OpenWeather attribute "Tomorrow_ChanceSnow", "number" // AttributeID = 83 - OpenWeather, UK Met Office, & Parsed from US NWS Detailed Forecast attribute "Tomorrow_FeelsLike", "number" // AttributeID = 103 - OpenWeather attribute "Tomorrow_Sunrise", "string" // AttributeID = 113 - Calculated from OpenWeather attribute "Tomorrow_Sunset", "string" // AttributeID = 123 - Calculated from OpenWeather attribute "Tomorrow_SunriseEpoch", "number" // AttributeID = 133 - OpenWeather attribute "Tomorrow_SunsetEpoch", "string" // AttributeID = 143 - OpenWeather attribute "Tomorrow_IconURL", "string" // AttributeID = 153 - US NWS & OpenWeather attribute "Tomorrow_Humidity", "number" // AttributeID = 163 - OpenWeather attribute "Tomorrow_Pressure", "number" // AttributeID = 173 - OpenWeather attribute "Tomorrow_UVI", "number" // AttributeID = 183 - OpenWeather attribute "Tomorrow_MinTemp", "number" // AttributeID = 193 - attribute "Tomorrow_MaxTemp", "number" // AttributeID = 203 - attribute "Tomorrow_ValidTime", "string" // AttributeID = 213 - WeatherUnderground "validTimeLocal[ 1 ]" // Attributes for Tomorrow night's forecast attribute "Tomorrownight_Temperature", "number" // AttributeID = 4 - US NWS, UK Met Office, & OpenWeather attribute "Tomorrownight_WindSpeed", "number" // AttributeID = 14 - US NWS & UK Met Office attribute "Tomorrownight_WindDirection", "number" // AttributeID = 24 - US NWS & UK Met Office attribute "Tomorrownight_WindDirectionString", "string" // AttributeID = 94 - This attribute was created to use words for the direction of the wind attribute "Tomorrownight_WindGust", "number" // AttributeID = 34 - UK Met Office & Parsed from US NWS Detailed Forecast attribute "Tomorrownight_ShortForecast", "string" // AttributeID = 44 - US NWS attribute "Tomorrownight_DetailedForecast", "string" // AttributeID = 54 - US NWS attribute "Tomorrownight_ChanceRain", "number" // AttributeID = 64 - UK Met Office "ProbabilityOfRain" & Parsed from US NWS Detailed Forecast attribute "Tomorrownight_ChanceSnow", "number" // AttributeID = 84 - Parsed from US NWS Detailed Forecast attribute "Tomorrownight_FeelsLike", "number" // AttributeID = 104 - OpenWeather attribute "Tomorrownight_IconURL", "string" // AttributeID = 154 - US NWS & OpenWeather attribute "Tomorrownight_Humidity", "number" // AttributeID = 164 - OpenWeather attribute "Tomorrownight_Pressure", "number" // AttributeID = 174 - OpenWeather //attribute "Tomorrownight_UVI", "number" // AttributeID = 184 - OpenWeather // Current condition attributes that have been added as of 0.9.1 attribute "Conditions", "string" // attribute "solarradiation", "number" // attribute "uv", "number" // attribute "winddir", "number" // attribute "dewPoint", "number" // attribute "feelsLike", "number" // // attribute "hourlyRain", "number" // attribute "WindDirectionString", "string" // attribute "HeatIndex", "number" // attribute "WindChill", "number" // attribute "Neighborhood", "string" // attribute "Quality Control", "number" // attribute "PWS Software Type", "string" // attribute "Elevation", "number" // } preferences{ section{ if( ShowAllPreferences ){ if( Region == "US" ){ input( type: "bool", name: "ChildrenEnabled", title: "Enable Forecast Child Devices?", description: "Once enabled, child devices will be made for daily forecasting.", required: false, defaultValue: false ) //} else if( Region == "UK" ){ // input( type: "string", name: "APIKEY", title: "APIKEY for UK Met Office?", defaultValue: null ) } else if( Region == "Legacy UK" ){ input( type: "string", name: "ClientID", title: "Client ID for Legacy UK Met Office?", defaultValue: null ) input( type: "string", name: "ClientSecret", title: "Client Secret for Legacy UK Met Office?", defaultValue: null ) } else if( Region == "OW" ){ input( type: "string", name: "APIKey", title: "API Key for OpenWeather?", defaultValue: null ) input( type: "bool", name: "ChildrenEnabled", title: "Enable Forecast Child Devices?", description: "Once enabled, child devices will be made for daily forecasting.", required: false, defaultValue: false ) } else if( Region == "WA" ){ input( type: "string", name: "APIKey", title: "API Key for Weather API?", defaultValue: null ) } else if( Region == "WU" ){ input( type: "string", name: "StationID", title: "Station ID?", defaultValue: null ) input( type: "string", name: "APIKey", title: "API Key for Weather Underground?", defaultValue: null ) input( type: "bool", name: "ChildrenEnabled", title: "Enable Forecast Child Devices?", description: "Once enabled, child devices will be made for daily forecasting.", required: false, defaultValue: false ) } else if( Region == "WWO" ){ input( type: "string", name: "APIKey", title: "API Key for World Weather Online?", defaultValue: null ) } input( type: "enum", name: "Region", title: "Service to Query", required: true, multiple: false, options: [ [ "US" : "United States NWS" ], [ "UK" : "United Kingdom Met Office" ], [ "Legacy UK" : "Legacy United Kingdom Met Office" ], [ "OW" : "OpenWeather" ], [ "WA" : "Weather API" ], [ "WU" : "Weather Underground" ], [ "WWO" : "World Weather Online" ] ], defaultValue: "US" ) input( type: "enum", name: "RefreshRate", title: "Refresh Rate", required: true, multiple: false, options: [ "5 Minutes", "10 Minutes", "15 Minutes", "30 Minutes", "1 Hour", "3 Hours", "Daily", "Manual" ], defaultValue: "1 Hour" ) input( type: "enum", name: "LogType", title: "Enable Logging?", required: false, multiple: false, options: [ "None", "Info", "Debug", "Trace" ], defaultValue: "Info" ) input( type: "bool", name: "ShowAllPreferences", title: "Show All Preferences?", defaultValue: true ) } else { input( type: "bool", name: "ShowAllPreferences", title: "Show All Preferences?", defaultValue: true ) } } } } // updated is called whenever device parameters are saved def updated(){ unschedule() switch( RefreshRate ){ case "5 Minutes": runEvery5Minutes( refresh ) break case "10 Minutes": runEvery10Minutes( refresh ) break case "15 Minutes": runEvery15Minutes( refresh ) break case "30 Minutes": runEvery30Minutes( refresh ) break case "1 Hour": runEvery1Hour( refresh ) break case "3 Hours": runEvery3Hours( refresh ) break case "Daily": schedule( new Date(), refresh ) break case "Manual": break } Logging( "Refresh rate of ${ RefreshRate }", 3 ) // Set the driver name and version before update checking is scheduled if( state."Driver Name" != null ){ state.remove( "Driver Name" ) state.remove( "Driver Version" ) device.deleteCurrentState( "Driver Name" ) device.deleteCurrentState( "Driver Version" ) } ProcessState( "DriverName", "${ DriverName() }" ) ProcessState( "DriverVersion", "${ DriverVersion() }" ) // Schedule the daily driver version check schedule( new Date(), CheckForUpdate ) if( !ChildrenEnabled ){ getChildDevices().each{ Logging( "Children disabled, deleting ${ it.deviceNetworkId }", 3 ) deleteChildDevice( it.deviceNetworkId ) } } else { switch( Region ){ case "UK": case "Legacy UK": case "WA": case "WWO": break case "US": case "OW": case "WU": if( getChildDevice( "${ device.name } Sunday" ) == null ){ addChild( "${ device.name } Sunday" ) } if( getChildDevice( "${ device.name } Monday" ) == null ){ addChild( "${ device.name } Monday" ) } if( getChildDevice( "${ device.name } Tuesday" ) == null ){ addChild( "${ device.name } Tuesday" ) } if( getChildDevice( "${ device.name } Wednesday" ) == null ){ addChild( "${ device.name } Wednesday" ) } if( getChildDevice( "${ device.name } Thursday" ) == null ){ addChild( "${ device.name } Thursday" ) } if( getChildDevice( "${ device.name } Friday" ) == null ){ addChild( "${ device.name } Friday" ) } if( getChildDevice( "${ device.name } Saturday" ) == null ){ addChild( "${ device.name } Saturday" ) } break default: break } } Logging( "Updated", 2 ) } // def UK_API_Key_Submission( String Key ){ if( Key != null ){ Logging( "Setting UKAPIKey to = ${ Key }", 4 ) ProcessState( "UKAPIKey", Key ) Logging( "UKAPIKey was set to = ${ state.UKAPIKey }", 4 ) } else { Logging( "No key provided for UK API Key Submission", 5 ) } } // refresh performs a poll of data def refresh(){ Logging( "Refreshing weather information...", 3 ) // Backup required values if they exist def TempCWA def TempGridX def TempGridY if( Region == "US" && state.CWA != null && state.GridX != null && state.GridY != null ){ TempCWA = state.CWA TempGridX = state.GridX TempGridY = state.GridY } // Clear current state values state.clear() // if( Region == "US" ){ state.CWA = TempCWA state.GridX = TempGridX state.GridY = TempGridY } // Poll the weather service PollNWS() } // installed is called when the device is installed, all it really does is run updated def installed(){ Logging( "Installed", 2 ) updated() } // initialize is called when the device is initialized, all it really does is run updated def initialize(){ Logging( "Initialized", 2 ) updated() } // parse def parse( String description ){ Logging( "Something came in on parse... ${ description }", 3 ) } //Poll NWS for data, method depends on regional service being used def PollNWS() { if( Region == "US" ){ if( state.CWA != null && state.GridX != null && state.GridY != null ){ Logging( "Checking for forecast", 2 ) def Params = [ uri: "https://api.weather.gov/gridpoints/${ state.CWA }/${ state.GridX },${ state.GridY }/forecast", contentType: "application/json", requestContentType: "application/json" ] asynchttpGet( "GetUSForecast", Params ) } else { Logging( "Getting specific location for US NWS", 2 ) def Params = [ uri: "https://api.weather.gov/points/${ location.latitude },${ location.longitude }", contentType: "application/json", requestContentType: "application/json" ] asynchttpGet( "GetUSGrid", Params ) } } else if( Region == "UK" ){ //if( state.UKAPIKey != null ){ TempLat = location.latitude TempLong = location.longitude // Uxbridge UK //TempLat = "51.5558492" //TempLong = "-0.4824408" // Syndey AU - Prospect Nature Reserve //TempLat = -33.8387391 //TempLong = 150.8904717 //TempAPIKEY = state.UKAPIKey TempAPIKEY = "ENTER_UK_API_KEY_HERE" Headers = [ 'Apikey': "${ TempAPIKEY }", 'accept': "application/json" ] def Params = [ uri: "https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/daily?latitude=${ TempLat }&longitude=${ TempLong }", headers: Headers, contentType: "application/json", requestContentType: "application/json" ] asynchttpGet( "GetUKForecast", Params ) //} else { //Logging( "UK API Key Submission required for UK method to work properly.", 5 ) //} } else if( Region == "Legacy UK" ){ TempLat = location.latitude TempLong = location.longitude // Uxbridge UK //TempLat = "51.5558492" //TempLong = "-0.4824408" // Syndey AU - Prospect Nature Reserve //TempLat = -33.8387391 //TempLong = 150.8904717 Headers = [ 'x-ibm-client-id': "${ ClientID }", 'x-ibm-client-secret': "${ ClientSecret }", 'accept': "application/json" ] def Params = [ uri: "https://api-metoffice.apiconnect.ibmcloud.com/metoffice/production/v0/forecasts/point/daily?latitude=${ TempLat }&longitude=${ TempLong }", headers: Headers, contentType: "application/json", requestContentType: "application/json" ] asynchttpGet( "GetUKForecast", Params ) } else if( Region == "OW" ){ // https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&appid={YOUR API KEY} def Params if( location.temperatureScale == "F" ){ Params = [ uri: "https://api.openweathermap.org/data/2.5/onecall?lat=${ location.latitude }&lon=${ location.longitude }&appid=${ APIKey }&units=imperial" ] } else { Params = [ uri: "https://api.openweathermap.org/data/2.5/onecall?lat=${ location.latitude }&lon=${ location.longitude }&appid=${ APIKey }&units=metric" ] } asynchttpGet( "GetOWForecast", Params ) } else if( Region == "WA" ){ // WeatherAPI // http://api.weatherapi.com/v1/forecast.json?key=&q=${ location.latitude },${ location.longitude }&days=2 def Params Params = [ uri: "http://api.weatherapi.com/v1/forecast.json?key=${ APIKey }&q=${ location.latitude },${ location.longitude }&days=2" ] asynchttpGet( "GetWAForecast", Params ) } else if( Region == "WU" ){ // Weather Underground API // https://api.weather.com/v2/pws/observations/current?stationId=KMAHANOV10&format=json&units=e&apiKey=yourApiKey def Params if( location.temperatureScale == "F" ){ Params = [ uri: "https://api.weather.com/v2/pws/observations/current?stationId=${ StationID }&format=json&units=e&apiKey=${ APIKey }" ] } else { Params = [ uri: "https://api.weather.com/v2/pws/observations/current?stationId=${ StationID }&format=json&units=m&apiKey=${ APIKey }" ] } asynchttpGet( "GetWUCurrent", Params ) // https://api.weather.com/v3/wx/forecast/daily/5day?geocode=33.74,-84.39&format=json&units=e&language=en-US&apiKey=yourApiKey if( location.temperatureScale == "F" ){ Params = [ uri: "https://api.weather.com/v3/wx/forecast/daily/5day?geocode=${ location.latitude },${ location.longitude }&format=json&units=e&language=en-US&apiKey=${ APIKey }" ] } else { Params = [ uri: "https://api.weather.com/v3/wx/forecast/daily/5day?geocode=${ location.latitude },${ location.longitude }&format=json&units=m&language=en-US&apiKey=${ APIKey }" ] } asynchttpGet( "GetWUForecast", Params ) } else if( Region == "WWO" ){ // World Weather Online Premium API // http://api.worldweatheronline.com/premium/v1/weather.ashx?key=xxxxxxxxxxxxx&q=${ location.latitude },${ location.longitude }&num_of_days=2&tp=24&format=json def Params Params = [ uri: "http://api.worldweatheronline.com/premium/v1/weather.ashx?key=${ APIKey }&q=${ location.latitude },${ location.longitude }&num_of_days=2&tp=24&format=json" ] asynchttpGet( "GetWWOForecast", Params ) } } // Handles the response from NWS for Grid coordinates def GetUSGrid( resp, data ) { if( resp.getStatus() == 200 ) { Json = parseJson( resp.data ) //Logging( "GetGrid attempt ${ Json }", 3 ) if( Json.properties.cwa != null ){ state.CWA = Json.properties.cwa // US-NWS Office Logging( "state.CWA ${ state.CWA }", 3 ) } if( Json.properties.gridX != null ){ state.GridX = Json.properties.gridX // US-NWS Grid X Value Logging( "state.GridX ${ state.GridX }", 3 ) } if( Json.properties.gridY != null ){ state.GridY = Json.properties.gridY // US-NWS Grid Y Value Logging( "state.GridY ${ state.GridY }", 3 ) } } else { Logging( "Error connecting to US NWS: ${ resp.status }", 4 ) } } // Get the US forecast returned, output events and populate attributes as needed def GetUSForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ // Updated if( Json.properties.updated != null ){ ProcessEvent( "Updated", Json.properties.updated ) //state.Updated = Json.properties.updated // Date/Time of update } // Units if( Json.properties.units != null ){ state.Units = Json.properties.units // Units that information is in } // Today's Forecast // Today's temperature if( Json.properties.periods[ 0 ].temperature != null ){ ProcessEvent( "Today_Temperature", Json.properties.periods[ 0 ].temperature, Json.properties.periods[ 0 ].temperatureUnit ) ProcessEvent( "temperature", Json.properties.periods[ 0 ].temperature, Json.properties.periods[ 0 ].temperatureUnit ) } // Today's wind speed if( Json.properties.periods[ 0 ].windSpeed != null ){ def Split = Json.properties.periods[ 0 ].windSpeed.split() if( Split.size() > 2 ){ ProcessEvent( "Today_WindSpeed", Split[ 2 ] as int, "mph" ) ProcessEvent( "windSpeed", Split[ 2 ] as int, "mph" ) } else { ProcessEvent( "Today_WindSpeed", Split[ 0 ] as int, "mph" ) ProcessEvent( "windSpeed", Split[ 0 ] as int, "mph" ) } } // Today's wind direction if( Json.properties.periods[ 0 ].windDirection != null ){ ProcessEvent( "Today_WindDirection", USWindDirection( Json.properties.periods[ 0 ].windDirection ), "°" ) ProcessEvent( "Today_WindDirectionString", Json.properties.periods[ 0 ].windDirection ) ProcessEvent( "windDirection", USWindDirection( Json.properties.periods[ 0 ].windDirection ), "°" ) } // Today's short forecast if( Json.properties.periods[ 0 ].shortForecast != null ){ ProcessEvent( "Today_ShortForecast", Json.properties.periods[ 0 ].shortForecast ) } // Today's detailed forecast if( Json.properties.periods[ 0 ].detailedForecast != null ){ ProcessEvent( "Today_DetailedForecast", Json.properties.periods[ 0 ].detailedForecast ) ProcessUSDetailedForecast( 1, state.Today_DetailedForecast ) } // Today's icon if( Json.properties.periods[ 0 ].icon != null ){ ProcessEvent( "Today_IconURL", Json.properties.periods[ 0 ].icon ) } // Tonight's Forecast // Tonight's temperature if( Json.properties.periods[ 1 ].temperature != null ){ ProcessEvent( "Tonight_Temperature", Json.properties.periods[ 1 ].temperature, Json.properties.periods[ 1 ].temperatureUnit ) } // Tonight's wind speed if( Json.properties.periods[ 1 ].windSpeed != null ){ def Split = Json.properties.periods[ 1 ].windSpeed.split() if( Split.size() > 2 ){ ProcessEvent( "Tonight_WindSpeed", Split[ 2 ] as int, "mph" ) } else { ProcessEvent( "Tonight_WindSpeed", Split[ 0 ] as int, "mph" ) } } // Tonight's wind direction if( Json.properties.periods[ 1 ].windDirection != null ){ ProcessEvent( "Tonight_WindDirection", USWindDirection( Json.properties.periods[ 1 ].windDirection ), "°" ) ProcessEvent( "Tonight_WindDirectionString", Json.properties.periods[ 1 ].windDirection ) } // Tonight's short forecast if( Json.properties.periods[ 1 ].shortForecast != null ){ ProcessEvent( "Tonight_ShortForecast", Json.properties.periods[ 1 ].shortForecast ) } // Tonight's detailed forecast if( Json.properties.periods[ 1 ].detailedForecast != null ){ ProcessEvent( "Tonight_DetailedForecast", Json.properties.periods[ 1 ].detailedForecast ) ProcessUSDetailedForecast( 2, state.Tonight_DetailedForecast ) } // Tonight's icon if( Json.properties.periods[ 1 ].icon != null ){ ProcessEvent( "Tonight_IconURL", Json.properties.periods[ 1 ].icon ) } // Tomorrow's Forecast // Tomorrow's temperature if( Json.properties.periods[ 2 ].temperature != null ){ ProcessEvent( "Tomorrow_Temperature", Json.properties.periods[ 2 ].temperature, Json.properties.periods[ 2 ].temperatureUnit ) } // Tomorrow's wind speed if( Json.properties.periods[ 2 ].windSpeed != null ){ def Split = Json.properties.periods[ 2 ].windSpeed.split() if( Split.size() > 2 ){ ProcessEvent( "Tomorrow_WindSpeed", Split[ 2 ] as int, "mph" ) } else { ProcessEvent( "Tomorrow_WindSpeed", Split[ 0 ] as int, "mph" ) } } // Tomorrow's wind direction if( Json.properties.periods[ 2 ].windDirection != null ){ ProcessEvent( "Tomorrow_WindDirection", USWindDirection( Json.properties.periods[ 2 ].windDirection ), "°" ) ProcessEvent( "Tomorrow_WindDirectionString", Json.properties.periods[ 2 ].windDirection ) } // Tomorrow's short forecast if( Json.properties.periods[ 2 ].shortForecast != null ){ ProcessEvent( "Tomorrow_ShortForecast", Json.properties.periods[ 2 ].shortForecast ) } // Tomorrow's detailed forecast if( Json.properties.periods[ 2 ].detailedForecast != null ){ ProcessEvent( "Tomorrow_DetailedForecast", Json.properties.periods[ 2 ].detailedForecast ) ProcessUSDetailedForecast( 3, state.Tomorrow_DetailedForecast ) } // Tomorrow's icon if( Json.properties.periods[ 2 ].icon != null ){ ProcessEvent( "Tomorrow_IconURL", Json.properties.periods[ 2 ].icon ) } // Tomorrow Night's Forecast // Tomorrow night's temperature if( Json.properties.periods[ 3 ].temperature != null ){ ProcessEvent( "Tomorrownight_Temperature", Json.properties.periods[ 3 ].temperature, Json.properties.periods[ 3 ].temperatureUnit ) } // Tomorrow night's wind speed if( Json.properties.periods[ 3 ].windSpeed != null ){ def Split = Json.properties.periods[ 3 ].windSpeed.split() if( Split.size() > 2 ){ ProcessEvent( "Tomorrownight_WindSpeed", Split[ 2 ] as int, "mph" ) } else { ProcessEvent( "Tomorrownight_WindSpeed", Split[ 0 ] as int, "mph" ) } } // Tomorrow night's wind direction if( Json.properties.periods[ 3 ].windDirection != null ){ ProcessEvent( "Tomorrownight_WindDirection", USWindDirection( Json.properties.periods[ 3 ].windDirection ), "°" ) ProcessEvent( "Tomorrownight_WindDirectionString", Json.properties.periods[ 3 ].windDirection ) } // Tomorrow night's short forecast if( Json.properties.periods[ 3 ].shortForecast != null ){ ProcessEvent( "Tomorrownight_ShortForecast", Json.properties.periods[ 3 ].shortForecast ) } // Tomorrow night's detailed forecast if( Json.properties.periods[ 3 ].detailedForecast != null ){ ProcessEvent( "Tomorrownight_DetailedForecast", Json.properties.periods[ 3 ].detailedForecast ) ProcessUSDetailedForecast( 4, state.Tomorrownight_DetailedForecast ) } // Tomorrow night's icon if( Json.properties.periods[ 3 ].icon != null ){ ProcessEvent( "Tomorrownight_IconURL", Json.properties.periods[ 3 ].icon ) } // Section for the forecast child devices if( ChildrenEnabled ){ ClearForecastChildren() Json.properties.periods.each(){ switch( it.name ){ case "Sunday": case "Monday": case "Tuesday": case "Wednesday": case "Thursday": case "Friday": case "Saturday": if( it.temperature != null ){ PostEventToChild( "${ device.name } ${ it.name }", "temperature", it.temperature, "${ location.temperatureScale }" ) } if( it.windDirection != null ){ PostEventToChild( "${ device.name } ${ it.name }", "WindDirectionString", "${ it.windDirection }" ) } if( it.startTime != null ){ PostEventToChild( "${ device.name } ${ it.name }", "Forecast Valid as of", "${ it.startTime }" ) } if( it.detailedForecast != null ){ PostEventToChild( "${ device.name } ${ it.name }", "Conditions", "${ it.detailedForecast }" ) } break } } } Logging( "US NWS data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else { Logging( "Error connecting to US NWS: ${ resp.status }", 4 ) return } } // Get the forecast returned, output events and populate attributes as needed def GetUKForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ if( Json.features[ 0 ].properties.modelRunDate != null ){ ProcessEvent( "Updated", Json.features[ 0 ].properties.modelRunDate ) } // Today's Forecast // Today's temperature if( Json.features[ 0 ].properties.timeSeries[ 0 ].dayMaxScreenTemperature != null ){ ProcessEvent( "Today_Temperature", Json.features[ 0 ].properties.timeSeries[ 0 ].dayMaxScreenTemperature, "C" ) ProcessEvent( "temperature", Json.features[ 0 ].properties.timeSeries[ 0 ].dayMaxScreenTemperature, "C" ) } // Today's min temperature if( Json.features[ 0 ].properties.timeSeries[ 0 ].nightLowerBoundMinTemp != null ){ ProcessEvent( "Today_MinTemp", Json.features[ 0 ].properties.timeSeries[ 0 ].nightLowerBoundMinTemp, "C" ) } // Today's max temperature if( Json.features[ 0 ].properties.timeSeries[ 0 ].dayUpperBoundMaxTemp != null ){ ProcessEvent( "Today_MaxTemp", Json.features[ 0 ].properties.timeSeries[ 0 ].dayUpperBoundMaxTemp, "C" ) } // Today's wind speed if( Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindSpeed != null ){ ProcessEvent( "Today_WindSpeed", Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindSpeed, "knots" ) ProcessEvent( "windSpeed", Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindSpeed, "knots" ) } // Today's wind direction if( Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindDirection != null ){ ProcessEvent( "Today_WindDirection", Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindDirection, "°" ) ProcessEvent( "windDirection", Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindDirection, "°" ) } // Today's wind gust if( Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindGust != null ){ ProcessEvent( "Today_WindGust", Json.features[ 0 ].properties.timeSeries[ 0 ].midday10MWindGust, "knots" ) } // Today's chance of rain if( Json.features[ 0 ].properties.timeSeries[ 0 ].dayProbabilityOfRain != null ){ ProcessEvent( "Today_ChanceRain", Json.features[ 0 ].properties.timeSeries[ 0 ].dayProbabilityOfRain, "%" ) } // Today's UVI if( Json.features[ 0 ].properties.timeSeries[ 0 ].maxUvIndex != null ){ ProcessEvent( "Today_UVI", Json.features[ 0 ].properties.timeSeries[ 0 ].maxUvIndex, "" ) } // Today's short forecast if( Json.features[ 0 ].properties.timeSeries[ 0 ].daySignificantWeatherCode != null ){ ProcessEvent( "Today_ShortForecast", UKSignificantWeather( Json.features[ 0 ].properties.timeSeries[ 0 ].daySignificantWeatherCode ) ) } // Tonight's Forecast // Tonight's temperature if( Json.features[ 0 ].properties.timeSeries[ 0 ].nightMinScreenTemperature != null ){ ProcessEvent( "Tonight_Temperature", Json.features[ 0 ].properties.timeSeries[ 0 ].nightMinScreenTemperature, "C" ) } // Tonight's wind speed if( Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindSpeed != null ){ ProcessEvent( "Tonight_WindSpeed", Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindSpeed, "knots" ) } // Tonight's wind direction if( Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindDirection != null ){ ProcessEvent( "Tonight_WindDirection", Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindDirection, "°" ) } // Tonight's wind gust if( Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindGust != null ){ ProcessEvent( "Tonight_WindGust", Json.features[ 0 ].properties.timeSeries[ 0 ].midnight10MWindGust, "knots" ) } // Tonight's chance of rain if( Json.features[ 0 ].properties.timeSeries[ 0 ].nightProbabilityOfRain != null ){ ProcessEvent( "Tonight_ChanceRain", Json.features[ 0 ].properties.timeSeries[ 0 ].nightProbabilityOfRain, "%" ) } // Tonight's short forecast if( Json.features[ 0 ].properties.timeSeries[ 0 ].nightSignificantWeatherCode != null ){ ProcessEvent( "Tonight_ShortForecast", UKSignificantWeather( Json.features[ 0 ].properties.timeSeries[ 0 ].nightSignificantWeatherCode ) ) } // Tomorrow's Forecast // Tomorrow's temperature if( Json.features[ 0 ].properties.timeSeries[ 1 ].dayMaxScreenTemperature != null ){ ProcessEvent( "Tomorrow_Temperature", Json.features[ 0 ].properties.timeSeries[ 1 ].dayMaxScreenTemperature, "C" ) } // Today's min temperature if( Json.features[ 0 ].properties.timeSeries[ 1 ].nightLowerBoundMinTemp != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.features[ 0 ].properties.timeSeries[ 1 ].nightLowerBoundMinTemp, "C" ) } // Today's max temperature if( Json.features[ 0 ].properties.timeSeries[ 1 ].dayUpperBoundMaxTemp != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.features[ 0 ].properties.timeSeries[ 1 ].dayUpperBoundMaxTemp, "C" ) } // Tomorrow's wind speed if( Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindSpeed != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindSpeed, "knots" ) } // Tomorrow's wind direction if( Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindDirection != null ){ ProcessEvent( "Tomorrow_WindDirection", Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindDirection, "°" ) } // Tomorrow's wind gust if( Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindGust != null ){ ProcessEvent( "Tomorrow_WindGust", Json.features[ 0 ].properties.timeSeries[ 1 ].midday10MWindGust, "knots" ) } // Tomorrow's chance of rain if( Json.features[ 0 ].properties.timeSeries[ 1 ].dayProbabilityOfRain != null ){ ProcessEvent( "Tomorrow_ChanceRain", Json.features[ 0 ].properties.timeSeries[ 1 ].dayProbabilityOfRain, "%" ) } // Tomorrow's UVI if( Json.features[ 0 ].properties.timeSeries[ 1 ].maxUvIndex != null ){ ProcessEvent( "Tomorrow_UVI", Json.features[ 0 ].properties.timeSeries[ 1 ].maxUvIndex, "" ) } // Tomorrow's short forecast if( Json.features[ 0 ].properties.timeSeries[ 1 ].daySignificantWeatherCode != null ){ ProcessEvent( "Tomorrow_ShortForecast", UKSignificantWeather( Json.features[ 0 ].properties.timeSeries[ 1 ].daySignificantWeatherCode ) ) } // Tomorrow night's Forecast // Tomorrow night's temperature if( Json.features[ 0 ].properties.timeSeries[ 1 ].nightMinScreenTemperature != null ){ ProcessEvent( "Tomorrownight_Temperature", Json.features[ 0 ].properties.timeSeries[ 1 ].nightMinScreenTemperature, "C" ) } // Tomorrow night's wind speed if( Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindSpeed != null ){ ProcessEvent( "Tomorrownight_WindSpeed", Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindSpeed, "knots" ) } // Tomorrow night's wind direction if( Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindDirection != null ){ ProcessEvent( "Tomorrownight_WindDirection", Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindDirection, "°" ) } // Tomorrow night's wind gust if( Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindGust != null ){ ProcessEvent( "Tomorrownight_WindGust", Json.features[ 0 ].properties.timeSeries[ 1 ].midnight10MWindGust, "knots" ) } // Tomorrow night's chance of rain if( Json.features[ 0 ].properties.timeSeries[ 1 ].nightProbabilityOfRain != null ){ ProcessEvent( "Tomorrownight_ChanceRain", Json.features[ 0 ].properties.timeSeries[ 1 ].nightProbabilityOfRain, "%" ) } // Tomorrow night's short forecast if( Json.features[ 0 ].properties.timeSeries[ 1 ].nightSignificantWeatherCode != null ){ ProcessEvent( "Tomorrownight_ShortForecast", UKSignificantWeather( Json.features[ 0 ].properties.timeSeries[ 1 ].nightSignificantWeatherCode ) ) } Logging( "UK/Australia Met Office data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid API credentials provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to UK Met Office: ${ resp.status }", 4 ) } } // Get the forecast returned, output events and populate attributes as needed from OpenWeather def GetOWForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ // Updated date if( Json.current.dt != null ){ ProcessEvent( "Updated", ConvertEpochToDate( Json.current.dt ) ) } // Current temperature if( Json.current.temp != null ){ ProcessEvent( "temperature", Json.current.temp, location.temperatureScale ) } // Current feelsLike if( Json.current.feels_like != null ){ ProcessEvent( "feelsLike", Json.current.feels_like, location.temperatureScale ) } // Current pressure if( Json.current.pressure != null ){ ProcessEvent( "pressure", Json.current.pressure, "hPa" ) } // Current humidity if( Json.current.humidity != null ){ ProcessEvent( "humidity", Json.current.humidity, "%" ) } // Current dewPoint if( Json.current.dew_point != null ){ ProcessEvent( "dewPoint", Json.current.dew_point, location.temperatureScale ) } // Current uvi if( Json.current.uvi != null ){ ProcessEvent( "uv", Json.current.uvi ) } // Current hourlyRain if( Json.current.rain != null ){ if( Json.current.rain.'1h' != null ){ ProcessEvent( "hourlyRain", Json.current.rain.'1h', "mm" ) } } // Current Conditions if( Json.current.weather.description != null ){ ProcessEvent( "Conditions", Json.current.weather.description ) } // Current wind speed if( Json.current.wind_speed != null ){ ProcessEvent( "windSpeed", Json.current.wind_speed, "" ) } // Current wind gust if( Json.current.wind_gust != null ){ ProcessEvent( "windGust", Json.current.wind_gust, "" ) } // Current wind direction if( Json.current.wind_deg != null ){ ProcessEvent( "winddir", Json.current.wind_deg, "°" ) } // Today's temperature if( Json.daily[ 0 ].temp.day != null ){ ProcessEvent( "Today_Temperature", Json.daily[ 0 ].temp.day, location.temperatureScale ) } // Today's min temperature if( Json.daily[ 0 ].temp.min != null ){ ProcessEvent( "Today_MinTemp", Json.daily[ 0 ].temp.min, location.temperatureScale ) } // Today's max temperature if( Json.daily[ 0 ].temp.max != null ){ ProcessEvent( "Today_MaxTemp", Json.daily[ 0 ].temp.max, location.temperatureScale ) } // Tonight's temperature if( Json.daily[ 0 ].temp.night != null ){ ProcessEvent( "Tonight_Temperature", Json.daily[ 0 ].temp.night, location.temperatureScale ) } // Today's "feels like" temperature if( Json.daily[ 0 ].feels_like.day != null ){ ProcessEvent( "Today_FeelsLike", Json.daily[ 0 ].feels_like.day, location.temperatureScale ) } // Tonight's "feels like" temperature if( Json.daily[ 0 ].feels_like.night != null ){ ProcessEvent( "Tonight_FeelsLike", Json.daily[ 0 ].feels_like.night, location.temperatureScale ) } // Today's humidity if( Json.daily[ 0 ].humidity != null ){ ProcessEvent( "Today_Humidity", Json.daily[ 1 ].humidity, "%" ) ProcessEvent( "humidity", Json.daily[ 1 ].humidity, "%" ) } // Today's air pressure if( Json.daily[ 0 ].pressure != null ){ ProcessEvent( "Today_Pressure", Json.daily[ 0 ].pressure, "" ) } // Today's wind speed if( Json.daily[ 0 ].wind_speed != null ){ ProcessEvent( "Today_WindSpeed", Json.daily[ 0 ].wind_speed, "" ) } // Today's wind direction if( Json.daily[ 0 ].wind_deg != null ){ ProcessEvent( "Today_WindDirection", Json.daily[ 0 ].wind_deg, "°" ) } // Today's chance of rain if( Json.daily[ 0 ].rain != null ){ ProcessEvent( "Today_ChanceRain", Json.daily[ 0 ].rain, "%" ) } else { ProcessEvent( "Today_ChanceRain", 0, "%" ) } // Today's chance of clouds if( Json.daily[ 0 ].clouds != null ){ ProcessEvent( "Today_ChanceClouds", Json.daily[ 0 ].clouds, "%" ) } else { // OpenWeather appears not to list the variable if the chance is 0 (at least fo rain, so I am making sure here) ProcessEvent( "Today_ChanceClouds", 0, "%" ) } // Today's chance of snow if( Json.daily[ 0 ].snow != null ){ ProcessEvent( "Today_ChanceSnow", Json.daily[ 0 ].snow, "%" ) } else { ProcessEvent( "Today_ChanceSnow", 0, "%" ) } // Today's UVI if( Json.daily[ 0 ].uvi != null ){ ProcessEvent( "Today_UVI", Json.daily[ 0 ].uvi ) } // Today's sunrise if( Json.daily[ 0 ].sunrise != null ){ ProcessEvent( "Today_SunriseEpoch", Json.daily[ 0 ].sunrise ) ProcessEvent( "Today_Sunrise", ConvertEpochToDate( Json.daily[ 0 ].sunrise ) ) } // Today's sunset if( Json.daily[ 0 ].sunset != null ){ ProcessEvent( "Today_SunsetEpoch", Json.daily[ 0 ].sunset ) ProcessEvent( "Today_Sunset", ConvertEpochToDate( Json.daily[ 0 ].sunset ) ) } // Today's icon if( Json.daily[ 0 ].weather[ 0 ].icon != null ){ ProcessEvent( "Today_IconURL", "http://openweathermap.org/img/wn/${ Json.daily[ 0 ].weather[ 0 ].icon }@2x.png" ) } // Tomorrow's Forecast // Tomorrow's temperature if( Json.daily[ 1 ].temp.day != null ){ ProcessEvent( "Tomorrow_Temperature", Json.daily[ 1 ].temp.day, location.temperatureScale ) } // Tomorrow's min temperature if( Json.daily[ 1 ].temp.min != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.daily[ 1 ].temp.min, location.temperatureScale ) } // Today's max temperature if( Json.daily[ 1 ].temp.max != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.daily[ 1 ].temp.max, location.temperatureScale ) } // Tomorrow night's temperature if( Json.daily[ 1 ].temp.night != null ){ ProcessEvent( "Tomorrownight_Temperature", Json.daily[ 1 ].temp.night, location.temperatureScale ) } // Tomorrow's "feels like" temperature if( Json.daily[ 1 ].feels_like.day != null ){ ProcessEvent( "Tomorrow_FeelsLike", Json.daily[ 1 ].feels_like.day, location.temperatureScale ) } // Tomorrow night's "feels like" temperature if( Json.daily[ 1 ].feels_like.night != null ){ ProcessEvent( "Tomorrownight_FeelsLike", Json.daily[ 1 ].feels_like.night, location.temperatureScale ) } // Tomorrow's humidity if( Json.daily[ 1 ].humidity != null ){ ProcessEvent( "Tomorrow_Humidity", Json.daily[ 1 ].humidity, "%" ) } // Tomorrow's air pressure if( Json.daily[ 1 ].pressure != null ){ ProcessEvent( "Tomorrow_Pressure", Json.daily[ 1 ].pressure, "" ) } // Tomorrow's wind speed if( Json.daily[ 1 ].wind_speed != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.daily[ 1 ].wind_speed, "" ) } // Tomorrow's wind direction if( Json.daily[ 1 ].wind_deg != null ){ ProcessEvent( "Tomorrow_WindDirection", Json.daily[ 1 ].wind_deg, "" ) } // Tomorrow's chance of rain if( Json.daily[ 1 ].rain != null ){ ProcessEvent( "Tomorrow_ChanceRain", Json.daily[ 1 ].rain, "%" ) } else { ProcessEvent( "Tomorrow_ChanceRain", 0, "%" ) } // Tomorrow's chance of clouds if( Json.daily[ 1 ].clouds != null ){ ProcessEvent( "Tomorrow_ChanceClouds", Json.daily[ 1 ].clouds, "%" ) } else { ProcessEvent( "Tomorrow_ChanceClouds", 0, "%" ) } // Tomorrow's chance of snow if( Json.daily[ 1 ].snow != null ){ ProcessEvent( "Tomorrow_ChanceSnow", Json.daily[ 1 ].snow, "%" ) } else { ProcessEvent( "Tomorrow_ChanceSnow", 0, "%" ) } // Tomorrow's UVI if( Json.daily[ 1 ].uvi != null ){ ProcessEvent( "Tomorrow_UVI", Json.daily[ 1 ].uvi ) } // Tomorrow's sunrise if( Json.daily[ 1 ].sunrise != null ){ ProcessEvent( "Tomorrow_SunriseEpoch", Json.daily[ 1 ].sunrise ) ProcessEvent( "Tomorrow_Sunrise", ConvertEpochToDate( Json.daily[ 1 ].sunrise ) ) } // Tomorrow's sunset if( Json.daily[ 1 ].sunset != null ){ ProcessEvent( "Tomorrow_SunsetEpoch", Json.daily[ 1 ].sunset ) ProcessEvent( "Tomorrow_Sunset", ConvertEpochToDate( Json.daily[ 1 ].sunset ) ) } // Tomorrow's icon if( Json.daily[ 1 ].weather[ 0 ].icon != null ){ ProcessEvent( "Tomorrow_IconURL", "http://openweathermap.org/img/wn/${ Json.daily[ 1 ].weather[ 0 ].icon }@2x.png" ) } // Section for the forecast child devices if( ChildrenEnabled ){ ClearForecastChildren() for( i = 0; i <= 5; i++ ){ def TempDate = ConvertEpochToDate( Json.daily[ i ].dt ) def day = TempDate[ Calendar.DAY_OF_WEEK ] Day = ReturnDayName( day ) if( Json.daily[ i ].temp.min != null ){ PostEventToChild( "${ device.name } ${ Day }", "Min Temperature", Json.daily[ i ].temp.min, "${ location.temperatureScale }" ) } if( Json.daily[ i ].temp.max != null ){ PostEventToChild( "${ device.name } ${ Day }", "Max Temperature", Json.daily[ i ].temp.max, "${ location.temperatureScale }" ) } if( Json.daily[ i ].feels_like.day != null ){ PostEventToChild( "${ device.name } ${ Day }", "feelsLike", Json.daily[ i ].feels_like.day, "${ location.temperatureScale }" ) } if( Json.daily[ i ].pop != null ){ PostEventToChild( "${ device.name } ${ Day }", "Chance Precipitation", Json.daily[ i ].pop, "%" ) } if( Json.daily[ i ].pressure != null ){ PostEventToChild( "${ device.name } ${ Day }", "pressure", Json.daily[ i ].pressure ) } if( Json.daily[ i ].humidity != null ){ PostEventToChild( "${ device.name } ${ Day }", "humidity", Json.daily[ i ].humidity ) } if( Json.daily[ i ].dew_point != null ){ PostEventToChild( "${ device.name } ${ Day }", "dewPoint", Json.daily[ i ].dew_point ) } if( Json.daily[ i ].wind_speed != null ){ PostEventToChild( "${ device.name } ${ Day }", "windSpeed", Json.daily[ i ].wind_speed ) } if( Json.daily[ i ].wind_gust != null ){ PostEventToChild( "${ device.name } ${ Day }", "windGust", Json.daily[ i ].wind_gust ) } if( Json.daily[ i ].wind_deg != null ){ PostEventToChild( "${ device.name } ${ Day }", "windDirection", Json.daily[ i ].wind_dir ) } if( Json.daily[ i ].clouds != null ){ PostEventToChild( "${ device.name } ${ Day }", "Cloud Cover", Json.daily[ i ].clouds, "%" ) } if( Json.daily[ i ].uvi != null ){ PostEventToChild( "${ device.name } ${ Day }", "uv", Json.daily[ i ].uvi ) } if( Json.daily[ i ].sunrise != null ){ PostEventToChild( "${ device.name } ${ Day }", "Sunrise", "${ ConvertEpochToDate( Json.daily[ i ].sunrise ) }" ) } if( Json.daily[ i ].sunset != null ){ PostEventToChild( "${ device.name } ${ Day }", "Sunset", "${ ConvertEpochToDate( Json.daily[ i ].sunset ) }" ) } if( Json.daily[ i ].moon_phase != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moon Phase", "${ Json.daily[ i ].moon_phase }" ) } if( Json.daily[ i ].moonrise != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moonrise", "${ ConvertEpochToDate( Json.daily[ i ].moonrise ) }" ) } if( Json.daily[ i ].moonset != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moonset", "${ ConvertEpochToDate( Json.daily[ i ].moonset ) }" ) } if( Json.daily[ i ].weather[ 0 ].description != null ){ PostEventToChild( "${ device.name } ${ Day }", "Conditions", "${ Json.daily[ i ].weather[ 0 ].description }" ) } } } Logging( "OpenWeather data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid APIKey provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to OpenWeather: ${ resp.status }", 4 ) } } // Get the WA forecast returned, output events and populate attributes as needed def GetWAForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ // Updated if( Json.current.last_updated != null ){ ProcessEvent( "Updated", Json.current.last_updated ) } // uv if( Json.current.uv != null ){ ProcessEvent( "uv", Json.current.uv ) } // humidity if( Json.current.humidity != null ){ ProcessEvent( "humidity", Json.current.humidity, "%" ) } if( location.temperatureScale == "F" ){ // temperature if( Json.current.temp_f != null ){ ProcessEvent( "temperature", Json.current.temp_f, "F" ) } // feelsLike if( Json.current.feelslike_f != null ){ ProcessEvent( "feelsLike", Json.current.feelslike_f, "F" ) } // pressure if( Json.current.pressure_in != null ){ ProcessEvent( "pressure", Json.current.pressure_in, "in" ) } // wind speed if( Json.current.windspeedMiles != null ){ ProcessEvent( "windSpeed", Json.current.windspeedMiles, "mph" ) } // Today's temperature if( Json.forecast.forecastday[ 0 ].day.avgtemp_f != null ){ ProcessEvent( "Today_Temperature", Json.forecast.forecastday[ 0 ].day.avgtemp_f, "F" ) } // Today's min temperature if( Json.forecast.forecastday[ 0 ].day.mintemp_f != null ){ ProcessEvent( "Today_MinTemp", Json.forecast.forecastday[ 0 ].day.mintemp_f, "F" ) } // Today's max temperature if( Json.forecast.forecastday[ 0 ].day.maxtemp_f != null ){ ProcessEvent( "Today_MaxTemp", Json.forecast.forecastday[ 0 ].day.maxtemp_f, "F" ) } // Today's wind gust if( Json.forecast.forecastday[ 0 ].day.maxwind_mph != null ){ ProcessEvent( "Today_WindGust", Json.forecast.forecastday[ 0 ].day.maxwind_mph, "mph" ) } // Tomorrow's temperature if( Json.forecast.forecastday[ 1 ].day.avgtemp_f != null ){ ProcessEvent( "Tomorrow_Temperature", Json.forecast.forecastday[ 1 ].day.avgtemp_f, "F" ) } // Tomorrow's min temperature if( Json.forecast.forecastday[ 1 ].day.mintemp_f != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.forecast.forecastday[ 1 ].day.mintemp_f, "F" ) } // Tomorrow's max temperature if( Json.forecast.forecastday[ 1 ].day.maxtemp_f != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.forecast.forecastday[ 1 ].day.maxtemp_f, "F" ) } // Tomorrow's wind gust if( Json.forecast.forecastday[ 1 ].day.maxwind_mph != null ){ ProcessEvent( "Tomorrow_WindGust", Json.forecast.forecastday[ 1 ].day.maxwind_mph, "mph" ) } } else { // temperature if( Json.current.temp_c != null ){ ProcessEvent( "temperature", Json.current.temp_c, "C" ) } // feelsLike if( Json.current.feelslike_c != null ){ ProcessEvent( "feelsLike", Json.current.feelslike_c, "C" ) } // pressure if( Json.current.pressure_mb != null ){ ProcessEvent( "pressure", Json.current.pressure_mb, "mb" ) } // wind speed if( Json.current.windspeedKmph != null ){ ProcessEvent( "windSpeed", Json.current.windspeedKmph, "kmph" ) } // Today's temperature if( Json.forecast.forecastday[ 0 ].day.avgtemp_f != null ){ ProcessEvent( "Today_Temperature", Json.forecast.forecastday[ 0 ].day.avgtemp_c, "C" ) } // Today's min temperature if( Json.forecast.forecastday[ 0 ].day.mintemp_c != null ){ ProcessEvent( "Today_MinTemp", Json.forecast.forecastday[ 0 ].day.mintemp_c, "C" ) } // Today's max temperature if( Json.forecast.forecastday[ 0 ].day.maxtemp_c != null ){ ProcessEvent( "Today_MaxTemp", Json.forecast.forecastday[ 0 ].day.maxtemp_c, "C" ) } // Today's wind gust if( Json.forecast.forecastday[ 0 ].day.maxwind_kph != null ){ ProcessEvent( "Today_WindGust", Json.forecast.forecastday[ 0 ].day.maxwind_kph, "kph" ) } // Tomorrow's temperature if( Json.forecast.forecastday[ 1 ].day.avgtemp_c != null ){ ProcessEvent( "Tomorrow_Temperature", Json.forecast.forecastday[ 1 ].day.avgtemp_c, "C" ) } // Tomorrow's min temperature if( Json.forecast.forecastday[ 1 ].day.mintemp_c != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.forecast.forecastday[ 1 ].day.mintemp_c, "C" ) } // Tomorrow's max temperature if( Json.forecast.forecastday[ 1 ].day.maxtemp_c != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.forecast.forecastday[ 1 ].day.maxtemp_c, "C" ) } // Tomorrow's wind gust if( Json.forecast.forecastday[ 1 ].day.maxwind_kph != null ){ ProcessEvent( "Tomorrow_WindGust", Json.forecast.forecastday[ 1 ].day.maxwind_kph, "kph" ) } } // Today's short forecast if( Json.forecast.forecastday[ 0 ].day.condition.text != null ){ ProcessEvent( "Today_ShortForecast", Json.forecast.forecastday[ 0 ].day.condition.text ) } // Today's sunrise if( Json.forecast.forecastday[ 0 ].astro.sunrise != null ){ ProcessEvent( "Today_Sunrise", "${ Json.forecast.forecastday[ 0 ].date }T${ Json.forecast.forecastday[ 0 ].astro.sunrise }" ) } // Today's sunset if( Json.forecast.forecastday[ 0 ].astro.sunset != null ){ ProcessEvent( "Today_Sunset", "${ Json.forecast.forecastday[ 0 ].date }T${ Json.forecast.forecastday[ 0 ].astro.sunset }" ) } // Today's weather icon if( Json.forecast.forecastday[ 0 ].day.condition.icon != null ){ def Temp = Json.forecast.forecastday[ 0 ].day.condition.icon.split( "//" ) ProcessEvent( "Today_IconURL", Temp[ 1 ] ) } // Today's humidity if( Json.forecast.forecastday[ 0 ].day.avghumidity != null ){ ProcessEvent( "Today_Humidity", Json.forecast.forecastday[ 0 ].day.avghumidity, "%" ) ProcessEvent( "humidity", Json.forecast.forecastday[ 0 ].day.avghumidity, "%" ) } // Today's UVI if( Json.forecast.forecastday[ 0 ].day.uv != null ){ ProcessEvent( "Today_UVI", Json.forecast.forecastday[ 0 ].day.uv ) } // Tomorrow's short forecast if( Json.forecast.forecastday[ 1 ].day.condition.text != null ){ ProcessEvent( "Tomorrow_ShortForecast", Json.forecast.forecastday[ 1 ].day.condition.text ) } // Tomorrow's sunrise if( Json.forecast.forecastday[ 1 ].astro.sunrise != null ){ ProcessEvent( "Tomorrow_Sunrise", "${ Json.forecast.forecastday[ 1 ].date }T${ Json.forecast.forecastday[ 1 ].astro.sunrise }" ) } // Tomorrow's sunset if( Json.forecast.forecastday[ 1 ].astro.sunset != null ){ ProcessEvent( "Tomorrow_Sunset", "${ Json.forecast.forecastday[ 1 ].date }T${ Json.forecast.forecastday[ 1 ].astro.sunset }" ) } // Tomorrow's weather icon if( Json.forecast.forecastday[ 1 ].day.condition.icon != null ){ def Temp = Json.forecast.forecastday[ 1 ].day.condition.icon.split( "//" ) ProcessEvent( "Tomorrow_IconURL", Temp[ 1 ] ) } // Tomorrow's humidity if( Json.forecast.forecastday[ 1 ].day.avghumidity != null ){ ProcessEvent( "Tomorrow_Humidity", Json.forecast.forecastday[ 1 ].day.avghumidity, "%" ) } // Tomorrow's UVI if( Json.forecast.forecastday[ 1 ].day.uv != null ){ ProcessEvent( "Tomorrow_UVI", Json.forecast.forecastday[ 1 ].day.uv ) } Logging( "WeatherAPI.com data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid APIKey provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to WeatherAPI.com: ${ resp.status }", 4 ) } } // Get the WU Current, output events and populate attributes as needed def GetWUCurrent( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ // Updated if( Json.observations[ 0 ].obsTimeLocal != null ){ ProcessEvent( "Updated", Json.observations[ 0 ].obsTimeLocal ) } if( location.temperatureScale == "F" ){ // Today's temperature if( Json.observations[ 0 ].imperial.temp != null ){ ProcessEvent( "Today_Temperature", Json.observations[ 0 ].imperial.temp, "F" ) ProcessEvent( "temperature", Json.observations[ 0 ].imperial.temp, "F" ) } // Today's wind gust if( Json.observations[ 0 ].imperial.windGust != null ){ ProcessEvent( "Today_WindGust", Json.observations[ 0 ].imperial.windGust, "mph" ) } // Today's wind speed if( Json.observations[ 0 ].imperial.windSpeed != null ){ ProcessEvent( "Today_WindSpeed", Json.observations[ 0 ].imperial.windSpeed, "mph" ) ProcessEvent( "windSpeed", Json.observations[ 0 ].imperial.windSpeed, "mph" ) } // Today's pressure if( Json.observations[ 0 ].imperial.pressure != null ){ ProcessEvent( "Today_Pressure", Json.observations[ 0 ].imperial.pressure ) } // Dew Point if( Json.observations[ 0 ].imperial.dewpt != null ){ ProcessEvent( "dewPoint", Json.observations[ 0 ].imperial.dewpt ) } // Heat Index if( Json.observations[ 0 ].imperial.heatIndex != null ){ ProcessEvent( "feelsLike", Json.observations[ 0 ].imperial.heatIndex ) } // Wind Chill if( Json.observations[ 0 ].imperial.windChill != null ){ ProcessEvent( "WindChill", Json.observations[ 0 ].imperial.windChill ) } } else { // Today's temperature if( Json.observations[ 0 ].metric.temp != null ){ ProcessEvent( "Today_Temperature", Json.observations[ 0 ].metric.temp, "C" ) ProcessEvent( "temperature", Json.observations[ 0 ].metric.temp, "C" ) } // Today's wind gust if( Json.observations[ 0 ].metric.windGust != null ){ ProcessEvent( "Today_WindGust", Json.observations[ 0 ].metric.windGust, "kph" ) } // Today's wind speed if( Json.observations[ 0 ].metric.windSpeed != null ){ ProcessEvent( "Today_WindSpeed", Json.observations[ 0 ].metric.windSpeed, "kph" ) ProcessEvent( "windSpeed", Json.observations[ 0 ].metric.windSpeed, "kph" ) } // Today's pressure if( Json.observations[ 0 ].metric.pressure != null ){ ProcessEvent( "Today_Pressure", Json.observations[ 0 ].metric.pressure ) } // Dew Point if( Json.observations[ 0 ].metric.dewpt != null ){ ProcessEvent( "dewPoint", Json.observations[ 0 ].metric.dewpt ) } // Heat Index if( Json.observations[ 0 ].metric.heatIndex != null ){ ProcessEvent( "feelsLike", Json.observations[ 0 ].metric.heatIndex ) } // Wind Chill if( Json.observations[ 0 ].metric.windChill != null ){ ProcessEvent( "WindChill", Json.observations[ 0 ].metric.windChill ) } } // Neighborhood if( Json.observations[ 0 ].neighborhood != null ){ ProcessEvent( "Neighborhood", Json.observations[ 0 ].neighborhood ) } // Quality Control Indicator if( Json.observations[ 0 ].qcStatus != null ){ ProcessEvent( "Quality Control", Json.observations[ 0 ].qcStatus ) } // PWS Software Type if( Json.observations[ 0 ].softwareType != null ){ ProcessEvent( "PWS Software Type", Json.observations[ 0 ].softwareType ) } // Elevation if( Json.observations[ 0 ].elev != null ){ ProcessEvent( "Elevation", Json.observations[ 0 ].elev ) } // Today's humidity if( Json.observations[ 0 ].humidity != null ){ ProcessEvent( "Today_Humidity", Json.observations[ 0 ].humidity, "%" ) ProcessEvent( "humidity", Json.observations[ 0 ].humidity, "%" ) } // Today's wind direction if( Json.observations[ 0 ].winddir != null ){ ProcessEvent( "Today_WindDirection", Json.observations[ 0 ].winddir, "°" ) ProcessEvent( "windDirection", Json.observations[ 0 ].winddir, "°" ) } // Today's UVI if( Json.observations[ 0 ].uv != null ){ ProcessEvent( "uv", Json.observations[ 0 ].uv ) } // Today's Solar Radiation if( Json.observations[ 0 ].solarRadiation != null ){ ProcessEvent( "solarradiation", Json.observations[ 0 ].solarRadiation ) } Logging( "Weather Underground observation data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid APIKey provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to Weather Underground: ${ resp.status }", 4 ) } } // Get the WU forecast, output events and populate attributes as needed def GetWUForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ if( location.temperatureScale == "F" ){ // Tonight's temperature if( Json.daypart[ 0 ].temperature[ 1 ] != null ){ ProcessEvent( "Tonight_Temperature", Json.daypart[ 0 ].temperature[ 1 ] , "F" ) } // Tomorrow's temperature if( Json.daypart[ 0 ].temperature[ 2 ] != null ){ ProcessEvent( "Tomorrow_Temperature", Json.daypart[ 0 ].temperature[ 2 ] , "F" ) } // Tomorrow night's temperature if( Json.daypart[ 0 ].temperature[ 2 ] != null ){ ProcessEvent( "Tomorrownight_Temperature", Json.daypart[ 0 ].temperature[ 3 ] , "F" ) } // Today's min temperature if( Json.temperatureMin[ 0 ] != null ){ ProcessEvent( "Today_MinTemp", Json.temperatureMin[ 0 ] , "F" ) } // Tomorrow's min temperature if( Json.temperatureMin[ 1 ] != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.temperatureMin[ 1 ] , "F" ) } // Today's max temperature if( Json.temperatureMax[ 0 ] != null ){ ProcessEvent( "Today_MaxTemp", Json.temperatureMax[ 0 ] , "F" ) } // Tomorrow's max temperature if( Json.temperatureMax[ 1 ] != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.temperatureMax[ 1 ] , "F" ) } // Tonight's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tonight_WindSpeed", Json.daypart[ 0 ].windSpeed[ 1 ] , "mph" ) } // Tomorrow's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.daypart[ 0 ].windSpeed[ 2 ] , "mph" ) } // Tomorrow night's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tomorrownight_WindSpeed", Json.daypart[ 0 ].windSpeed[ 3 ] , "mph" ) } } else { // Tonight's temperature if( Json.daypart[ 0 ].temperature[ 1 ] != null ){ ProcessEvent( "Tonight_Temperature", Json.daypart[ 0 ].temperature[ 1 ] , "C" ) } // Tomorrow's temperature if( Json.daypart[ 0 ].temperature[ 2 ] != null ){ ProcessEvent( "Tomorrow_Temperature", Json.daypart[ 0 ].temperature[ 2 ] , "C" ) } // Tomorrow night's temperature if( Json.daypart[ 0 ].temperature[ 2 ] != null ){ ProcessEvent( "Tomorrownight_Temperature", Json.daypart[ 0 ].temperature[ 3 ] , "C" ) } // Today's min temperature if( Json.temperatureMin[ 0 ] != null ){ ProcessEvent( "Today_MinTemp", Json.temperatureMin[ 0 ] , "C" ) } // Tomorrow's min temperature if( Json.temperatureMin[ 1 ] != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.temperatureMin[ 1 ] , "C" ) } // Today's max temperature if( Json.temperatureMax[ 0 ] != null ){ ProcessEvent( "Today_MaxTemp", Json.temperatureMax[ 0 ] , "C" ) } // Tomorrow's max temperature if( Json.temperatureMax[ 1 ] != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.temperatureMax[ 1 ] , "C" ) } // Tonight's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tonight_WindSpeed", Json.daypart[ 0 ].windSpeed[ 1 ] , "kph" ) } // Tomorrow's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.daypart[ 0 ].windSpeed[ 2 ] , "kph" ) } // Tomorrow night's wind speed if( Json.daypart[ 0 ].windSpeed[ 1 ] != null ){ ProcessEvent( "Tomorrownight_WindSpeed", Json.daypart[ 0 ].windSpeed[ 3 ] , "kph" ) } } // Today's sunrise if( Json.sunriseTimeLocal[ 0 ] != null ){ ProcessEvent( "Today_Sunrise", Json.sunriseTimeLocal[ 0 ] ) } // Today's sunset if( Json.sunsetTimeLocal[ 0 ] != null ){ ProcessEvent( "Today_Sunset", Json.sunsetTimeLocal[ 0 ] ) } // Today's Detailed Forecast if( Json.narrative[ 0 ] != null ){ ProcessEvent( "Today_DetailedForecast", Json.narrative[ 0 ] ) } // Today's Valid Time if( Json.validTimeLocal[ 0 ] != null ){ ProcessEvent( "Today_ValidTime", Json.validTimeLocal[ 0 ] ) } // Tonight's wind direction if( Json.daypart[ 0 ].windDirection[ 1 ] != null ){ ProcessEvent( "Tonight_WindDirection", Json.daypart[ 0 ].windDirection[ 1 ] , "°" ) } // Tomorrow's wind direction if( Json.daypart[ 0 ].windDirection[ 1 ] != null ){ ProcessEvent( "Tomorrow_WindDirection", Json.daypart[ 0 ].windDirection[ 2 ] , "°" ) } // Tomorrow night's wind direction if( Json.daypart[ 0 ].windDirection[ 1 ] != null ){ ProcessEvent( "Tomorrownight_WindDirection", Json.daypart[ 0 ].windDirection[ 3 ] , "°" ) } // Tomorrow's sunrise if( Json.sunriseTimeLocal[ 1 ] != null ){ ProcessEvent( "Tomorrow_Sunrise", Json.sunriseTimeLocal[ 1 ] ) } // Tomorrow's sunset if( Json.sunsetTimeLocal[ 1 ] != null ){ ProcessEvent( "Tomorrow_Sunset", Json.sunsetTimeLocal[ 1 ] ) } // Tomorrow's Detailed Forecast if( Json.narrative[ 1 ] != null ){ ProcessEvent( "Tomorrow_DetailedForecast", Json.narrative[ 1 ] ) } // Tomorrow's Valid Time if( Json.validTimeLocal[ 1 ] != null ){ ProcessEvent( "Tomorrow_ValidTime", Json.validTimeLocal[ 1 ] ) } Logging( "Weather Underground forecast data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) // Section for the forecast child devices if( ChildrenEnabled ){ ClearForecastChildren() for( i = 0; i <= 5; i++ ){ def Day = Json.dayOfWeek[ i ] if( Json.temperatureMin[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Min Temperature", Json.temperatureMin[ i ], "${ location.temperatureScale }" ) } if( Json.temperatureMax[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Max Temperature", Json.temperatureMax[ i ], "${ location.temperatureScale }" ) } if( Json.qpf[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Chance Precipitation", ( Json.qpf[ i ] * 100 ), "%" ) } if( Json.qpfSnow[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Chance Snow", ( Json.qpfSnow[ i ] * 100 ), "%" ) } if( Json.sunriseTimeLocal[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Sunrise", "${ Json.sunriseTimeLocal[ i ] }" ) } if( Json.sunsetTimeLocal[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Sunset", "${ Json.sunsetTimeLocal[ i ] }" ) } if( Json.moonPhase[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moon Phase", "${ Json.moonPhase[ i ] }" ) } if( Json.moonriseTimeLocal[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moonrise", "${ Json.moonriseTimeLocal[ i ] }" ) } if( Json.moonsetTimeLocal[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Moonset", "${ Json.moonsetTimeLocal[ i ] }" ) } if( Json.validTimeLocal[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Forecast Valid as of", "${ Json.validTimeLocal[ i ] }" ) } if( Json.narrative[ i ] != null ){ PostEventToChild( "${ device.name } ${ Day }", "Conditions", "${ Json.narrative[ i ] }" ) } } } } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid APIKey provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to Weather Underground: ${ resp.status }", 4 ) } } // Get the WWO forecast returned, output events and populate attributes as needed def GetWWOForecast( resp, data ){ if( resp.getStatus() == 200 ){ Logging( "${ Region } = ${ resp.data }", 4 ) Json = parseJson( resp.data ) if( Json != null ){ // Updated if( Json.data.current_condition[ 0 ].observation_time != null ){ ProcessEvent( "Updated", "${ Json.data.weather[ 0 ].date }T${ Json.data.current_condition[ 0 ].observation_time }" ) } // wind direction if( Json.data.current_condition[ 0 ].winddirDegree != null ){ ProcessEvent( "windDirection", Json.data.current_condition[ 0 ].winddirDegree, "°" ) } if( Json.data.current_condition[ 0 ].winddir16Point != null ){ ProcessEvent( "WindDirectionString", Json.data.current_condition[ 0 ].winddir16Point ) } // Conditions if( Json.data.current_condition[ 0 ].weatherDesc != null ){ ProcessEvent( "Conditions", Json.data.current_condition[ 0 ].weatherDesc ) } if( location.temperatureScale == "F" ){ // temperature if( Json.data.current_condition[ 0 ].temp_F != null ){ ProcessEvent( "temperature", Json.data.current_condition[ 0 ].temp_F, "°F" ) } // feelsLike if( Json.data.current_condition[ 0 ].FeelsLikeF != null ){ ProcessEvent( "feelsLike", Json.data.current_condition[ 0 ].FeelsLikeF, "°F" ) } // Rain if( Json.data.current_condition[ 0 ].precipInches != null ){ ProcessEvent( "hourlyRain", Json.data.current_condition[ 0 ].precipInches, "in" ) } // windSpeed if( Json.data.current_condition[ 0 ].windspeedMiles != null ){ ProcessEvent( "windSpeed", Json.data.current_condition[ 0 ].windspeedMiles, "mph" ) } // pressure if( Json.data.current_condition[ 0 ].pressureInches != null ){ ProcessEvent( "pressure", Json.data.current_condition[ 0 ].pressureInches, "in" ) } } else { // temperature if( Json.data.current_condition[ 0 ].temp_C != null ){ ProcessEvent( "temperature", Json.data.current_condition[ 0 ].temp_C, "°C" ) } // feelsLike if( Json.data.current_condition[ 0 ].FeelsLikeC != null ){ ProcessEvent( "feelsLike", Json.data.current_condition[ 0 ].FeelsLikeC, "°C" ) } // Rain if( Json.data.current_condition[ 0 ].precipMM != null ){ ProcessEvent( "hourlyRain", Json.data.current_condition[ 0 ].precipMM, "mm" ) } // windSpeed if( Json.data.current_condition[ 0 ].windspeedKmph != null ){ ProcessEvent( "windSpeed", Json.data.current_condition[ 0 ].windspeedKmph, "kmph" ) } // pressure if( Json.data.current_condition[ 0 ].pressure != null ){ ProcessEvent( "pressure", Json.data.current_condition[ 0 ].pressure, "mbar" ) } } // Today's temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].hourly[ 0 ].tempF != null ){ ProcessEvent( "Today_Temperature", Json.data.weather[ 0 ].hourly[ 0 ].tempF, "F" ) } } else { if( Json.data.weather[ 0 ].hourly[ 0 ].tempC != null ){ ProcessEvent( "Today_Temperature", Json.data.weather[ 0 ].hourly[ 0 ].tempC, "C" ) } } // Today's min temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].mintempF != null ){ ProcessEvent( "Today_MinTemp", Json.data.weather[ 0 ].mintempF, "F" ) } } else { if( Json.data.weather[ 0 ].mintempC != null ){ ProcessEvent( "Today_MinTemp", Json.data.weather[ 0 ].mintempC, "C" ) } } // Today's max temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].maxtempF!= null ){ ProcessEvent( "Today_MaxTemp", Json.data.weather[ 0 ].maxtempF, "F" ) } } else { if( Json.data.weather[ 0 ].maxtempC != null ){ ProcessEvent( "Today_MaxTemp", Json.data.weather[ 0 ].maxtempC, "C" ) } } // Today's wind speed if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].hourly[ 0 ].windspeedMiles != null ){ ProcessEvent( "Today_WindSpeed", Json.data.weather[ 0 ].hourly[ 0 ].windspeedMiles, "mph" ) } } else { if( Json.data.weather[ 0 ].hourly[ 0 ].windspeedKmph != null ){ ProcessEvent( "Today_WindSpeed", Json.data.weather[ 0 ].hourly[ 0 ].windspeedKmph, "kph" ) } } // Today's wind direction if( Json.data.weather[ 0 ].hourly[ 0 ].winddirDegree != null ){ ProcessEvent( "Today_WindDirection", Json.data.weather[ 0 ].hourly[ 0 ].winddirDegree, "°" ) } // Today's wind gust if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].hourly[ 0 ].WindGustMiles != null ){ ProcessEvent( "Today_WindGust", Json.data.weather[ 0 ].hourly[ 0 ].WindGustMiles, "mph" ) } } else { if( Json.data.weather[ 0 ].hourly[ 0 ].WindGustKmph != null ){ ProcessEvent( "Today_WindGust", Json.data.weather[ 0 ].hourly[ 0 ].WindGustKmph, "kph" ) } } // Today's short forecast if( Json.data.weather[ 0 ].hourly[ 0 ].weatherDesc[ 0 ].value != null ){ ProcessEvent( "Today_ShortForecast", Json.data.weather[ 0 ].hourly[ 0 ].weatherDesc[ 0 ].value ) } // Today's chance of rain if( Json.data.weather[ 0 ].hourly[ 0 ].chanceofrain != null ){ ProcessEvent( "Today_ChanceRain", Json.data.weather[ 0 ].hourly[ 0 ].chanceofrain ) } // Today's chance of clouds if( Json.data.weather[ 0 ].hourly[ 0 ].chanceofovercast != null ){ ProcessEvent( "Today_ChanceClouds", Json.data.weather[ 0 ].hourly[ 0 ].chanceofovercast ) } // Today's chance of snow if( Json.data.weather[ 0 ].hourly[ 0 ].chanceofsnow != null ){ ProcessEvent( "Today_ChanceSnow", Json.data.weather[ 0 ].hourly[ 0 ].chanceofsnow ) } // Today's wind string if( Json.data.weather[ 0 ].hourly[ 0 ].winddir16Point != null ){ ProcessEvent( "Today_WindDirectionString", Json.data.weather[ 0 ].hourly[ 0 ].winddir16Point ) } // Today's feels like if( location.temperatureScale == "F" ){ if( Json.data.weather[ 0 ].hourly[ 0 ].FeelsLikeF != null ){ ProcessEvent( "Today_FeelsLike", Json.data.weather[ 0 ].hourly[ 0 ].FeelsLikeF, "F" ) } } else { if( Json.data.weather[ 0 ].hourly[ 0 ].FeelsLikeC != null ){ ProcessEvent( "Today_FeelsLike", Json.data.weather[ 0 ].hourly[ 0 ].FeelsLikeC, "C" ) } } // Today's sunrise if( Json.data.weather[ 0 ].astronomy[ 0 ].sunrise != null ){ ProcessEvent( "Today_Sunrise", "${ Json.data.weather[ 0 ].date }T${ Json.data.weather[ 0 ].astronomy[ 0 ].sunrise }" ) } // Today's sunset if( Json.data.weather[ 0 ].astronomy[ 0 ].sunset != null ){ ProcessEvent( "Tomorrownight_WindSpeed", "${ Json.data.weather[ 0 ].date }T${ Json.data.weather[ 0 ].astronomy[ 0 ].sunset }" ) } // Today's weather icon if( Json.data.weather[ 0 ].hourly[ 0 ].weatherIconUrl[ 0 ].value != null ){ ProcessEvent( "Today_IconURL", Json.data.weather[ 0 ].hourly[ 0 ].weatherIconUrl[ 0 ].value ) } // Today's humidity if( Json.data.weather[ 0 ].hourly[ 0 ].humidity != null ){ ProcessEvent( "Today_Humidity", Json.data.weather[ 0 ].hourly[ 0 ].humidity, "%" ) } // Today's pressure if( Json.data.weather[ 0 ].hourly[ 0 ].pressure != null ){ ProcessEvent( "Today_Pressure", Json.data.weather[ 0 ].hourly[ 0 ].pressure ) } // Today's UVI if( Json.data.weather[ 0 ].hourly[ 0 ].uvIndex != null ){ ProcessEvent( "Today_UVI", Json.data.weather[ 0 ].hourly[ 0 ].uvIndex ) } // Tomorrow's temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].hourly[ 0 ].tempF != null ){ ProcessEvent( "Tomorrow_Temperature", Json.data.weather[ 1 ].hourly[ 0 ].tempF, "F" ) } } else { if( Json.data.weather[ 1 ].hourly[ 0 ].tempC != null ){ ProcessEvent( "Tomorrow_Temperature", Json.data.weather[ 1 ].hourly[ 0 ].tempC, "C" ) } } // Tomorrow's min temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].mintempF != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.data.weather[ 1 ].mintempF, "F" ) } } else { if( Json.data.weather[ 1 ].mintempC != null ){ ProcessEvent( "Tomorrow_MinTemp", Json.data.weather[ 1 ].mintempC, "C" ) } } // Tomorrow's max temperature if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].maxtempF!= null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.data.weather[ 1 ].maxtempF, "F" ) } } else { if( Json.data.weather[ 1 ].maxtempC != null ){ ProcessEvent( "Tomorrow_MaxTemp", Json.data.weather[ 1 ].maxtempC, "C" ) } } // Tomorrow's wind speed if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].hourly[ 0 ].windspeedMiles != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.data.weather[ 1 ].hourly[ 0 ].windspeedMiles, "mph" ) } } else { if( Json.data.weather[ 1 ].hourly[ 0 ].windspeedKmph != null ){ ProcessEvent( "Tomorrow_WindSpeed", Json.data.weather[ 1 ].hourly[ 0 ].windspeedKmph, "kph" ) } } // Tomorrow's wind direction if( Json.data.weather[ 1 ].hourly[ 0 ].winddirDegree != null ){ ProcessEvent( "Tomorrow_WindDirection", Json.data.weather[ 1 ].hourly[ 0 ].winddirDegree, "°" ) } // Tomorrow's wind gust if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].hourly[ 0 ].WindGustMiles != null ){ ProcessEvent( "Tomorrow_WindGust", Json.data.weather[ 1 ].hourly[ 0 ].WindGustMiles, "mph" ) } } else { if( Json.data.weather[ 1 ].hourly[ 0 ].WindGustKmph != null ){ ProcessEvent( "Tomorrow_WindGust", Json.data.weather[ 1 ].hourly[ 0 ].WindGustKmph, "kph" ) } } // Tomorrow's short forecast if( Json.data.weather[ 1 ].hourly[ 0 ].weatherDesc[ 0 ].value != null ){ ProcessEvent( "Tomorrow_ShortForecast", Json.data.weather[ 1 ].hourly[ 0 ].weatherDesc[ 0 ].value ) } // Tomorrow's chance of rain if( Json.data.weather[ 1 ].hourly[ 0 ].chanceofrain != null ){ ProcessEvent( "Tomorrow_ChanceRain", Json.data.weather[ 1 ].hourly[ 0 ].chanceofrain ) } // Tomorrow's chance of clouds if( Json.data.weather[ 1 ].hourly[ 0 ].chanceofovercast != null ){ ProcessEvent( "Tomorrow_ChanceClouds", Json.data.weather[ 1 ].hourly[ 0 ].chanceofovercast ) } // Tomorrow's chance of snow if( Json.data.weather[ 1 ].hourly[ 0 ].chanceofsnow != null ){ ProcessEvent( "Tomorrow_ChanceSnow", Json.data.weather[ 1 ].hourly[ 0 ].chanceofsnow ) } // Tomorrow's wind string if( Json.data.weather[ 1 ].hourly[ 0 ].winddir16Point != null ){ ProcessEvent( "Tomorrow_WindDirectionString", Json.data.weather[ 1 ].hourly[ 0 ].winddir16Point ) } // Tomorrow's feels like if( location.temperatureScale == "F" ){ if( Json.data.weather[ 1 ].hourly[ 0 ].FeelsLikeF != null ){ ProcessEvent( "Tomorrow_FeelsLike", Json.data.weather[ 1 ].hourly[ 0 ].FeelsLikeF, "F" ) } } else { if( Json.data.weather[ 1 ].hourly[ 0 ].FeelsLikeC != null ){ ProcessEvent( "Tomorrow_FeelsLike", Json.data.weather[ 1 ].hourly[ 0 ].FeelsLikeC, "C" ) } } // Tomorrow's sunrise if( Json.data.weather[ 1 ].astronomy[ 0 ].sunrise != null ){ ProcessEvent( "Tomorrow_Sunrise", "${ Json.data.weather[ 1 ].date }T${ Json.data.weather[ 1 ].astronomy[ 0 ].sunrise }" ) } // Tomorrow's sunset if( Json.data.weather[ 1 ].astronomy[ 0 ].sunset != null ){ ProcessEvent( "Tomorrownight_WindSpeed", "${ Json.data.weather[ 1 ].date }T${ Json.data.weather[ 1 ].astronomy[ 0 ].sunset }" ) } // Tomorrow's weather icon if( Json.data.weather[ 1 ].hourly[ 0 ].weatherIconUrl[ 0 ].value != null ){ ProcessEvent( "Tomorrow_IconURL", Json.data.weather[ 1 ].hourly[ 0 ].weatherIconUrl[ 0 ].value ) } // Tomorrow's humidity if( Json.data.weather[ 1 ].hourly[ 0 ].humidity != null ){ ProcessEvent( "Tomorrow_Humidity", Json.data.weather[ 1 ].hourly[ 0 ].humidity, "%" ) } // Today's pressure if( Json.data.weather[ 1 ].hourly[ 0 ].pressure != null ){ ProcessEvent( "Tomorrow_Pressure", Json.data.weather[ 1 ].hourly[ 0 ].pressure ) } // Tomorrow's UVI if( Json.data.weather[ 1 ].hourly[ 0 ].uvIndex != null ){ ProcessEvent( "Tomorrow_UVI", Json.data.weather[ 1 ].hourly[ 0 ].uvIndex ) } Logging( "World Weather Online forecast data as of ${ new Date() }.", 2 ) ProcessEvent( "Data as of", "${ new Date() }" ) } else { Logging( "No data returned by API", 4 ) } } else if( resp.getStatus() == 401 ){ Logging( "Invalid APIKey provided", 4 ) } else if( resp.getStatus() == 429 ){ Logging( "Maximum API calls per day", 4 ) } else { Logging( "Error connecting to World Weather Online: ${ resp.status }", 4 ) } } // Process data to check against current state value and then send an event if it has changed def ProcessEvent( Name, Value, Unit = null ){ if( state."${ Name }" != Value ){ state."${ Name }" = Value if( Unit != null ){ Logging( "Event ${ Name } = ${ Value }${ Unit }", 4 ) sendEvent( name: "${ Name }", value: Value, unit: Unit, isStateChange: true ) } else { Logging( "Event ${ Name } = ${ Value }", 4 ) sendEvent( name: "${ Name }", value: Value, isStateChange: true ) } } } // Check data to against current state value and change the state if needed def ProcessState( Name, Value ){ if( state."${ Name }" != Value ){ state."${ Name }" = Value Logging( "state.${ Name } = ${ Value }", 4 ) } } // Significant weather code check for UK Met Office codes def UKSignificantWeather( Code ){ def WeatherString switch( Code ){ case "NA": WeatherString = "Not available" break case "0": WeatherString = "Clear night" break case "1": WeatherString = "Sunny day" break case "2": WeatherString = "Partly cloudy (night)" break case "3": WeatherString = "Partly cloudy (day)" break case "4": WeatherString = "Not used" break case "5": WeatherString = "Mist" break case "6": WeatherString = "Fog" break case "7": WeatherString = "Cloudy" break case "8": WeatherString = "Overcast" break case "9": WeatherString = "Light rain shower (night)" break case "10": WeatherString = "Light rain shower (day)" break case "11": WeatherString = "Drizzle" break case "12": WeatherString = "Light rain" break case "13": WeatherString = "Heavy rain shower (night)" break case "14": WeatherString = "Heavy rain shower (day)" break case "15": WeatherString = "Heavy rain" break case "16": WeatherString = "Sleet shower (night)" break case "17": WeatherString = "Sleet shower (day)" break case "18": WeatherString = "Sleet" break case "19": WeatherString = "Hail shower (night)" break case "20": WeatherString = "Hail shower (day)" break case "21": WeatherString = "Hail" break case "22": WeatherString = "Light snow shower (night)" break case "23": WeatherString = "Light snow shower (day)" break case "24": WeatherString = "Light snow" break case "25": WeatherString = "Heavy snow shower (night)" break case "26": WeatherString = "Heavy snow shower (day)" break case "27": WeatherString = "Heavy snow" break case "28": WeatherString = "Thunder shower (night)" break case "29": WeatherString = "Thunder shower (day)" break case "30": WeatherString = "Thunder" break default: WeatherString = "Not available" break } return WeatherString } // Provide a number based on US Wind Direction string def USWindDirection( WindDirection ){ def WindDegrees switch( WindDirection ){ case "N": WindDegrees = 0 break case "NE": WindDegrees = 45 break case "E": WindDegrees = 90 break case "SE": WindDegrees = 135 break case "S": WindDegrees = 180 break case "SW": WindDegrees = 225 break case "W": WindDegrees = 270 break case "NW": WindDegrees = 315 break } return WindDegrees } // Process the US detailed forecast for Chance of Precipitation and Type def ProcessUSDetailedForecast( Which, DetailedForecast ){ def Split = DetailedForecast.split( "Chance of precipitation is " ) if( Split.size() > 1 ){ Split = Split[ 1 ].split( "%" ) def Percent = Split[ 0 ] //Logging( "Detailed = ${ DetailedForecast }", 3 ) if( DetailedForecast.contains( "Rain" ) || DetailedForecast.contains( "rain" ) ){ switch( Which ){ case 1: ProcessEvent( "Today_ChanceRain", Percent, "%" ) break case 2: ProcessEvent( "Tonight_ChanceRain", Percent, "%" ) break case 3: ProcessEvent( "Tomorrow_ChanceRain", Percent, "%" ) break case 4: ProcessEvent( "Tomorrownight_ChanceRain", Percent, "%" ) break } } else if( DetailedForecast.contains( "Snow" ) || DetailedForecast.contains( "snow" ) ){ switch( Which ){ case 1: ProcessEvent( "Today_ChanceSnow", Percent, "%" ) break case 2: ProcessEvent( "Tonight_ChanceSnow", Percent, "%" ) break case 3: ProcessEvent( "Tomorrow_ChanceSnow", Percent, "%" ) break case 4: ProcessEvent( "Tomorrownight_ChanceSnow", Percent, "%" ) break } } } else { switch( Which ){ case 1: ProcessEvent( "Today_ChanceRain", 0, "%" ) ProcessEvent( "Today_ChanceSnow", 0, "%" ) break case 2: ProcessEvent( "Tonight_ChanceRain", 0, "%" ) ProcessEvent( "Tonight_ChanceSnow", 0, "%" ) break case 3: ProcessEvent( "Tomorrow_ChanceRain", 0, "%" ) ProcessEvent( "Tomorrow_ChanceSnow", 0, "%" ) break case 4: ProcessEvent( "Tomorrownight_ChanceRain", 0, "%" ) ProcessEvent( "Tomorrownight_ChanceSnow", 0, "%" ) break } } Split = DetailedForecast.split( "gusts as high as " ) if( Split.size() > 1 ){ Split = Split[ 1 ].split( " mph" ) def WindGust = Split[ 0 ] switch( Which ){ case 1: ProcessEvent( "Today_WindGust", WindGust, "mph" ) break case 2: ProcessEvent( "Tonight_WindGust", WindGust, "mph" ) break case 3: ProcessEvent( "Tomorrow_WindGust", WindGust, "mph" ) break case 4: ProcessEvent( "Tomorrownight_WindGust", WindGust, "mph" ) break } } } // Checks the location.getTemperatureScale() to convert temperature values def ConvertTemperature( Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( location.getTemperatureScale() == "C" ){ ReturnValue = ( ( ( Value - 32 ) * 5 ) / 9 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Checks the location.getTemperatureScale() to convert in to mm def ConvertInches( Number Value ){ if( Value != null ){ def TempInt def ReturnValue = Value as double if( location.getTemperatureScale() == "C" ){ ReturnValue = ( Value * 25.4 ) TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) } TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Checks the location.getTemperatureScale() to convert mph to kph def ConvertMPH( Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( location.getTemperatureScale() == "C" ){ ReturnValue = ( Value * 1.609 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Used to convert epoch values to text dates def ConvertEpochToDate( Number Epoch ){ def date = use( groovy.time.TimeCategory ) { new Date( 0 ) + Epoch.seconds } return date } String ReturnDayName( Number Day ){ def map=[ 1:"Sunday", 2:"Monday", 3:"Tuesday", 4:"Wednesday", 5:"Thursday", 6:"Friday", 7:"Saturday" ] return map[ Day ] } // Clears forecast data from child devices def ClearForecastChildren(){ for( i = 0; i <= 6; i++ ){ PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Min Temperature", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Max Temperature", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Chance Precipitation", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Chance Snow", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Sunrise", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Sunset", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Moon Phase", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Moonrise", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Moonset", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Forecast Valid as of", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Conditions", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "temperature", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "windSpeed", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "windDirection", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "WindDirectionString", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "windGust", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "humidity", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "pressure", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "uv", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "feelsLike", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "dewPoint", null ) PostEventToChild( "${ device.name } ${ ReturnDayName( i + 1 ) }", "Cloud Cover", null ) } } // Post data to child device def PostEventToChild( Child, Variable, Value, Unit = null, ForceEvent = false ){ if( ChildrenEnabled ){ if( Child != null ){ if( getChildDevice( "${ Child }" ) == null ){ addSensor( "${ Child }" ) } if( getChildDevice( "${ Child }" ) != null ){ if( Unit != null ){ if( ForceEvent ){ getChildDevice( "${ Child }" ).ProcessEvent( "${ Variable }", Value, "${ Unit }", ForceEvent ) Logging( "Child Event: ${ Variable } = ${ Value }${ Unit }", 4 ) } else { getChildDevice( "${ Child }" ).ProcessEvent( "${ Variable }", Value, "${ Unit }" ) Logging( "Child Event: ${ Variable } = ${ Value }", 4 ) } } else { if( ForceEvent ){ getChildDevice( "${ Child }" ).ProcessEvent( "${ Variable }", Value, null, ForceEvent ) Logging( "Child Event: ${ Variable } = ${ Value }${ Unit }", 4 ) } else { getChildDevice( "${ Child }" ).ProcessEvent( "${ Variable }", Value ) Logging( "Child Event: ${ Variable } = ${ Value }", 4 ) } } } else { if( Unit != null ){ Logging( "Failure to add ${ Child } and post ${ Variable }=${ Value }${ Unit }", 5 ) } else { Logging( "Failure to add ${ Child } and post ${ Variable }=${ Value }", 5 ) } } } else { Logging( "Failure to add child because child name was null", 5 ) } } } // Post data to child device def PostStateToChild( Child, Variable, Value ){ def ChildParent = "${ Child }" if( ChildrenEnabled ){ if( ChildParent != null ){ if( getChildDevice( "${ ChildParent }" ) == null ){ addChild( "${ ChildParent }" ) } if( getChildDevice( "${ ChildParent }" ) != null ){ Logging( "${ ChildParent } State: ${ Variable } = ${ Value }", 4 ) getChildDevice( "${ ChildParent }" ).ProcessState( "${ Variable }", Value ) } else { Logging( "${ ChildParent } does not exist so could not post ${ Variable }=${ Value }", 5 ) } } else { Logging( "Failure to add child because child name was null", 5 ) } } } // Adds a WeatherSensorChild child device // Based on @mircolino's method for child sensors def addChild( String DNI ){ try{ Logging( "addChild(${ DNI })", 3 ) addChildDevice( "WeatherSensorChild", DNI, [ name: "${ DNI }" ] ) } catch( Exception e ){ def Temp = e as String if( Temp.contains( "not found" ) ){ Logging( "WeatherSensorChild driver is not loaded, this is required for child devices.\n Disabling children for rest of refresh.", 5 ) ChildrenEnabled = false } else { Logging( "Exception in addChild: ${ Temp }", 5 ) } } } // uninstalling device so make sure to clean up children void uninstalled() { // Delete all children getChildDevices().each{ deleteChildDevice( it.deviceNetworkId ) } Logging( "Uninstalled", 2 ) } // Handles whether logging is enabled and thus what to put there. def Logging( LogMessage, LogLevel ){ // Add all messages as info logging if( ( LogLevel == 2 ) && ( LogType != "None" ) ){ log.info( "${ device.displayName } - ${ LogMessage }" ) } else if( ( LogLevel == 3 ) && ( ( LogType == "Debug" ) || ( LogType == "Trace" ) ) ){ log.debug( "${ device.displayName } - ${ LogMessage }" ) } else if( ( LogLevel == 4 ) && ( LogType == "Trace" ) ){ log.trace( "${ device.displayName } - ${ LogMessage }" ) } else if( LogLevel == 5 ){ log.error( "${ device.displayName } - ${ LogMessage }" ) } } // Checks drdsnell.com for the latest version of the driver // Original inspiration from @cobra's version checking def CheckForUpdate(){ ProcessEvent( "DriverName", DriverName() ) ProcessEvent( "DriverVersion", DriverVersion() ) httpGet( uri: "https://www.drdsnell.com/projects/hubitat/drivers/versions.json", contentType: "application/json" ){ resp -> switch( resp.status ){ case 200: if( resp.data."${ DriverName() }" ){ CurrentVersion = DriverVersion().split( /\./ ) if( resp.data."${ DriverName() }".version == "REPLACED" ){ ProcessEvent( "DriverStatus", "Driver replaced, please use ${ resp.data."${ state.DriverName }".file }" ) } else if( resp.data."${ DriverName() }".version == "REMOVED" ){ ProcessEvent( "DriverStatus", "Driver removed and no longer supported." ) } else { SiteVersion = resp.data."${ DriverName() }".version.split( /\./ ) if( CurrentVersion == SiteVersion ){ Logging( "Driver version up to date", 3 ) ProcessEvent( "DriverStatus", "Up to date" ) } else if( ( CurrentVersion[ 0 ] as int ) > ( SiteVersion [ 0 ] as int ) ){ Logging( "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" ) } else if( ( CurrentVersion[ 1 ] as int ) > ( SiteVersion [ 1 ] as int ) ){ Logging( "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" ) } else if( ( CurrentVersion[ 2 ] as int ) > ( SiteVersion [ 2 ] as int ) ){ Logging( "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version" ) } else if( ( SiteVersion[ 0 ] as int ) > ( CurrentVersion[ 0 ] as int ) ){ Logging( "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" ) } else if( ( SiteVersion[ 1 ] as int ) > ( CurrentVersion[ 1 ] as int ) ){ Logging( "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" ) } else if( ( SiteVersion[ 2 ] as int ) > ( CurrentVersion[ 2 ] as int ) ){ Logging( "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available" ) } } } else { Logging( "${ DriverName() } is not published on drdsnell.com", 2 ) ProcessEvent( "DriverStatus", "${ DriverName() } is not published on drdsnell.com" ) } break default: Logging( "Unable to check drdsnell.com for ${ DriverName() } driver updates.", 2 ) break } } }