/* * WeatherFlow * * Description: * This Hubitat driver polls the WeatherFlow API for your station's data. * * The driver CAN support child devices if the WeatherSensorChild.groovy driver is also installed. This will generate a number of * children based on the sensors reporting. Children will be automatically created when applicable data is posted. All children are * deleted if the feature is disabled (it is disabled by default). Child sensors can be useful for Rules or the Dashboard in order to * focus on particular values, like a specific temperature sensor, or combining temperature and humidity readings. * * Required Information: * 1) Token to allow data to be polled (from account Settings - Data Authorizations) * 2) Your Station ID (if you do not know, enter a number and run the GetStationList command, then check the Station List state variable) * * Optional: * If you have multiple sensors, enter the Sensor ID for the sensor data you want displayed in the parent device * * Features List: * Child devices generated based on sensors reporting * Ability to read and report all known values returned from the WeatherFlow API for a particular station * 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.4.17 - Correction for station observation handling * 0.4.16 - TimestampDate was being posted instead of TimestampString in some places * 0.4.15 - TimestampEpoch is now posted as an event * 0.4.14 - Cleanup of some TempID and Data.device_id points to keep code simpler * 0.4.13 - Corrected missing humidity value from being copied to parent device and correcting ParentSensorID to an int * 0.4.12 - Change to data handling for multiple stations and added Preference for Sensor ID to provide data to parent device * 0.4.11 - Added preference for Pressure Units and changed conversions and events to handle the unit selected * 0.4.10 - Correction to ProcessEvent function * 0.4.9 - Handling for null station data and update to driver specific attribute names * 0.4.8 - Removing hubs from device-specific data call as WeatherFlow API no longer returns data for them * 0.4.7 - Added a command to "ClearStateVariables" so events and variables will sync * 0.4.6 - Added rain + hail for PrecipitationTypeString * 0.4.5 - Swapped out many areas where a state variable was being referenced to instead use the data for that state * 0.4.4 - Fix for items being processed as states that should have been events (caused other issues as well) * 0.4.3 - Additional data points added and updated driver version checking * 0.4.2 - Converting lightning strike distance from km if needed * 0.4.1 - Added 1 minute refresh back in, added extra checks around device labeling * 0.4.0 - Added support for Forecast data, changed refresh interval (removed 1 minute added 1 hour), * now using Token instead of API Key, added GetStationList command, reworked child naming * 0.3.5 - Replaced boolean attributes with string attributes because boolean attributes are not valid * 0.3.4 - Handle devices reported without Serial # * 0.3.3 - Rework of logging and expanding value range when converting inches * 0.3.2 - Some rework of data processing and adding missed attributes * 0.3.1 - Rework of version control and state/event processing * 0.02 - Rework child naming convention * 0.01 - Initial version based on my AmbientEcowittWeather driver * * Thank you(s): * Thank you to @Cobra for inspiration of how I perform driver version checking. * Thank you to @mircolino for working out a parent/child method and pointing out other areas for significant improvement. */ // Returns the driver name def DriverName(){ return "WeatherFlow" } // Returns the driver version def DriverVersion(){ return "0.4.17" } // Driver Metadata metadata{ definition( name: "WeatherFlow", namespace: "Snell", author: "David Snell", importUrl: "https://www.drdsnell.com/projects/hubitat/drivers/WeatherFlow.groovy" ) { // Indicate what capabilities the device should be capable of capability "Sensor" capability "Refresh" capability "Battery" capability "PressureMeasurement" capability "RelativeHumidityMeasurement" capability "CarbonDioxideMeasurement" capability "TemperatureMeasurement" capability "UltravioletIndex" capability "IlluminanceMeasurement" capability "pHMeasurement" // Commands from the driver command "PollForecast" command "GetStationList" command "ClearStateVariables" // 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 status information/notices for driver // Special attributes attribute "Station List", "list" // // Station attributes attribute "StationName", "string" attribute "PublicName", "string" attribute "Elevation", "number" attribute "Latitude", "number" attribute "Longitude", "number" attribute "TimeZone", "string" attribute "Public", "string" attribute "LocalMode", "string" attribute "Units_Temp", "string" attribute "Units_Wind", "string" attribute "Units Precip", "string" attribute "Units Pressure", "string" attribute "Units Distance", "string" attribute "Units Direction", "string" attribute "Units Other", "string" attribute "Status Code", "number" attribute "Status Message", "string" attribute "Shared With WeatherFlow", "string" attribute "Shared With WeatherUnderground", "string" attribute "LastModifiedEpoch", "number" attribute "LastModifiedString", "string" attribute "Messages", "string" attribute "SensorIDs", "number" // Attributes for observational data attribute "TimestampEpoch", "number" attribute "TimestampString", "string" attribute "AirTemperature", "number" attribute "AirTemperatureIndoor", "number" attribute "temperature", "number" attribute "temperatureIndoor", "number" attribute "BarometricPressure", "number" attribute "BarometricPressureIndoor", "number" attribute "StationPressure", "number" attribute "StationPressureIndoor", "number" attribute "pressure", "number" attribute "pressureIndoor", "number" attribute "SeaLevelPressure", "number" attribute "SeaLevelPressureIndoor", "number" attribute "RelativeHumidity", "number" attribute "RelativeHumidityIndoor", "number" attribute "humidity", "number" attribute "humidityIndoor", "number" attribute "Precip", "number" attribute "PrecipAccumLast1hr", "number" attribute "PrecipAccumLocalDay", "number" attribute "PrecipAccumLocalDayFinal", "number" attribute "PrecipAccumLocalYesterday", "number" attribute "PrecipAccumLocalYesterdayFinal", "number" attribute "PrecipMinutesLocalDay", "number" attribute "PrecipMinutesLocalYesterday", "number" attribute "PrecipMinutesLocalYesterdayFinal", "number" attribute "windAvg", "number" attribute "windSpeed", "number" attribute "windGust", "number" attribute "windLull", "number" attribute "windDirection", "number" attribute "WindDirectionString", "string" attribute "SolarRadiation", "number" attribute "ultravioletIndex", "number" attribute "ultravioletIndexIndoor", "number" attribute "illuminance", "number" attribute "illuminanceIndoor", "number" attribute "LightningStrikeLastEpoch", "number" attribute "LightningStrikeLastDate", "string" attribute "LightningStrikeLastDistance", "number" attribute "LightningStrikeCount", "number" attribute "LightningStrikeCountLast1hr", "number" attribute "LightningStrikeCountLast3hr", "number" attribute "feelsLike", "number" attribute "feelsLikein", "number" attribute "HeatIndex", "number" attribute "HeatIndexIndoor", "number" attribute "WindChill", "number" attribute "WindChillIndoor", "number" attribute "dewPoint", "number" attribute "dewPointIndoor", "number" attribute "WetBulbTemperature", "number" attribute "WetBulbTemperatureIndoor", "number" attribute "WetBulbGlobeTemperature", "number" attribute "DeltaT", "number" attribute "AirDensity", "number" attribute "PrecipAnalysisTypeYesterday", "number" attribute "PrecipAccumLocalYesterday", "number" attribute "PrecipTotal1h", "number" attribute "PressureTrend", "string" attribute "PrecipitationType", "number" attribute "PrecipitationTypeString", "string" attribute "PrecipitationAnalysisType", "number" attribute "PrecipitationAnalysisTypeString", "string" attribute "RainAccumulation", "number" attribute "RainAccumulationFinal", "number" attribute "LocalDayRainAccumulation", "number" attribute "LocalDayRainAccumulationFinal", "number" // Forecast Attributes attribute "Today Conditions", "string" attribute "Today Icon", "string" attribute "Today Sunrise", "string" attribute "Today Sunset", "string" attribute "Today High Temperature", "number" attribute "Today Low Temperature", "number" attribute "Today Chance Precipitation", "number" attribute "Today Precipitation Icon", "string" attribute "Today Precipitation Type", "string" attribute "Tomorrow Conditions", "string" attribute "Tomorrow Icon", "string" attribute "Tomorrow Sunrise", "string" attribute "Tomorrow Sunset", "string" attribute "Tomorrow High Temperature", "number" attribute "Tomorrow Low Temperature", "number" attribute "Tomorrow Chance Precipitation", "number" attribute "Tomorrow Precipitation Icon", "string" attribute "Tomorrow Precipitation Type", "string" } preferences{ section{ if( ShowAllPreferences ){ input( type: "string", name: "Token", title: "Token for WeatherFlow", required: true ) input( type: "string", name: "StationID", title: "Station ID to be checked", required: true ) input( type: "string", name: "ParentSensorID", title: "Sensor ID for Parent data", description: "If more than one sensor, enter Sensor's ID to copy data to parent device", required: false ) input( type: "enum", name: "RefreshRate", title: "Data Refresh Rate", required: false, multiple: false, options: [ "1 minute", "5 minutes", "15 minutes", "30 minutes", "Hourly", "Manual" ], defaultValue: "5 minutes" ) input( type: "enum", name: "MeasurementStandard", title: "Measurement Standard?", description: "Metric (as in kilometers) or Imperial (as in miles)", required: false, multiple: false, options: [ "Metric", "Imperial" ], defaultValue: "Metric" ) input( type: "enum", name: "PressureUnits", title:"Pressure Units?", description: "Select unit of pressure: mbar, psi, or inHG", required: false, options: [ "mbar", "psi", "inHG" ] ) input( type: "enum", name: "WindDirMethod", title: "Wind Direction Method?", description: "How do you want wind direction indicated?", required: false, multiple: false, options: [ [ "1" : "Degrees Only" ], [ "2" : "4 Compass Values (Letter Only)" ], [ "3" : "8 Compass Values (Letters Only)" ], [ "4" : "16 Compass Values (Letters Only)" ], [ "5" : "4 Compass Values (Words)" ], [ "6" : "8 Compass Values (Words)" ], [ "7" : "16 Compass Values (Words)" ] ], defaultValue: "1" ) input( type: "bool", name: "ChildrenEnabled", title: "Enable Child Devices?", defaultValue: false, description: "Once enabled, child devices will be made for added sensors.", required: false ) 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() // Schedule automatic checks of things def Hour = ( new Date().format( "h" ) as int ) def Minute = ( new Date().format( "m" ) as int ) def Second = ( new Date().format( "s" ) as int ) switch( RefreshRate ){ case "1 minute": schedule( "${ Second } * * ? * *", "refresh" ) break case "5 minutes": schedule( "${ Second } 0/5 * ? * *", "refresh" ) break case "15 minutes": schedule( "${ Second } 0/15 * ? * *", "refresh" ) break case "30 minutes": schedule( "${ Second } 0/30 * ? * *", "refresh" ) break case "Hourly": schedule( "${ Second } ${ Minute } * ? * *", "refresh" ) break case "Manual": break } Logging( "Refresh rate: ${ RefreshRate }", 4 ) // Set the driver name and version before update checking is scheduled if( state."Driver Status" != null ){ state.remove( "Driver Name" ) state.remove( "Driver Version" ) state.remove( "Driver Status" ) device.deleteCurrentState( "Driver Status" ) device.deleteCurrentState( "Driver Name" ) device.deleteCurrentState( "Driver Version" ) } ProcessEvent( "DriverName", DriverName() ) ProcessEvent( "DriverVersion", DriverVersion() ) ProcessEvent( "DriverStatus", null ) // Schedule the things to check daily schedule( "${ Second } ${ Minute } ${ Hour } ? * *", "CheckForUpdate" ) schedule( "${ Second } ${ Minute } 0 ? * *", "PollForecast" ) if( MeasurementStandard == null ){ MeasurementStandard = "Metric" } if( WindDirMethod == null ){ WindDirMethod = "1" } if( LogType == null ){ LogType = "2" } if( state.ParentSensorID != null ){ ProcessState( "ParentSensorID", ParentSensorID as int ) } if( !ChildrenEnabled ){ getChildDevices().each{ Logging( "Children disabled, deleting ${ it.deviceNetworkId }", 3 ) deleteChildDevice( it.deviceNetworkId ) } } Logging( "Saved preferences", 2 ) } // Clears all the current state variables def ClearStateVariables(){ state.clear() } // refresh performs a poll of data def refresh(){ PollWeatherFlow() } // 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() } // uninstalling device so make sure to clean up children void uninstalled() { // Delete all children getChildDevices().each{ deleteChildDevice( it.deviceNetworkId ) } Logging( "Uninstalled", 2 ) } // parse is one of those "special" methods for when data is returned. // Nothing should trigger it in this driver so a logging if something DOES show up. def parse( String description ){ Logging( "Something arrived for parse: ${ description }", 3 ) } //Poll WeatherFlow for station data def PollWeatherFlow(){ if( Token != null ){ def Params Params = [ uri: "https://swd.weatherflow.com/swd/rest/stations/${ StationID }?token=${ Token }", contentType: "application/json" ] asynchttpGet( "GetStationData", Params ) } else { Logging( "Token is required for WeatherFlow API.", 5 ) } } //Poll WeatherFlow for list of stations def GetStationList(){ if( Token != null ){ def Params Params = [ uri: "https://swd.weatherflow.com/swd/rest/stations?token=${ Token }", contentType: "application/json" ] asynchttpGet( "HandleStationList", Params ) } else { Logging( "Token is required for WeatherFlow API.", 5 ) } } // Handles the response from WeatherFlow and triggers more checks for specific device and observation data def HandleStationList( resp, data ){ switch( resp.getStatus() ){ case 200: Data = parseJson( resp.data ) Logging( "GetStationList response: ${ Data }", 4 ) def TempStations = [] Data.stations.each(){ TempStations.add( it.station_id as int ) } ProcessState( "Station List", TempStations ) break case 401: Logging( "Unauthorized, most likely due to invalid token.", 5 ) break case 404: Logging( "Station not found.", 5 ) break default: Logging( "Error connecting to WeatherFlow: ${ resp.status }", 5 ) break } } // Handles the response from WeatherFlow and triggers more checks for specific device and observation data def GetStationData( resp, data ){ switch( resp.getStatus() ){ case 200: Logging( "Raw response: ${ resp.data }", 4 ) if( resp.data != null ){ Data = parseJson( resp.data ) ParseStation( Data ) def Params if( Data.stations != null ){ if( Data.stations[ 0 ].devices != null ){ for( int i = 0; i < Data.stations[ 0 ].devices.size(); i++ ){ if( Data.stations[ 0 ].devices[ i ].device_type != "HB" ){ Logging( "Getting data for device: ${ Data.stations[ 0 ].devices[ i ].device_id }", 4 ) Params = [ uri: "https://swd.weatherflow.com/swd/rest/observations/device/${ Data.stations[ 0 ].devices[ i ].device_id }?token=${ Token }", contentType: "application/json" ] Logging( "Params for device: ${ Data.stations[ 0 ].devices[ i ].device_id } = ${ Params }", 4 ) asynchttpGet( "GetDeviceData", Params, [ Method: "GetDeviceData", Device: "${ Data.stations[ 0 ].devices[ i ].device_id }" ] ) } } } } Params = [ uri: "https://swd.weatherflow.com/swd/rest/observations/station/${ StationID }?token=${ Token }", contentType: "application/json" ] Logging( "Station Obs Params = ${ Params }", 4 ) asynchttpGet( "GetWeatherData", Params, [ Method: "GetWeatherData" ] ) } else { Logging( "No data returned by WeatherFlow", 5 ) } break case 401: Logging( "Unauthorized, most likely due to invalid token.", 5 ) break case 404: Logging( "Station not found.", 5 ) break default: Logging( "Error connecting to WeatherFlow: ${ resp.status }", 5 ) break } } // Handles the response from WeatherFlow observational data def GetWeatherData( resp, data ){ switch( resp.getStatus() ){ case 200: Logging( "Obs data raw response: ${ resp.data }", 4 ) Data = parseJson( resp.data ) if( Data != null ){ ParseWeather( Data ) } else { Logging( "No obs data returned by WeatherFlow", 5 ) } break case 401: Logging( "Unauthorized, most likely due to invalid token when getting obs data.", 5 ) break case 404: Logging( "Station not found for obs data.", 5 ) break default: Logging( "Error connecting to WeatherFlow when getting obs data: ${ resp.status }", 5 ) break } } // Handles the response from WeatherFlow for Device Data def GetDeviceData( resp, data ){ if( resp != null ){ switch( resp.getStatus() ){ case 200: Data = parseJson( resp.data ) Logging( "Device ${ data.Device } response: ${ Data }", 4 ) if( Data != null ){ ParseDevice( Data ) } else { Logging( "No data returned by WeatherFlow for Device ${ data.Device }", 5 ) } break case 401: Logging( "Unauthorized, most likely due to invalid token when getting Device ${ data.Device } data.", 5 ) break case 404: Logging( "Device ${ data.Device } not found.", 5 ) break default: Logging( "Error connecting to WeatherFlow for Device ${ data.Device } data: ${ resp.status }", 5 ) break } } } // Parse the station data returned, output events and populate attributes as needed def ParseStation( Data ){ if( Data != null ){ if( Data.stations != null ){ if( Data.stations[ 0 ] != null ){ if( Data.stations[ 0 ].devices != null ){ def TempInfo = new int[ Data.stations[ 0 ].devices.size() ] for( int i = 0; i < Data.stations[ 0 ].devices.size(); i++ ){ def DeviceID if( Data.stations[ 0 ].devices[ i ].device_id != null ){ // device_id DeviceID = Data.stations[ 0 ].devices[ i ].device_id TempInfo[ i ] = DeviceID PostStateToChild( "${ DeviceID }", "device_id", DeviceID ) } if( Data.stations[ 0 ].devices[ i ].hardware_revision != null ){ // hardware_revision PostStateToChild( "${ DeviceID }", "hardware_revision", Data.stations[ 0 ].devices[ i ].hardware_revision ) } if( Data.stations[ 0 ].devices[ i ].firmware_revision != null ){ // firmware_revision PostStateToChild( "${ DeviceID }", "firmware_revision", Data.stations[ 0 ].devices[ i ].firmware_revision ) } if( Data.stations[ 0 ].devices[ i ].device_meta.environment != null ){ // Environment PostStateToChild( "${ DeviceID }", "environment", Data.stations[ 0 ].devices[ i ].device_meta.environment ) } if( Data.stations[ 0 ].devices[ i ].device_meta.agl != null ){ // agl PostStateToChild( "${ DeviceID }", "agl", Data.stations[ 0 ].devices[ i ].device_meta.agl ) } if( Data.stations[ 0 ].devices[ i ].device_meta.wifi_network_name != null ){ // wifi_network_name PostStateToChild( "${ DeviceID }", "wifi_network_name", Data.stations[ 0 ].devices[ i ].device_meta.wifi_network_name ) } if( Data.stations[ 0 ].devices[ i ].device_meta.name != null ){ // Device name PostStateToChild( "${ DeviceID }", "name", Data.stations[ 0 ].devices[ i ].device_meta.name ) } if( Data.stations[ 0 ].devices[ i ].serial_number != null ){ // serial_number PostStateToChild( "${ DeviceID }", "serial_number", Data.stations[ 0 ].devices[ i ].serial_number ) } if( Data.stations[ 0 ].devices[ i ].device_type != null ){ // device_type PostStateToChild( "${ DeviceID }", "Device Type", Data.stations[ 0 ].devices[ i ].device_type ) if( ChildrenEnabled ){ if( getChildDevice( "${ DeviceID }" ) != null ){ if( getChildDevice( "${ DeviceID }" ).label == null ){ if( Data.stations[ 0 ].devices[ i ].device_type == "HB" ){ getChildDevice( "${ DeviceID }" ).label = "Hub" } else if( Data.stations[ 0 ].devices[ i ].device_type == "ST" ){ getChildDevice( "${ DeviceID }" ).label = "Station" } else if( Data.stations[ 0 ].devices[ i ].device_type == "AR" ){ getChildDevice( "${ DeviceID }" ).label = "Air" } else if( Data.stations[ 0 ].devices[ i ].device_type == "SK" ){ getChildDevice( "${ DeviceID }" ).label = "Sky" } } } } } } // Copy the TempInfo (array of device_ids) over to the state.SensorIDs state.SensorIDs = TempInfo if( Data.stations[ 0 ].timezone != null ){ // timezone ProcessState( "Timezone", Data.stations[ 0 ].timezone ) } if( Data.stations[ 0 ].station_meta.elevation != null ){ // elevation ProcessState( "Elevation", Data.stations[ 0 ].station_meta.elevation ) } if( Data.stations[ 0 ].station_meta.share_with_wf ){ // Shared with WeatherFlow ProcessState( "Shared with WeatherFlow", "true" ) } else { ProcessState( "Shared with WeatherFlow", "false" ) } if( Data.stations[ 0 ].station_meta.share_with_wu ){ // Shared with WeatherUnderground ProcessState( "Shared with WeatherUnderground", "true" ) } else { ProcessState( "Shared with WeatherUnderground", "false" ) } if( Data.stations[ 0 ].latitude != null ){ // latitude ProcessState( "Latitude", Data.stations[ 0 ].latitude ) } if( Data.stations[ 0 ].station_items != null ){ // station_items for( int i = 0; i < Data.stations[ 0 ].station_items.size(); i++ ){ //if( ( Data.stations[ 0 ].station_items[ i ].item != "diagnostics" ) && ( Data.stations[ 0 ].station_items[ i ].item != "forecast" ) ){ // ProcessState( "${ Data.stations[ 0 ].station_items[ i ].item }_Sensor", Data.stations[ 0 ].station_items[ i ].device_id ) //} if( Data.stations[ 0 ].station_items[ i ].device_id != null ){ PostStateToChild( "${ Data.stations[ 0 ].station_items[ i ].device_id }", "${ Data.stations[ 0 ].station_items[ i ].item }_Sensor", "true" ) PostStateToChild( "${ Data.stations[ 0 ].station_items[ i ].device_id }", "location_item_id", Data.stations[ 0 ].station_items[ i ].location_item_id ) } } } if( Data.stations[ 0 ].is_local_mode != null ){ // is_local_mode ProcessState( "LocalMode", "${ Data.stations[ 0 ].is_local_mode }" ) } if( Data.stations[ 0 ].name != null ){ // name ProcessState( "StationName", Data.stations[ 0 ].name ) } if( Data.stations[ 0 ].messages != null ){ // station_items ProcessState( "Messages", Data.stations[ 0 ].messages ) } if( Data.stations[ 0 ].last_modified_epoch != null ){ // last_modified_epoch ProcessState( "LastModifiedEpoch", Data.stations[ 0 ].last_modified_epoch ) ProcessState( "LastModifiedString", ConvertEpochToDate( Data.stations[ 0 ].last_modified_epoch ) ) } if( Data.stations[ 0 ].public_name != null ){ // public_name ProcessState( "PublicName", Data.stations[ 0 ].public_name ) } if( Data.stations[ 0 ].longitude != null ){ // longitude ProcessState( "Longitude", Data.stations[ 0 ].longitude ) } } } } } } // Parse the data returned, output events and populate attributes as needed def ParseWeather( Data ){ Logging( "Weather data = ${ Data }", 4 ) // For development purposes, to see if new attributes show up if( Data.station_name != null ){ // Station name ProcessState( "StationName", Data.station_name ) } if( Data.elevation != null ){ // Station elevation ProcessState( "Elevation", Data.elevation ) } if( Data.latitude != null ){ // Station latitude ProcessState( "Latitude", Data.latitude ) } if( Data.longitude != null ){ // Station longitude ProcessState( "Longitude", Data.longitude ) } if( Data.timezone != null ){ // Station time zone ProcessState( "TimeZone", Data.timezone ) } if( Data.is_public != null ){ // Station is public ProcessState( "Public", "${ Data.is_public }" ) } if( Data.station_units != null ){ if( Data.station_units.units_temp != null ){ // Station units temp ProcessState( "UnitsTemp", Data.station_units.units_temp ) } if( Data.station_units.units_wind != null ){ // Station units wind ProcessState( "UnitsWind", Data.station_units.units_wind ) } if( Data.station_units.units_precip != null ){ // Station units precip ProcessState( "UnitsPrecip", Data.station_units.units_precip ) } if( Data.station_units.units_pressure != null ){ // Station units pressure ProcessState( "UnitsPressure", Data.station_units.units_pressure ) } if( Data.station_units.units_distance != null ){ // Station units distance ProcessState( "UnitsDistance", Data.station_units.units_distance ) } if( Data.station_units.units_direction != null ){ // Station units direction ProcessState( "UnitsDirection", Data.station_units.units_direction ) } if( Data.station_units.units_other != null ){ // Station units other ProcessState( "UnitsOther", Data.station_units.units_other ) } } if( Data.status != null ){ if( Data.status.status_code != null ){ // Station status code ProcessState( "StatusCode", Data.status.status_code ) } if( Data.status.status_message != null ){ // Station status message ProcessState( "StatusMessage", Data.status.status_message ) } } def TempID = null def ParentSensorID = null if( Data.device_id != null ){ // device_id TempID = Data.device_id as int } else if( Data.station_id != null ){ TempID = Data.station_id as int } if( state.ParentSensorID != null ){ ParentSensorID = state.ParentSensorID as int } Logging( "TempID = ${ TempID } & ParentSensorID = ${ ParentSensorID }", 4 ) if( TempID != null ){ Data.obs[ 0 ].each{ switch( it.key ){ case "timestamp": if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "TimestampEpoch", it.value ) ProcessEvent( "TimestampString", ConvertEpochToDate( it.value ) ) } PostEventToChild( "${ TempID }", "TimestampEpoch", it.value ) PostEventToChild( "${ TempID }", "TimestampString", ConvertEpochToDate( it.value ) ) break case "air_temperature": if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "AirTemperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) ProcessEvent( "temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "AirTemperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "${ TempID }", "temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "air_temperature_indoor": if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "AirTemperatureIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "AirTemperatureIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "${ TempID }", "temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "barometric_pressure": if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressure", it.value, "mb" ) ProcessEvent( "pressure", it.value, "mb" ) } PostEventToChild( "${ TempID }", "BarometricPressure", it.value, "mb" ) PostEventToChild( "${ TempID }", "pressure", it.value, "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressure", ConvertPressure( "Metric", it.value ), "psi" ) ProcessEvent( "pressure", ConvertPressure( "Metric", it.value ), "psi" ) } PostEventToChild( "${ TempID }", "BarometricPressure", ConvertPressure( "Metric", it.value ), "psi" ) PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", it.value ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressure", ConvertPressure( "Metric", it.value ), "inHG" ) ProcessEvent( "pressure", ConvertPressure( "Metric", it.value ), "inHG" ) } PostEventToChild( "${ TempID }", "BarometricPressure", ConvertPressure( "Metric", it.value ), "inHG" ) PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", it.value ), "inHG" ) } break case "barometric_pressure_indoor": if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressureIndoor", it.value, "mb" ) } PostEventToChild( "${ TempID }", "pressure", it.value, "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressureIndoor", ConvertPressure( "Metric", it.value ), "psi" ) } PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", it.value ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "BarometricPressureIndoor", ConvertPressure( "Metric", it.value ), "inHG" ) } PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", it.value ), "inHG" ) } break case "station_pressure": if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", it.value, "mb" ) } PostEventToChild( "${ TempID }", "StationPressure", it.value, "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", ConvertPressure( "Metric", it.value ), "psi" ) } PostEventToChild( "${ TempID }", "StationPressure", ConvertPressure( "Metric", it.value ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", ConvertPressure( "Metric", it.value ), "inHG" ) } PostEventToChild( "${ TempID }", "StationPressure", ConvertPressure( "Metric", it.value ), "inHG" ) } break case "sea_level_pressure": if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressure", it.value, "mb" ) } PostEventToChild( "${ TempID }", "SeaLevelPressure", it.value, "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressure", ConvertPressure( "Metric", it.value ), "psi" ) } PostEventToChild( "${ TempID }", "SeaLevelPressure", ConvertPressure( "Metric", it.value ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressure", ConvertPressure( "Metric", it.value ), "inHG" ) } PostEventToChild( "${ TempID }", "SeaLevelPressure", ConvertPressure( "Metric", it.value ), "inHG" ) } break case "sea_level_pressure_indoor": if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressureIndoor", it.value, "mb" ) } PostEventToChild( "${ TempID }", "SeaLevelPressureIndoor", it.value, "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressureIndoor", ConvertPressure( "Metric", it.value ), "psi" ) } PostEventToChild( "${ TempID }", "SeaLevelPressureIndoor", ConvertPressure( "Metric", it.value ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SeaLevelPressureIndoor", ConvertPressure( "Metric", it.value ), "inHG" ) } PostEventToChild( "${ TempID }", "SeaLevelPressureIndoor", ConvertPressure( "Metric", it.value ), "inHG" ) } break case "relative_humidity": // Outdoor relative humidity if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RelativeHumidity", it.value, "%" ) ProcessEvent( "humidity", it.value, "%" ) } PostEventToChild( "${ TempID }", "RelativeHumidity", it.value, "%" ) PostEventToChild( "${ TempID }", "humidity", it.value, "%" ) break case "relative_humidity_indoor": // Indoor relative humidity if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RelativeHumidityIndoor", it.value, "%" ) } PostEventToChild( "${ TempID }", "RelativeHumidityIndoor", it.value, "%" ) break case "precip": // precip if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "Precip", it.value, "mm" ) } PostEventToChild( "${ TempID }", "Precip", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "Precip", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "Precip", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_accum_last_1hr": // precip accum last 1hr if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLast1hr", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipAccumLast1hr", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLast1hr", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipAccumLast1hr", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_accum_local_day_final": // if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalDayFinal", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalDayFinal", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalDayFinal", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalDayFinal", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_accum_local_day": // precip accum local day if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalDay", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalDay", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalDay", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalDay", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_accum_local_yesterday": // precip accum local yesterday if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterday", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterday", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterday", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterday", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_accum_local_yesterday_final": // precip accum local yesterday final if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterdayFinal", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterdayFinal", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterdayFinal", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterdayFinal", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_minutes_local_day": // precip minutes local day if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalDay", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalDay", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalDay", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalDay", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_minutes_local_yesterday": // precip minutes local yesterday if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalYesterday", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalYesterday", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalYesterday", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalYesterday", ConvertInches( "Metric", it.value ), "inches" ) } break case "precip_minutes_local_yesterday_final": // precip minutes local yesterday final if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalYesterdayFinal", it.value, "mm" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalYesterdayFinal", it.value, "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipMinutesLocalYesterdayFinal", ConvertInches( "Metric", it.value ), "inches" ) } PostEventToChild( "${ TempID }", "PrecipMinutesLocalYesterdayFinal", ConvertInches( "Metric", it.value ), "inches" ) } break case "wind_avg": // wind avg if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", it.value, "m/s" ) ProcessEvent( "windSpeed", it.value, "m/s" ) } PostEventToChild( "${ TempID }", "WindAvg", it.value, "m/s" ) PostEventToChild( "${ TempID }", "windSpeed", it.value, "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", ConvertMetersSecond( "Metric", it.value ), "mph" ) ProcessEvent( "windSpeed", it.value, "mph" ) } PostEventToChild( "${ TempID }", "WindAvg", it.value, "mph" ) PostEventToChild( "${ TempID }", "windSpeed", it.value, "mph" ) } break case "wind_gust": // wind gust if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", it.value, "m/s" ) } PostEventToChild( "${ TempID }", "windGust", it.value, "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", ConvertMetersSecond( "Metric", it.value ), "mph" ) } PostEventToChild( "${ TempID }", "windGust", it.value, "mph" ) } break case "wind_lull": // wind lull if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windLull", it.value, "m/s" ) } PostEventToChild( "${ TempID }", "windLull", it.value, "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windLull", ConvertMetersSecond( "Metric", it.value ), "mph" ) } PostEventToChild( "${ TempID }", "windLull", it.value, "mph" ) } break case "wind_direction": // wind direction if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windDirection", it.value, "°" ) ProcessEvent( "WindDirectionString", MakeWindDirectionString( it.value ) ) } PostEventToChild( "${ TempID }", "windDirection", it.value, "°" ) PostEventToChild( "${ TempID }", "WindDirectionString", MakeWindDirectionString( it.value ) ) break case "solar_radiation": // solar radiation if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SolarRadiation", it.value ) } PostEventToChild( "${ TempID }", "SolarRadiation", it.value ) break case "uv": // Outdoor uv if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "ultravioletIndex", it.value, "uvi" ) } PostEventToChild( "${ TempID }", "ultravioletIndex", it.value, "uvi" ) break case "uv_indoor": // Indoor uv if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "ultravioletIndexIndoor", it.value, "uvi" ) } PostEventToChild( "${ TempID }", "ultravioletIndexIndoor", state.ultravioletIndexIndoor, "uvi" ) break case "brightness": // Outdoor brightness if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "illuminance", it.value, "lux" ) } PostEventToChild( "${ TempID }", "illuminance", it.value, "lux" ) break case "brightness_indoor": // Indoor brightness if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "illuminanceIndoor", it.value, "lux" ) } PostEventToChild( "${ TempID }", "illuminanceIndoor", state.illuminanceIndoor, "lux" ) break case "lightning_strike_last_epoch": // lightning strike last epoch if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "LightningStrikeLastEpoch", it.value ) ProcessEvent( "LightningStrikeLastDate", ConvertEpochToDate( it.value ) ) } PostStateToChild( "${ TempID }", "LightningStrikeLastEpoch", it.value ) PostEventToChild( "${ TempID }", "LightningStrikeLastDate", ConvertEpochToDate( it.value ) ) break case "lightning_strike_last_distance": // lightning strike last distance if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeLastDistance", it.value, "km" ) } PostEventToChild( "${ TempID }", "LightningStrikeLastDistance", it.value, "km" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeLastDistance", ConvertMiles( "Metric", it.value ), "miles" ) } PostEventToChild( "${ TempID }", "LightningStrikeLastDistance", ConvertMiles( "Metric", it.value ), "miles" ) } break case "lightning_strike_count": // lightning strike count if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCount", it.value ) } PostEventToChild( "${ TempID }", "LightningStrikeCount", it.value ) break case "lightning_strike_count_last_1hr": // lightning strike count last 1hr if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCountLast1hr", it.value ) } PostEventToChild( "${ TempID }", "LightningStrikeCountLast1hr", it.value ) break case "lightning_strike_count_last_3hr": // lightning strike count last 3hr if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCountLast3hr", it.value ) } PostEventToChild( "${ TempID }", "LightningStrikeCountLast3hr", it.value ) break case "feels_like": // Outdoor feels like if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "feelsLike", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "feelsLike", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "feels_like_indoor": // Indoor feels like if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "feelsLikein", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "feelsLikein", state.feelsLikein, "°${ location.getTemperatureScale() }" ) break case "heat_index": // Outdoor heat index if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "HeatIndex", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "HeatIndex", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "heat_index_indoor": // Indoor heat index if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "HeatIndexIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "HeatIndexIndoor", state.HeatIndexIndoor, "°${ location.getTemperatureScale() }" ) break case "wind_chill": // Outdoor wind chill if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindChill", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "WindChill", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "wind_chill_indoor": // Indoor wind chill if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindChillIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "WindChillIndoor", state.WindChillIndoor, "°${ location.getTemperatureScale() }" ) break case "dew_point": // Outdoor dew point if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "dewPoint", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "dewPoint", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "dew_point_indoor": // Indoor dew point if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "dewPointIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "dewPointIndoor", state.dewPointIndoor, "°${ location.getTemperatureScale() }" ) break case "wet_bulb_temperature": // Outdoor wet bulb temperature if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WetBulbTemperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "WetBulbTemperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "wet_bulb_temperature_indoor": // Indoor wet bulb temperature if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WetBulbTemperatureIndoor", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostStateToChild( "${ TempID }", "WetBulbTemperatureIndoor", state.WetBulbTemperatureIndoor ) break case "wet_bulb_globe_temperature": // if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WetBulbGlobeTemperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) } PostStateToChild( "${ TempID }", "WetBulbGlobeTemperature", state.WetBulbGlobeTemperature ) break case "delta_t": // delta t if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "DeltaT", it.value ) } PostStateToChild( "${ TempID }", "DeltaT", state.DeltaT ) break case "air_density": // air density if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "AirDensity", it.value ) } PostStateToChild( "${ TempID }", "AirDensity", state.AirDensity ) break case "precip_analysis_type_yesterday": // precip_analysis_type_yesterday if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAnalysisTypeYesterday", it.value ) } PostStateToChild( "${ TempID }", "PrecipAnalysisTypeYesterday", state.PrecipAnalysisTypeYesterday ) break case "pressure_trend": // pressure_trend if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PressureTrend", "${ it.value }" ) } PostStateToChild( "${ TempID }", "PressureTrend", state."PressureTrend" ) break default: Logging( "Unhandled data ${ it.key } = ${ it.value }", 3 ) break } } } } // Parse the station data returned, output events and populate attributes as needed def ParseDevice( Data ){ Logging( "Device data = ${ Data }", 4 ) // For development purposes, to see if new attributes show up def TempID = null def ParentSensorID = null if( Data.device_id != null ){ // device_id TempID = Data.device_id as int } if( state.ParentSensorID != null ){ ParentSensorID = state.ParentSensorID as int } if( TempID != null ){ if( Data.summary != null ){ if( Data.summary.heat_index != null ){ // heat_index if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "HeatIndex", ConvertTemperature( "C", Data.summary.heat_index ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "HeatIndex", ConvertTemperature( "C", Data.summary.heat_index ), "°${ location.getTemperatureScale() }" ) } if( Data.summary.pressure_trend != null ){ // pressure_trend if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PressureTrend", Data.summary.pressure_trend ) } PostEventToChild( "${ TempID }", "PressureTrend", Data.summary.pressure_trend ) } if( Data.summary.wind_chill != null ){ // wind_chill if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindChill", ConvertTemperature( "C", Data.summary.wind_chill ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "WindChill", ConvertTemperature( "C", Data.summary.wind_chill ), "°${ location.getTemperatureScale() }" ) } if( Data.summary.strike_last_dist != null ){ // strike_last_dist if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeLastDistance", Data.summary.strike_last_dist, "km" ) } PostEventToChild( "${ TempID }", "LightningStrikeLastDistance", Data.summary.strike_last_dist, "km" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeLastDistance", ConvertMiles( "Metric", Data.summary.strike_last_dist ), "miles" ) } PostEventToChild( "${ TempID }", "LightningStrikeLastDistance", ConvertMiles( "Metric", Data.summary.strike_last_dist ), "miles" ) } } if( Data.summary.strike_count_3h != null ){ // strike_count_3h if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCountLast3hr", Data.summary.strike_count_3h ) } PostEventToChild( "${ TempID }", "LightningStrikeCountLast3hr", Data.summary.strike_count_3h ) } if( Data.summary.feels_like != null ){ // feels_like if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "feelsLike", ConvertTemperature( "C", Data.summary.feels_like ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "feelsLike", ConvertTemperature( "C", Data.summary.feels_like ), "°${ location.getTemperatureScale() }" ) } if( Data.summary.strike_last_epoch != null ){ // strike_last_epoch if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeLastEpoch", Data.summary.strike_last_epoch ) ProcessEvent( "LightningStrikeLastDate", ConvertEpochToDate( Data.summary.strike_last_epoch ) ) } PostEventToChild( "${ TempID }", "LightningStrikeLastEpoch", Data.summary.strike_last_epoch ) PostEventToChild( "${ TempID }", "LightningStrikeLastDate", ConvertEpochToDate( Data.summary.strike_last_epoch ) ) } if( Data.summary.precip_accum_local_yesterday_final != null ){ // precip_accum_local_yesterday_final if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterdayFinal", Data.summary.precip_accum_local_yesterday_final ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterdayFinal", Data.summary.precip_accum_local_yesterday_final ) } if( Data.summary.precip_analysis_type_yesterday != null ){ // precip_analysis_type_yesterday if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAnalysisTypeYesterday", Data.summary.precip_analysis_type_yesterday ) } PostEventToChild( "${ TempID }", "PrecipAnalysisTypeYesterday", Data.summary.precip_analysis_type_yesterday ) } if( Data.summary.precip_accum_local_yesterday != null ){ // precip_accum_local_yesterday if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipAccumLocalYesterday", Data.summary.precip_accum_local_yesterday ) } PostEventToChild( "${ TempID }", "PrecipAccumLocalYesterday", Data.summary.precip_accum_local_yesterday ) } if( Data.summary.precip_total_1h != null ){ // precip_total_1h if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipTotal1h", Data.summary.precip_total_1h ) } PostEventToChild( "${ TempID }", "PrecipTotal1h", Data.summary.precip_total_1h ) } } if( Data.type == "obs_air" ){ if( Data.obs[ 0 ][ 0 ] != null ){ // 0 - Epoch (seconds UTC) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) ProcessEvent( "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } PostEventToChild( "${ TempID }", "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) PostEventToChild( "${ TempID }", "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } if( Data.obs[ 0 ][ 1 ] != null ){ // 1 - Station Pressure (MB) if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", Data.obs[ 0 ][ 1 ], "mb" ) } PostEventToChild( "${ TempID }", "StationPressure", Data.obs[ 0 ][ 1 ], "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 1 ] ), "psi" ) } PostEventToChild( "${ TempID }", "StationPressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 1 ] ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "StationPressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 1 ] ), "inHG" ) } PostEventToChild( "${ TempID }", "StationPressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 1 ] ), "inHG" ) } } if( Data.obs[ 0 ][ 2 ] != null ){ // 2 - Air Temperature (C) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "AirTemperature", ConvertTemperature( "C", Data.obs[ 0 ][ 2 ] ), "°${ location.getTemperatureScale() }" ) ProcessEvent( "temperature", ConvertTemperature( "C", Data.obs[ 0 ][ 2 ] ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "AirTemperature", ConvertTemperature( "C", Data.obs[ 0 ][ 2 ] ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "${ TempID }", "temperature", ConvertTemperature( "C", Data.obs[ 0 ][ 2 ] ), "°${ location.getTemperatureScale() }" ) } if( Data.obs[ 0 ][ 3 ] != null ){ // 3 - Relative Humidity (%) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RelativeHumidity", Data.obs[ 0 ][ 3 ], "%" ) ProcessEvent( "humidity", Data.obs[ 0 ][ 3 ], "%" ) } PostEventToChild( "${ TempID }", "RelativeHumidity", Data.obs[ 0 ][ 3 ], "%" ) PostEventToChild( "${ TempID }", "humidity", Data.obs[ 0 ][ 3 ], "%" ) } if( Data.obs[ 0 ][ 4 ] != null ){ // 4 - Lightning Strike Count if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCount", Data.obs[ 0 ][ 4 ] ) } PostEventToChild( "${ TempID }", "LightningStrikeCount", Data.obs[ 0 ][ 4 ] ) } if( Data.obs[ 0 ][ 5 ] != null ){ // 5 - Lightning Strike Average Distance (km) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeAverageDistance", Data.obs[ 0 ][ 5 ], "km" ) } PostEventToChild( "${ TempID }", "LightningStrikeAverageDistance", Data.obs[ 0 ][ 5 ], "km" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeAverageDistance", ConvertMiles( "Metric", Data.obs[ 0 ][ 5 ] ), "miles" ) } PostEventToChild( "${ TempID }", "LightningStrikeAverageDistance", ConvertMiles( "Metric", Data.obs[ 0 ][ 5 ] ), "miles" ) } } if( Data.obs[ 0 ][ 6 ] != null ){ // 6 - Battery (volts) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_Battery", Data.obs[ 0 ][ 6 ], "volts" ) } PostStateToChild( "${ TempID }", "BatteryVoltage", Data.obs[ 0 ][ 6 ] ) if( Data.obs[ 0 ][ 6 ] >= 2.455 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 100, "%" ) } PostEventToChild( "${ TempID }", "battery", 100, "%" ) } else if( Data.obs[ 0 ][ 6 ] >= 2.41 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 75, "%" ) } PostEventToChild( "${ TempID }", "battery", 75, "%" ) } else if( Data.obs[ 0 ][ 6 ] >= 2.375 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 50, "%" ) } PostEventToChild( "${ TempID }", "battery", 50, "%" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 25, "%" ) } PostEventToChild( "${ TempID }", "battery", 25, "%" ) } } if( Data.obs[ 0 ][ 7 ] != null ){ // 7 - Report Interval (minutes) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "${ TempID }_Interval", Data.obs[ 0 ][ 7 ] ) } PostStateToChild( "${ TempID }", "Interval", Data.obs[ 0 ][ 7 ] ) } } else if( Data.type == "obs_sky" ){ if( Data.obs[ 0 ][ 0 ] != null ){ // 0 - Epoch (seconds UTC) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) ProcessEvent( "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } PostEventToChild( "${ TempID }", "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) PostEventToChild( "${ TempID }", "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } if( Data.obs[ 0 ][ 1 ] != null ){ // 1 - Illuminance (lux) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "illuminance", Data.obs[ 0 ][ 1 ], "lux" ) } PostEventToChild( "${ TempID }", "illuminance", Data.obs[ 0 ][ 1 ], "lux" ) } if( Data.obs[ 0 ][ 2 ] != null ){ // 2 - UV (index) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "ultravioletIndex", Data.obs[ 0 ][ 2 ], "uvi" ) } PostEventToChild( "${ TempID }", "ultravioletIndex", Data.obs[ 0 ][ 2 ],, "uvi" ) } if( Data.obs[ 0 ][ 3 ] != null ){ // 3 - Rain Accumulation (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulation", Data.obs[ 0 ][ 3 ], "mm" ) } PostEventToChild( "${ TempID }", "RainAccumulation", Data.obs[ 0 ][ 3 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 3 ] ), "inches" ) } PostEventToChild( "${ TempID }", "RainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 3 ] ), "inches" ) } } if( Data.obs[ 0 ][ 4 ] != null ){ // 4 - Wind Lull (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindLull", Data.obs[ 0 ][ 4 ], "m/s" ) } PostEventToChild( "${ TempID }", "WindLull", Data.obs[ 0 ][ 4 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindLull", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 4 ] ), "mph" ) } PostEventToChild( "${ TempID }", "WindLull", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 4 ] ), "mph" ) } } if( Data.obs[ 0 ][ 5 ] != null ){ // 5 - Wind Avg (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", Data.obs[ 0 ][ 5 ], "m/s" ) ProcessEvent( "windSpeed", Data.obs[ 0 ][ 5 ], "m/s" ) } PostEventToChild( "${ TempID }", "WindAvg", Data.obs[ 0 ][ 5 ], "m/s" ) PostEventToChild( "${ TempID }", "windSpeed", Data.obs[ 0 ][ 5 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 5 ] ), "mph" ) ProcessEvent( "windSpeed", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 5 ] ), "mph" ) } PostEventToChild( "${ TempID }", "WindAvg", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 5 ] ), "mph" ) PostEventToChild( "${ TempID }", "windSpeed", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 5 ] ), "mph" ) } } if( Data.obs[ 0 ][ 6 ] != null ){ // 6 - Wind Gust (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", Data.obs[ 0 ][ 6 ], "m/s" ) } PostEventToChild( "${ TempID }", "windGust", Data.obs[ 0 ][ 6 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 6 ] ), "mph" ) } PostEventToChild( "${ TempID }", "windGust", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 6 ] ), "mph" ) } } if( Data.obs[ 0 ][ 7 ] != null ){ // 7 - Wind Direction (degrees) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windDirection", Data.obs[ 0 ][ 7 ], "°" ) ProcessEvent( "WindDirectionString", MakeWindDirectionString( Data.obs[ 0 ][ 7 ] ) ) } PostEventToChild( "${ TempID }", "windDirection", Data.obs[ 0 ][ 7 ], "°" ) PostEventToChild( "${ TempID }", "WindDirectionString", MakeWindDirectionString( Data.obs[ 0 ][ 7 ] ) ) } if( Data.obs[ 0 ][ 8 ] != null ){ // 8 - Battery (volts) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_Battery", Data.obs[ 0 ][ 8 ], "volts" ) } PostStateToChild( "${ TempID }", "BatteryVoltage", Data.obs[ 0 ][ 6 ] ) if( Data.obs[ 0 ][ 8 ] >= 2.455 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 100, "%" ) } PostEventToChild( "${ TempID }", "battery", 100, "%" ) } else if( Data.obs[ 0 ][ 8 ] >= 2.41 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 75, "%" ) } PostEventToChild( "${ TempID }", "battery", 75, "%" ) } else if( Data.obs[ 0 ][ 8 ] >= 2.375 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 50, "%" ) } PostEventToChild( "${ TempID }", "battery", 50, "%" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 25, "%" ) } PostEventToChild( "${ TempID }", "battery", 25, "%" ) } } if( Data.obs[ 0 ][ 9 ] != null ){ // 9 - Report Interval (minutes) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "${ TempID }_Interval", Data.obs[ 0 ][ 9 ] ) } PostStateToChild( "${ TempID }", "Interval", Data.obs[ 0 ][ 9 ] ) } if( Data.obs[ 0 ][ 10 ] != null ){ // 10 - Solar Radiation (W/m^2) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SolarRadiation", Data.obs[ 0 ][ 10 ], "W/m^2" ) } PostEventToChild( "${ TempID }", "SolarRadiation", Data.obs[ 0 ][ 10 ], "W/m^2" ) } if( Data.obs[ 0 ][ 11 ] != null ){ // 11 - Local Day Rain Accumulation (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulation", Data.obs[ 0 ][ 11 ], "mm" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulation", Data.obs[ 0 ][ 11 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 11 ] ), "inches" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 11 ] ), "inches" ) } } if( Data.obs[ 0 ][ 12 ] != null ){ // 12 - Precipitation Type (0 = none, 1 = rain, 2 = hail, 3 = rain + hail) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationType", Data.obs[ 0 ][ 12 ] ) } PostEventToChild( "${ TempID }", "PrecipitationType", Data.obs[ 0 ][ 12 ] ) switch( Data.obs[ 0 ][ 12 ] ){ case 0: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "none" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "none" ) break case 1: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "rain" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "rain" ) break case 2: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "hail" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "hail" ) break case 3: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "rain + hail" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "rain + hail" ) break } } if( Data.obs[ 0 ][ 13 ] != null ){ // 13 - Wind Sample Interval (seconds) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "${ TempID }_WindSampleInterval", Data.obs[ 0 ][ 13 ] ) } PostStateToChild( "${ TempID }", "WindSampleInterval", Data.obs[ 0 ][ 13 ] ) } if( Data.obs[ 0 ][ 14 ] != null ){ // 14 - Rain Accumulation Final (Rain Check) (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulationFinal", Data.obs[ 0 ][ 14 ], "mm" ) } PostEventToChild( "${ TempID }", "RainAccumulationFinal", Data.obs[ 0 ][ 14 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 14 ] ), "inches" ) } PostEventToChild( "${ TempID }", "RainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 14 ] ), "inches" ) } } if( Data.obs[ 0 ][ 15 ] != null ){ // 15 - Local Day Rain Accumulation Final (Rain Check) (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulationFinal", Data.obs[ 0 ][ 15 ], "mm" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulationFinal", Data.obs[ 0 ][ 15 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 15 ] ), "inches" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 15 ] ), "inches" ) } } if( Data.obs[ 0 ][ 16 ] != null ){ // 16 - Precipitation Analysis Type (0 = none, 1 = Rain Check with user display on, 2 = Rain Check with user display off) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisType", Data.obs[ 0 ][ 16 ] ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisType", Data.obs[ 0 ][ 16 ] ) switch( Data.obs[ 0 ][ 16 ] ){ case 0: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "none" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "none" ) break case 1: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "Rain Check with user display on" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "Rain Check with user display on" ) break case 2: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "Rain Check with user display off" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "Rain Check with user display off" ) break } } } else if( Data.type == "obs_st" ){ if( Data.obs[ 0 ][ 0 ] != null ){ // 0 - Epoch (seconds UTC) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) ProcessEvent( "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } PostEventToChild( "${ TempID }", "TimestampEpoch", Data.obs[ 0 ][ 0 ] ) PostEventToChild( "${ TempID }", "TimestampString", ConvertEpochToDate( Data.obs[ 0 ][ 0 ] ) ) } if( Data.obs[ 0 ][ 1 ] != null ){ // 1 - Wind Lull (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindLull", Data.obs[ 0 ][ 1 ], "m/s" ) } PostEventToChild( "${ TempID }", "WindLull", Data.obs[ 0 ][ 1 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindLull", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 1 ] ), "mph" ) } PostEventToChild( "${ TempID }", "WindLull", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 1 ] ), "mph" ) } } if( Data.obs[ 0 ][ 2 ] != null ){ // 2 - Wind Avg (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", Data.obs[ 0 ][ 2 ], "m/s" ) ProcessEvent( "windSpeed", Data.obs[ 0 ][ 2 ], "m/s" ) } PostEventToChild( "${ TempID }", "WindAvg", Data.obs[ 0 ][ 2 ], "m/s" ) PostEventToChild( "${ TempID }", "windSpeed", Data.obs[ 0 ][ 2 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "WindAvg", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 2 ] ), "mph" ) ProcessEvent( "windSpeed", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 2 ] ), "mph" ) } PostEventToChild( "${ TempID }", "WindAvg", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 2 ] ), "mph" ) PostEventToChild( "${ TempID }", "windSpeed", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 2 ] ), "mph" ) } } if( Data.obs[ 0 ][ 3 ] != null ){ // 3 - Wind Gust (m/s) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", Data.obs[ 0 ][ 3 ], "m/s" ) } PostEventToChild( "${ TempID }", "windGust", Data.obs[ 0 ][ 3 ], "m/s" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windGust", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 3 ] ), "mph" ) } PostEventToChild( "${ TempID }", "windGust", ConvertMetersSecond( "Metric", Data.obs[ 0 ][ 3 ] ), "mph" ) } } if( Data.obs[ 0 ][ 4 ] != null ){ // 4 - Wind Direction (degrees) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "windDirection", Data.obs[ 0 ][ 4 ], "°" ) ProcessEvent( "WindDirectionString", MakeWindDirectionString( Data.obs[ 0 ][ 4 ] ) ) } PostEventToChild( "${ TempID }", "windDirection", Data.obs[ 0 ][ 4 ], "°" ) PostEventToChild( "${ TempID }", "WindDirectionString", MakeWindDirectionString( Data.obs[ 0 ][ 4 ] ) ) } if( Data.obs[ 0 ][ 5 ] != null ){ // 5 - Wind Sample Interval (seconds) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "${ TempID }_WindSampleInterval", Data.obs[ 0 ][ 5 ] ) } PostStateToChild( "${ TempID }", "WindSampleInterval", Data.obs[ 0 ][ 5 ] ) } if( Data.obs[ 0 ][ 6 ] != null ){ // 6 - Pressure (MB) if( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "pressure", Data.obs[ 0 ][ 6 ], "mb" ) } PostEventToChild( "${ TempID }", "pressure", Data.obs[ 0 ][ 6 ], "mb" ) } else if( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "pressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 6 ] ), "psi" ) } PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 6 ] ), "psi" ) } else if( PressureUnits == "inHG" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "pressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 6 ] ), "inHG" ) } PostEventToChild( "${ TempID }", "pressure", ConvertPressure( "Metric", Data.obs[ 0 ][ 6 ] ), "inHG" ) } } if( Data.obs[ 0 ][ 7 ] != null ){ // 7 - Air Temperature (C) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "AirTemperature", ConvertTemperature( "C", Data.obs[ 0 ][ 7 ] ), "°${ location.getTemperatureScale() }" ) ProcessEvent( "temperature", ConvertTemperature( "C", Data.obs[ 0 ][ 7 ] ), "°${ location.getTemperatureScale() }" ) } PostEventToChild( "${ TempID }", "AirTemperature", ConvertTemperature( "C", Data.obs[ 0 ][ 7 ] ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "${ TempID }", "temperature", ConvertTemperature( "C", Data.obs[ 0 ][ 7 ] ), "°${ location.getTemperatureScale() }" ) } if( Data.obs[ 0 ][ 8 ] != null ){ // 8 - Relative Humidity (%) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RelativeHumidity", Data.obs[ 0 ][ 8 ], "%" ) ProcessEvent( "humidity", Data.obs[ 0 ][ 8 ], "%" ) } PostEventToChild( "${ TempID }", "RelativeHumidity", Data.obs[ 0 ][ 8 ], "%" ) PostEventToChild( "${ TempID }", "humidity", Data.obs[ 0 ][ 8 ], "%" ) } if( Data.obs[ 0 ][ 9 ] != null ){ // 9 - Illuminance (lux) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "illuminance", Data.obs[ 0 ][ 9 ], "lux" ) } PostEventToChild( "${ TempID }", "illuminance", Data.obs[ 0 ][ 9 ], "lux" ) } if( Data.obs[ 0 ][ 10 ] != null ){ // 10 - UV (index) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "ultravioletIndex", Data.obs[ 0 ][ 10 ], "uvi" ) } PostEventToChild( "${ TempID }", "ultravioletIndex", Data.obs[ 0 ][ 10 ], "uvi" ) } if( Data.obs[ 0 ][ 11 ] != null ){ // 11 - Solar Radiation (W/m^2) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "SolarRadiation", Data.obs[ 0 ][ 11 ], "W/m^2" ) } PostEventToChild( "${ TempID }", "SolarRadiation", Data.obs[ 0 ][ 11 ], "W/m^2" ) } if( Data.obs[ 0 ][ 12 ] != null ){ // 12 - Rain Accumulation (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulation", Data.obs[ 0 ][ 12 ], "mm" ) } PostEventToChild( "${ TempID }", "RainAccumulation", Data.obs[ 0 ][ 12 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 12 ] ), "inches" ) } PostEventToChild( "${ TempID }", "RainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 12 ] ), "inches" ) } } if( Data.obs[ 0 ][ 13 ] != null ){ // 13 - Precipitation Type (0 = none, 1 = rain, 2 = hail, 3 = rain + hail) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationType", Data.obs[ 0 ][ 13 ] ) } PostEventToChild( "${ TempID }", "PrecipitationType", Data.obs[ 0 ][ 13 ] ) switch( Data.obs[ 0 ][ 13 ] ){ case 0: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "none" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "none" ) break case 1: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "rain" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "rain" ) break case 2: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "hail" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "hail" ) break case 3: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationTypeString", "rain + hail" ) } PostEventToChild( "${ TempID }", "PrecipitationTypeString", "rain + hail" ) break } } if( Data.obs[ 0 ][ 14 ] != null ){ // 14 - Average Strike Distance (km) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeAverageDistance", Data.obs[ 0 ][ 14 ], "km" ) } PostEventToChild( "${ TempID }", "LightningStrikeAverageDistance", Data.obs[ 0 ][ 14 ], "km" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeAverageDistance", ConvertMiles( "Metric", Data.obs[ 0 ][ 14 ] ), "miles" ) } PostEventToChild( "${ TempID }", "LightningStrikeAverageDistance", ConvertMiles( "Metric", Data.obs[ 0 ][ 14 ] ), "miles" ) } } if( Data.obs[ 0 ][ 15 ] != null ){ // 15 - Strike Count if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LightningStrikeCount", Data.obs[ 0 ][ 15 ] ) } PostEventToChild( "${ TempID }", "LightningStrikeCount", Data.obs[ 0 ][ 15 ] ) } if( Data.obs[ 0 ][ 16 ] != null ){ // 16 - Battery (volts) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_Battery", Data.obs[ 0 ][ 16 ], "volts" ) } PostStateToChild( "${ TempID }", "Battery Voltage", Data.obs[ 0 ][ 16 ] ) if( Data.obs[ 0 ][ 16 ] >= 2.455 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 100, "%" ) } PostEventToChild( "${ TempID }", "battery", 100, "%" ) } else if( Data.obs[ 0 ][ 16 ] >= 2.41 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 75, "%" ) } PostEventToChild( "${ TempID }", "battery", 75, "%" ) } else if( Data.obs[ 0 ][ 16 ] >= 2.375 ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 50, "%" ) } PostEventToChild( "${ TempID }", "battery", 50, "%" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "${ TempID }_battery%", 25, "%" ) } PostEventToChild( "${ TempID }", "battery", 25, "%" ) } } if( Data.obs[ 0 ][ 17 ] != null ){ // 17 - Report Interval (minutes) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessState( "${ TempID }_Interval", Data.obs[ 0 ][ 17 ] ) } PostStateToChild( "${ TempID }", "Interval", Data.obs[ 0 ][ 17 ] ) } if( Data.obs[ 0 ][ 18 ] != null ){ // 18 - Local Day Rain Accumulation (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulation", Data.obs[ 0 ][ 18 ], "mm" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulation", Data.obs[ 0 ][ 18 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 18 ] ), "inches" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulation", ConvertInches( "Metric", Data.obs[ 0 ][ 18 ] ), "inches" ) } } if( Data.obs[ 0 ][ 19 ] != null ){ // 19 - Rain Accumulation Final (Rain Check) (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulationFinal", Data.obs[ 0 ][ 19 ], "mm" ) } PostEventToChild( "${ TempID }", "RainAccumulationFinal", Data.obs[ 0 ][ 19 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "RainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 19 ] ), "inches" ) } PostEventToChild( "${ TempID }", "RainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 19 ] ), "inches" ) } } if( Data.obs[ 0 ][ 20 ] != null ){ // 20 - Local Day Rain Accumulation Final (Rain Check) (mm) if( MeasurementStandard == "Metric" ){ if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulationFinal", Data.obs[ 0 ][ 20 ], "mm" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulationFinal", Data.obs[ 0 ][ 20 ], "mm" ) } else { if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "LocalDayRainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 20 ] ), "inches" ) } PostEventToChild( "${ TempID }", "LocalDayRainAccumulationFinal", ConvertInches( "Metric", Data.obs[ 0 ][ 20 ] ), "inches" ) } } if( Data.obs[ 0 ][ 21 ] != null ){ // 21 - Precipitation Aanalysis Type (0 = none, 1 = Rain Check with user display on, 2 = Rain Check with user display off) if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisType", Data.obs[ 0 ][ 21 ] ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisType", Data.obs[ 0 ][ 21 ] ) switch( Data.obs[ 0 ][ 21 ] ){ case 0: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "none" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "none" ) break case 1: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "Rain Check with user display on" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "Rain Check with user display on" ) break case 2: if( ( TempID == ParentSensorID ) || ( ParentSensorID == null ) ){ ProcessEvent( "PrecipitationAnalysisTypeString", "Rain Check with user display off" ) } PostEventToChild( "${ TempID }", "PrecipitationAnalysisTypeString", "Rain Check with user display off" ) break } } } } } //Poll WeatherFlow for forecast def PollForecast(){ if( Token != null ){ def Params Params = [ uri: "https://swd.weatherflow.com/swd/rest/better_forecast?station_id=${ StationID }&token=${ Token }", contentType: "application/json" ] Logging( "Forecast Params = ${ Params }", 4 ) asynchttpGet( "GetForecastData", Params) } else { Logging( "Token is required for WeatherFlow API.", 5 ) } } // Handles the forecast response from WeatherFlow def GetForecastData( resp, data ){ switch( resp.getStatus() ){ case 200: Logging( "Raw response: ${ resp.data }", 4 ) if( resp.data != null ){ Data = parseJson( resp.data ) ProcessForecast( Data.forecast.daily[ 0 ], "Today" ) ProcessForecast( Data.forecast.daily[ 1 ], "Tomorrow" ) } else { Logging( "No data returned by WeatherFlow", 5 ) } break case 401: Logging( "Unauthorized, most likely due to invalid token.", 5 ) break case 404: Logging( "Station not found.", 5 ) break default: Logging( "Error connecting to WeatherFlow: ${ resp.status }", 5 ) break } } // ProcessForecast takes the forecast data and sets it for the correct day def ProcessForecast( Data, Day ){ Data.each{ switch( it.key ){ case "conditions": ProcessEvent( "${ Day } Conditions", "${ it.value }" ) PostEventToChild( "Forecast", "${ Day } Conditions", "${ it.value }" ) break case "icon": ProcessEvent( "${ Day } Icon", it.value ) PostEventToChild( "Forecast", "${ Day } Icon", "${ it.value }" ) break case "sunrise": ProcessEvent( "${ Day } Sunrise", ConvertEpochToDate( it.value ) ) PostEventToChild( "Forecast", "${ Day } Sunrise", ConvertEpochToDate( it.value ) ) break case "sunset": ProcessEvent( "${ Day } Sunset", ConvertEpochToDate( it.value ) ) PostEventToChild( "Forecast", "${ Day } Sunset", ConvertEpochToDate( it.value ) ) break case "air_temp_high": ProcessEvent( "${ Day } High Temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "Forecast", "${ Day } High Temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "air_temp_low": ProcessEvent( "${ Day } Low Temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) PostEventToChild( "Forecast", "${ Day } Low Temperature", ConvertTemperature( "C", it.value ), "°${ location.getTemperatureScale() }" ) break case "precip_probability": ProcessEvent( "${ Day } Chance Precipitation", it.value, "%" ) PostEventToChild( "Forecast", "${ Day } Chance Precipitation", it.value, "%" ) break case "precip_icon": ProcessEvent( "${ Day } Precipitation Icon", "${ it.value }" ) PostEventToChild( "Forecast", "${ Day } Precipitation Icon", "${ it.value }" ) break case "precip_type": ProcessEvent( "${ Day } Precipitation Type", "${ it.value }" ) PostEventToChild( "Forecast", "${ Day } Precipitation Type", "${ it.value }" ) break // Data to ignore case "day_start_local": case "day_num": case "month_num": break // Data not handled yet default: Logging( "Unhandled data ${ Day } ${ it.key } = ${ it.value } from Weatherflow forecast.", 3 ) PostStateToChild( "Forecast", "${ Day } ${ it.key }", "${ it.value }" ) break } } } // Makes a string that describes the current wind direction in words (or at least letters) def String MakeWindDirectionString( Direction ){ def WindDirectionString switch( WindDirMethod ){ case "1": // Just uses the degree value WindDirectionString = "${ Direction }°" break case "2": // 4 Compass Points - Letters if( Direction > 315 || Direction <= 45 ){ WindDirectionString = "N" } else if( Direction > 45 && Direction <= 135 ){ WindDirectionString = "E" } else if( Direction > 135 && Direction <= 225 ){ WindDirectionString = "S" } else if( Direction > 225 && Direction <= 315 ){ WindDirectionString = "W" } break case "3": // 8 Compass Points - Letters if( Direction > 337.5 || Direction <= 22.5 ){ WindDirectionString = "N" } else if( Direction > 22.5 && Direction <= 67.5 ){ WindDirectionString = "NE" } else if( Direction > 67.5 && Direction <= 112.5 ){ WindDirectionString = "E" } else if( Direction > 112.5 && Direction <= 157.5 ){ WindDirectionString = "SE" } else if( Direction > 157.5 && Direction <= 202.5 ){ WindDirectionString = "S" } else if( Direction > 202.5 && Direction <= 247.5 ){ WindDirectionString = "SW" } else if( Direction > 247.5 && Direction <= 292.5 ){ WindDirectionString = "W" } else if( Direction > 292.5 && Direction <= 337.5 ){ WindDirectionString = "NW" } break case "4": // 16 Compass Points - Letters if( Direction > 348.75 || Direction <= 11.25 ){ WindDirectionString = "N" } else if( Direction > 11.25 && Direction <= 33.75 ){ WindDirectionString = "NNE" } else if( Direction > 33.75 && Direction <= 56.25 ){ WindDirectionString = "NE" } else if( Direction > 56.25 && Direction <= 78.75 ){ WindDirectionString = "ENE" } else if( Direction > 78.75 && Direction <= 101.25 ){ WindDirectionString = "E" } else if( Direction > 101.25 && Direction <= 123.75 ){ WindDirectionString = "ESE" } else if( Direction > 123.75 && Direction <= 146.25 ){ WindDirectionString = "SE" } else if( Direction > 146.25 && Direction <= 168.75 ){ WindDirectionString = "SSE" } else if( Direction > 168.75 && Direction <= 191.25 ){ WindDirectionString = "S" } else if( Direction > 191.25 && Direction <= 213.75 ){ WindDirectionString = "SSW" } else if( Direction > 213.75 && Direction <= 236.25 ){ WindDirectionString = "SW" } else if( Direction > 236.25 && Direction <= 258.75 ){ WindDirectionString = "WSW" } else if( Direction > 258.75 && Direction <= 281.25 ){ WindDirectionString = "W" } else if( Direction > 281.25 && Direction <= 303.75 ){ WindDirectionString = "WNW" } else if( Direction > 303.75 && Direction <= 326.25 ){ WindDirectionString = "NW" } else if( Direction > 326.25 && Direction <= 348.75 ){ WindDirectionString = "NNW" } break case "5": // 4 Compass Points - Words if( Direction > 315 || Direction <= 45 ){ WindDirectionString = "North" } else if( Direction > 45 && Direction <= 135 ){ WindDirectionString = "East" } else if( Direction > 135 && Direction <= 225 ){ WindDirectionString = "South" } else if( Direction > 225 && Direction <= 315 ){ WindDirectionString = "West" } break case "6": // 8 Compass Points - Words if( Direction > 337.5 || Direction <= 22.5 ){ WindDirectionString = "North" } else if( Direction > 22.5 && Direction <= 67.5 ){ WindDirectionString = "North-East" } else if( Direction > 67.5 && Direction <= 112.5 ){ WindDirectionString = "East" } else if( Direction > 112.5 && Direction <= 157.5 ){ WindDirectionString = "South-East" } else if( Direction > 157.5 && Direction <= 202.5 ){ WindDirectionString = "South" } else if( Direction > 202.5 && Direction <= 247.5 ){ WindDirectionString = "South-West" } else if( Direction > 247.5 && Direction <= 292.5 ){ WindDirectionString = "West" } else if( Direction > 292.5 && Direction <= 337.5 ){ WindDirectionString = "North-West" } break case "7": // 16 Compass Points - Words if( Direction > 348.75 || Direction <= 11.25 ){ WindDirectionString = "North" } else if( Direction > 11.25 && Direction <= 33.75 ){ WindDirectionString = "North-North-East" } else if( Direction > 33.75 && Direction <= 56.25 ){ WindDirectionString = "North-East" } else if( Direction > 56.25 && Direction <= 78.75 ){ WindDirectionString = "East-North-East" } else if( Direction > 78.75 && Direction <= 101.25 ){ WindDirectionString = "East" } else if( Direction > 101.25 && Direction <= 123.75 ){ WindDirectionString = "East-South-East" } else if( Direction > 123.75 && Direction <= 146.25 ){ WindDirectionString = "South-East" } else if( Direction > 146.25 && Direction <= 168.75 ){ WindDirectionString = "South-South-East" } else if( Direction > 168.75 && Direction <= 191.25 ){ WindDirectionString = "South" } else if( Direction > 191.25 && Direction <= 213.75 ){ WindDirectionString = "South-South-West" } else if( Direction > 213.75 && Direction <= 236.25 ){ WindDirectionString = "South-West" } else if( Direction > 236.25 && Direction <= 258.75 ){ WindDirectionString = "West-South-West" } else if( Direction > 258.75 && Direction <= 281.25 ){ WindDirectionString = "West" } else if( Direction > 281.25 && Direction <= 303.75 ){ WindDirectionString = "West-North-West" } else if( Direction > 303.75 && Direction <= 326.25 ){ WindDirectionString = "North-West" } else if( Direction > 326.25 && Direction <= 348.75 ){ WindDirectionString = "North-North-West" } break } return WindDirectionString } // Used to convert epoch values to text dates def String ConvertEpochToDate( Number Epoch ){ def date = use( groovy.time.TimeCategory ) { new Date( 0 ) + Epoch.seconds } return date } // Checks the location.getTemperatureScale() to convert temperature values def ConvertTemperature( String Scale, Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( location.getTemperatureScale() == "C" && Scale.toUpperCase() == "F" ){ ReturnValue = ( ( ( Value - 32 ) * 5 ) / 9 ) Logging( "Temperature Conversion ${ Value }°${ Scale.toUpperCase() } to ${ ReturnValue }°${ location.getTemperatureScale() }", 4 ) } else if( location.getTemperatureScale() == "F" && Scale.toUpperCase() == "C" ) { ReturnValue = ( ( ( Value * 9 ) / 5 ) + 32 ) Logging( "Temperature Conversion ${ Value }°${ Scale.toUpperCase() } to ${ ReturnValue }°${ location.getTemperatureScale() }", 4 ) } else if( location.getTemperatureScale() == Scale.toUpperCase() ){ ReturnValue = Value } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Converts speed between m/s and mph def ConvertMetersSecond( String BaseScale, Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( BaseScale == "Metric" && MeasurementStandard == "Imperial" ){ ReturnValue = ( Value * 2.237 ) } else if( BaseScale.toUpperCase() == "Imperial" && MeasurementStandard == "Metric" ) { ReturnValue = ( Value / 2.237 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Converts distances between km and miles or km/h and mph def ConvertMiles( String BaseScale, Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( BaseScale == "Metric" && MeasurementStandard == "Imperial" ){ ReturnValue = ( Value / 1.609 ) } else if( BaseScale.toUpperCase() == "Imperial" && MeasurementStandard == "Metric" ) { ReturnValue = ( Value * 1.609 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Converts measurements between inches and mm def ConvertInches( String BaseScale, Number Value ){ if( Value != null ){ def TempInt def ReturnValue = Value as double if( BaseScale == "Metric" && MeasurementStandard == "Imperial" ){ ReturnValue = ( Value / 25.4 ) TempInt = ( ReturnValue * 100000 ) as int ReturnValue = ( TempInt / 100000 ) } else if( BaseScale.toUpperCase() == "Imperial" && MeasurementStandard == "Metric" ) { ReturnValue = ( Value * 25.4 ) TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) } return ReturnValue } } // Converts pressure between mbar and psi /* def ConvertPressure( String BaseScale, Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( BaseScale == "Metric" && MeasurementStandard == "Imperial" ){ ReturnValue = ( Value / 68.948 ) } else if( BaseScale.toUpperCase() == "Imperial" && MeasurementStandard == "Metric" ) { ReturnValue = ( Value * 68.948 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } */ // Converts pressure between mbar, psi, and inHG based on PressureUnits preference or MeasurementStandard (if PressureUnits not set) def ConvertPressure( String BaseScale, Number Value ){ if( Value != null ){ def ReturnValue = Value as double if( ( BaseScale == "Metric" ) && ( ( PressureUnits == "psi" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Imperial" ) ) ) ){ ReturnValue = ( Value / 68.948 ) } else if( ( BaseScale == "Imperial" ) && ( ( PressureUnits == "mbar" ) || ( ( PressureUnits == null ) && ( MeasurementStandard == "Metric" ) ) ) ){ ReturnValue = ( Value * 68.948 ) } else if( ( BaseScale == "Metric" ) && ( PressureUnits == "inHG" ) ){ ReturnValue = ( Value / 33.864 ) } else if( ( BaseScale == "Imperial" ) && ( PressureUnits == "inHG" ) ){ ReturnValue = ( Value * 2.036 ) } def TempInt = ( ReturnValue * 100 ) as int ReturnValue = ( TempInt / 100 ) return ReturnValue } } // Process data to check against current state value and then send an event if it has changed def ProcessEvent( Variable, Value, Unit = null ){ if( state."${ Variable }" != Value ){ state."${ Variable }" = Value if( Unit != null ){ Logging( "Event: ${ Variable } = ${ Value }${ Unit }", 4 ) sendEvent( name: "${ Variable }", value: Value, unit: Unit, isStateChange: true ) } else { Logging( "Event: ${ Variable } = ${ Value }", 4 ) sendEvent( name: "${ Variable }", value: Value, isStateChange: true ) } } } // Process data to check against current state value and then send an event if it has changed def ProcessState( Variable, Value ){ if( state."${ Variable }" != Value ){ Logging( "State: ${ Variable } = ${ Value }", 4 ) state."${ Variable }" = Value } } // Post data to child device def PostEventToChild( Child, Variable, Value, Unit = null ){ if( ChildrenEnabled ){ if( Child != null ){ def ChildParent = "${ Child }"// ${ device.deviceNetworkId }" if( getChildDevice( "${ ChildParent }" ) == null ){ Logging( "Adding Child ${ ChildParent } with ${ Variable } = ${ Value }", 4 ) addSensor( "${ ChildParent }" ) } if( getChildDevice( "${ ChildParent }" ) != null ){ if( Unit != null ){ getChildDevice( "${ ChildParent }" ).ProcessEvent( "${ Variable }", Value, "${ Unit }" ) Logging( "Child ${ ChildParent } Event: ${ Variable } = ${ Value }${ Unit }", 4 ) } else { getChildDevice( "${ ChildParent }" ).ProcessEvent( "${ Variable }", Value ) Logging( "Child ${ ChildParent } Event: ${ Variable } = ${ Value }", 4 ) } } else { if( Unit != null ){ Logging( "Failure to add ${ ChildParent } and post ${ Variable }=${ Value }${ Unit }", 5 ) } else { Logging( "Failure to add ${ ChildParent } and post ${ Variable }=${ Value }", 5 ) } } } else { Logging( "Failure to add event ${ Variable } because child name was null", 5 ) } } } // Post data to child device def PostStateToChild( Child, Variable, Value ){ if( ChildrenEnabled ){ if( Child != null ){ def ChildParent = "${ Child }"// ${ device.deviceNetworkId }" if( getChildDevice( "${ ChildParent }" ) == null ){ Logging( "Adding Child ${ ChildParent } with ${ Variable } = ${ Value }", 4 ) addSensor( "${ ChildParent }" ) } if( getChildDevice( "${ ChildParent }" ) != null ){ Logging( "Child ${ ChildParent } State: ${ Variable } = ${ Value }", 4 ) getChildDevice( "${ ChildParent }" ).ProcessState( "${ Variable }", Value ) } else { Logging( "Failure to add ${ ChildParent } and post ${ Variable }=${ Value }", 5 ) } } else { Logging( "Failure to add state ${ Variable } because child name was null", 5 ) } } } // Adds a WeatherSensorChild child device // Based on @mircolino's method for child sensors def addSensor( String DNI ){ try{ Logging( "addSensor(${ 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.", 4 ) ChildrenEnabled = false } else { Logging( "Exception in addSensor: ${ Temp }", 4 ) } } } // 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 } } }