/*
* WeatherSensorChild
*
* Description:
* This Hubitat driver provides a spot to put weather sensor data in children of the parent device. It has no
* ability to check anything on it's own but the children make it much easier for dashboard and comparison purposes.
*
* Instructions for using Tile Template method (originally based on @mircolino's HTML Templates):
* 1) In "Hubitat -> Devices" select the child/sensor (not the parent) you would like to "templetize"
* 2) In "Preferences -> Tile Template" enter your template (example below) and click "Save Preferences"
* Ex: "[font size='2'][b]Temperature:[/b] ${ temperature }°${ location.getTemperatureScale() }[/br][/font]"
* 3) In a Hubitat dashboard, add a new tile, and select the child/sensor, in the center select "Attribute", and on the right select the "Tile" attribute
* 4) Select the Add Tile button and the tile should appear
* NOTE: Should accept most HTML formatting commands with [] instead of <>
*
* Features List:
* Ability to receive just state variables without posting events
* Ability to clear state variables
* Ability to display temperature, humidity, battery, illuminance, uvi, pressure, pm25, windspeed, and more...
* 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.8.19 - Addition of a couple attributes for OpenWeather 3.0
* 0.8.18 - Addition of Gw2001-based attributes provided by @MajorEvent plus added LastUpdateString and LastUpdateEpoch
* 0.8.17 - Added image attribute to display weather icons
* 0.8.16 - Added Timestamp attributes for WeatherFlow purposes.
* 0.8.15 - Added ReturnState function
* 0.8.14 - Correction to ProcessEvent function and removal of old driver-specific attributes when Preferences are saved
* 0.8.13 - Added a daily schedule for driver update checking
* 0.8.12 - Added handling for forced event notifications
* 0.8.11 - Additional Air Quality attributes
* 0.8.10 - Fixed copy/paste error that removed a number of attributes
* 0.8.9 - Added command allowing state variables to be cleared
* 0.8.8 - Additional attributes added and corrected 24hr typo on some attributes
* 0.8.7 - Added attributes for AQI with 24hr average
* 0.8.6 - Additional attributes for air quality sensors
* 0.8.5 - Change to new method of handling Tiles, removed Clear command, driver update notifications, and logging
* 0.8.4 - Added additional lightning attributes
* 0.8.3 - Added additional forecast attributes
* 0.8.2 - Added forecast attributes for WeatherFlow
* 0.8.1 - Replaced boolean attributes with string attributes because boolean attributes are not valid
* 0.8.0 - Added AQI attributes and a missing WeatherFlow attribute
* 0.7.2 - Inclusion of WH55 (Leak) and WH57 (Lightning) attributes
* 0.7.1 - Corrections for totalRain and some wind attributes
* 0.7.0 - Inclusion of @mircolino's HTML Template capability
* 0.6.1 - New version checking convention and state/event handling
* 0.05 - Additional attributes
* 0.04 - More attributes for WeatherFlow and ability to clear state variables
* 0.03 - Additional attributes for WeatherFlow
* 0.02 - Mass inclusion of attributes to support almost anything
* 0.01 - Initial revision
*
* 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 as well as coming up with the
* HTML Template method to assist in formatting child/sensors for dashboard use
*/
// Returns the driver name
def DriverName(){
return "WeatherSensorChild"
}
// Returns the driver version
def DriverVersion(){
return "0.8.19"
}
// Driver Metadata
metadata{
definition( name: "WeatherSensorChild", namespace: "Snell", author: "David Snell", importUrl: "https://www.drdsnell.com/projects/hubitat/drivers/WeatherSensorChild.groovy" ) {
capability "Sensor"
capability "Battery"
capability "TemperatureMeasurement"
capability "RelativeHumidityMeasurement"
capability "CarbonDioxideMeasurement"
capability "IlluminanceMeasurement"
capability "PressureMeasurement"
capability "UltravioletIndex"
capability "WaterSensor"
//capability "pHMeasurement"
// Commands
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 version notices for driver
// Attributes
attribute "HeatIndex", "number" // HeatIndex is only calculated if temperature outside >= 80F
attribute "WindChill", "number" // WindChill is only calculated if temperature outside <= 50F and wind speed is > 3
// Arttributes for the values returned that Hubitat supports normally
attribute "temperature", "number"
attribute "humidity", "number" // Outside humidity
attribute "pressure", "number"
attribute "ultravioletIndex", "number"
attribute "illuminance", "number" // Based on solarradiation * 126.7
attribute "windSpeed", "number" // This attribute is NOT listed in Hubitat documentation... but is used by weather dashboard tile.
attribute "windDirection", "number" // This attribute is NOT listed in Hubitat documentation... but is used by weather dashboard tile.
attribute "humidityout", "number" // Not a real value returned by Ambient but a duplicate of the returned humidity
attribute "battery", "number" // Not a real value returned by Ambient but a duplicate of battout
// Attributes that have been created for conversion between measurement standards
attribute "baromrel", "number" // Created for switching between inHg/mmHg
attribute "baromabs", "number" // Created for switching between inHg/mmHg
attribute "windGust", "number" // Created for switching between mph/kph
attribute "windspd_avg2m", "number" // Created for switching between mph/kph
attribute "windspd_avg10m", "number" // Created for switching between mph/kph
attribute "hourlyRain", "number" // Created for switching between in/mm
attribute "eventRain", "number" // Created for switching between in/mm
attribute "dailyRain", "number" // Created for switching between in/mm
attribute "weeklyRain", "number" // Created for switching between in/mm
attribute "monthlyRain", "number" // Created for switching between in/mm
attribute "yearlyRain", "number" // Created for switching between in/mm
attribute "totalRain", "number" // Created for switching between in/mm
attribute "Last24HourRain", "number" // Created for switching between in/mm
attribute "WindDirectionString", "string" // This attribute was created to use words for the direction of the wind
// Attributes that exist in the data returned but MAY be modified due to measurement standards
attribute "dewPoint", "number"
attribute "feelsLike", "number"
attribute "dewPointin", "number"
attribute "feelsLikein", "number"
attribute "baromrelin", "number"
attribute "baromabsin", "number"
attribute "windgustmph", "number"
attribute "windspeedmph", "number"
attribute "maxdailygust", "number"
attribute "windspdmph_avg2m", "number"
attribute "windspdmph_avg10m", "number"
attribute "eventrainin", "number"
attribute "hourlyrainin", "number"
attribute "dailyrainin", "number"
attribute "weeklyrainin", "number"
attribute "monthlyrainin", "number"
attribute "yearlyrainin", "number"
attribute "totalrainin", "number"
attribute "totalRain", "number"
// Additional attributes for values returned
attribute "uv", "number"
attribute "winddir", "number"
attribute "solarradiation", "number"
attribute "windgustdir", "number"
attribute "WindGustDirectionString", "string" // This attribute was created to use words for the direction of the wind
attribute "winddir_avg2m", "number"
attribute "winddir_avg2mString", "string"
attribute "winddir_avg10m", "number"
attribute "winddir_avg10mString", "string"
// Attributes added for WeatherFlow
attribute "TimestampEpoch", "number"
attribute "TimestampString", "string"
attribute "BatteryVoltage", "number"
attribute "AirTemperature", "number"
attribute "BarometricPressure", "number"
attribute "StationPressure", "number"
attribute "SeaLevelPressure", "number"
attribute "RelativeHumidity", "number"
attribute "Precip", "number"
attribute "PrecipAccumLast1hr", "number"
attribute "PrecipAccumLocalDay", "number"
attribute "PrecipAccumLocalYesterday", "number"
attribute "PrecipAccumLocalYesterdayFinal", "number"
attribute "PrecipMinutesLocalDay", "number"
attribute "PrecipMinutesLocalYesterday", "number"
attribute "PrecipMinutesLocalYesterdayFinal", "number"
attribute "windAvg", "number"
attribute "windLull", "number"
attribute "SolarRadiation", "number"
attribute "LightningStrikeLastEpoch", "number"
attribute "LightningStrikeLastDate", "string"
attribute "LightningStrikeLastDistance", "number"
attribute "LightningStrikeCount", "number"
attribute "LightningStrikeCountLast1hr", "number"
attribute "LightningStrikeCountLast3hr", "number"
attribute "WetBulbTemperature", "number"
attribute "DeltaT", "number"
attribute "AirDensity", "number"
attribute "light_Sensor", "string"
attribute "lightning_Sensor", "string"
attribute "rain_Sensor", "string"
attribute "wind_Sensor", "string"
attribute "air_temperature_humidity_Sensor", "string"
attribute "barometric_pressure_Sensor", "string"
attribute "diagnostics_Sensor", "string"
attribute "forecast_Sensor", "string"
attribute "location_item_id", "number"
attribute "hardware_revision", "number"
attribute "device_id", "number"
attribute "firmware_revision", "number"
attribute "environment", "string"
attribute "agl", "number"
attribute "wifi_network_name", "string"
attribute "name", "string"
attribute "serial_number", "string"
attribute "device_type", "string"
attribute "show_precip_final", "string"
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"
// WH57 Lightning sensor
attribute "lightning", "string"
attribute "lightning_time", "string"
attribute "lightning_num", "number"
attribute "lightning_day", "string"
attribute "lightning_hour", "string"
attribute "Last Lightning Strike Time", "string"
attribute "Lightning Strike Distance", "number"
attribute "Lightning Strike Count", "number"
// WH55 Leak sensor
attribute "leak_ch1", "number" // 0 = no leak, 1 = leak
attribute "leak_ch2", "number" // 0 = no leak, 1 = leak
attribute "leak_ch3", "number" // 0 = no leak, 1 = leak
attribute "leak_ch4", "number" // 0 = no leak, 1 = leak
attribute "leak_ch1_water", "enum", [ "wet", "dry" ]
attribute "leak_ch2_water", "enum", [ "wet", "dry" ]
attribute "leak_ch3_water", "enum", [ "wet", "dry" ]
attribute "leak_ch4_water", "enum", [ "wet", "dry" ]
attribute "leakbatt1", "number" // 5 = full battery
attribute "leakbatt2", "number" // 5 = full battery
attribute "leakbatt3", "number" // 5 = full battery
attribute "leakbatt4", "number" // 5 = full battery
// WS90 Attributes for returned data
attribute "rrain_piezo", "number"
attribute "hrain_piezo", "number"
attribute "drain_piezo", "number"
attribute "wrain_piezo", "number"
attribute "mrain_piezo", "number"
attribute "yrain_piezo", "number"
attribute "erain_piezo", "number"
// Tile Template attribute
attribute "Tile", "string"; // Ex: "[font size='2'][b]Temperature:[/b] ${ temperature }°${ location.getTemperatureScale() }[/br][/font]"
// Air Quality
attribute "co2", "number"
attribute "pm25", "number"
attribute "pm25_24hr", "number"
attribute "pm25_avg_24hr", "number"
attribute "pm25_in", "number"
attribute "pm25_in_24hr", "number"
attribute "pm25_in_avg_24hr", "number"
attribute "batt_25", "number"
attribute "Air Quality Index", "number"
attribute "Air Quality Index 24hr Average", "number"
attribute "Indoor Air Quality Index 24hr Average", "number"
attribute "Indoor Air Quality Index", "number"
attribute "Air Quality Index 1", "number"
attribute "Air Quality Index 2", "number"
attribute "Air Quality Index 3", "number"
attribute "Air Quality Index 4", "number"
attribute "Air Quality Index String", "string"
attribute "Air Quality Index 24hr Average String", "string"
attribute "Indoor Air Quality Index String", "string"
attribute "Indoor Air Quality Index 24hr Average String", "string"
attribute "Air Quality Index 1 String", "string"
attribute "Air Quality Index 2 String", "string"
attribute "Air Quality Index 3 String", "string"
attribute "Air Quality Index 4 String", "string"
attribute "Air Quality Index Color", "string"
attribute "Air Quality Index 24hr Average Color", "string"
attribute "Indoor Air Quality Index Color", "string"
attribute "Indoor Air Quality Index 24hr Average Color", "string"
attribute "Air Quality Index 1 Color", "string"
attribute "Air Quality Index 2 Color", "string"
attribute "Air Quality Index 3 Color", "string"
attribute "Air Quality Index 4 Color", "string"
attribute "aqi_pm25", "number"
attribute "aqi_pm25_aqin", "number"
attribute "aqi_pm25_24hr", "number"
attribute "Indoor pm10", "number"
attribute "Indoor pm10_24hr", "number"
attribute "Indoor pm25", "number"
attribute "Indoor pm25_24hr", "number"
attribute "Indoor co2", "number"
attribute "Indoor co2_24hr", "number"
attribute "Air Quality Sensor pm25", "number"
attribute "Air Quality Sensor pm25_24hr", "number"
attribute "Air Quality Sensor Temperature", "number"
attribute "Air Quality Sensor Humidity", "number"
attribute "Air Quality Sensor Indoor pm10", "number"
attribute "Air Quality Sensor Indoor pm10_24hr", "number"
attribute "Air Quality Sensor Indoor pm25", "number"
attribute "Air Quality Sensor Indoor pm25_24hr", "number"
attribute "Air Quality Sensor Indoor co2", "number"
attribute "Air Quality Sensor Indoor co2_24hr", "number"
attribute "Air Quality Sensor Battery", "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"
attribute "Min Temperature", "number"
attribute "Max Temperature", "number"
attribute "Chance Precipitation", "number"
attribute "Chance Snow", "number"
attribute "Sunrise", "string"
attribute "Sunset", "string"
attribute "Moon Phase", "string"
attribute "Moonrise", "string"
attribute "Moonset", "string"
attribute "Cloud Cover", "number"
attribute "Conditions", "string"
attribute "Forecast Valid as of", "string"
// Attributes for OpenWeather 3.0
attribute "RainAmount", "number"
attribute "SnowAmount", "number"
// Attributes added for ease of use
attribute "image", "string" // image is meant to provide a spot for weather icons to be displayed
attribute "LastUpdateString", "string" // "Human readable" date/time of when data was last updated
attribute "LastUpdateEpoch", "number" // "Machine readable" epoch value of date/time when data was last updated
}
preferences{
section{
if( ShowAllPreferences ){
input( name: "TileTemplate", type: "string", title: "Tile Template", description: "Ex: [b]Temperature:[/b] \${ state.temperature }°${ location.getTemperatureScale() }[/br]", defaultValue: "");
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
def updated(){
if( LogType == null ){
LogType = "2"
}
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 daily driver version check
schedule( new Date(), CheckForUpdate )
Logging( "Updated", 2 )
}
// Clears state variables for the parent device and any children
def ClearStateVariables(){
state.clear()
}
// 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()
}
// Tile Template method based on @mircolino's HTML Template method
private void UpdateTile( String val ){
if( settings.TileTemplate ){
// Create special compund/html tile
val = settings.TileTemplate.toString().replaceAll( "\\[", "<" )
val = val.replaceAll( "\\]", ">" )
val = val.replaceAll( ~/\$\{\s*([A-Za-z][A-Za-z0-9_]*)\s*\}/ ) { java.util.ArrayList m -> device.currentValue("${ m [ 1 ] }").toString() }
if( device.currentValue( "Tile" ).toString() != val ){
sendEvent( name: "Tile", value: val )
}
}
}
// Process data to check against current state value and then send an event if it has changed
def ProcessEvent( Variable, Value, Unit = null, ForceEvent = false ){
if( ( state."${ Variable }" != Value ) || ( ForceEvent == true ) ){
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 )
}
UpdateTile( "${ Value }" )
}
}
// Process data to check against current state value and update if it has changed
def ProcessState( Variable, Value ){
if( state."${ Variable }" != Value ){
Logging( "State: ${ Variable } = ${ Value }", 4 )
state."${ Variable }" = Value
UpdateTile( "${ Value }" )
}
}
// Return a state value
def ReturnState( Variable ){
return state."${ Variable }"
}
// 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
}
}
}