pimatic-dewpoint
Version:
Calculates dew point, absolute humidity, wind chill factor, heat index, and apparent temperature
276 lines (235 loc) • 8.46 kB
text/coffeescript
module.exports = (env) ->
Promise = env.require 'bluebird'
assert = env.require 'cassert'
types = env.require('decl-api').types
_ = env.require 'lodash'
class DewPointPlugin extends env.plugins.Plugin
init: (app, , ) =>
deviceConfigDef = require("./device-config-schema")
.deviceManager.registerDeviceClass("DewPointDevice", {
configDef: deviceConfigDef.DewPointDevice,
createCallback: (config, lastState) =>
return new DewPointDevice(config, lastState)
})
plugin = new DewPointPlugin
class DewPointDevice extends env.devices.Device
temperature: 0.0
humidity: 0.0
dewPoint: 0.0
absHumidity: 0.0
windSpeed: 0.0
windChill: 0.0
heatIndex: 0.0
apparentTemperature: 0.0
attributes:
temperature:
description: "Temperature"
type: types.number
unit: "°C"
acronym: 'T'
humidity:
description: "Relative Humidity"
type: types.number
unit: "%"
acronym: 'RH'
dewPoint:
description: "Dew Point Temperature"
type: types.number
unit: "°C"
acronym: 'DT'
absHumidity:
description: "Absolute Humidity"
type: types.number
unit: "g/m³"
acronym: "AH"
windSpeed:
description: "Windspeed"
type: types.number
unit: "km/h"
acronym: 'WS'
windChill:
description: "Windchill Temperature"
type: types.number
unit: "°C"
acronym: 'WCT'
heatIndex:
description: "Heat Index Temperature"
type: types.number
unit: "°C"
acronym: 'HIT'
apparentTemperature:
description: "Apparent Temperature"
type: types.number
unit: "°C"
acronym: 'AT'
constructor: (, lastState) ->
= .id
= .name
= lastState?.temperature?.value or 0.0
= lastState?.humidity?.value or 0.0
= lastState?.dewPoint?.value or 0.0
= lastState?.absHumidity?.value or 0.0
= lastState?.windSpeed?.value or 0.0
= lastState?.windChill?.value or 0.0
= lastState?.heatIndex?.value or 0.0
= lastState?.apparentTemperature?.value or 0.0
= .units
= _.cloneDeep
if is "imperial"
["temperature"].unit = '°F'
["dewPoint"].unit = '°F'
["windChill"].unit = '°F'
["heatIndex"].unit = '°F'
["apparentTemperature"].unit = '°F'
else if is "standard"
["temperature"].unit = 'K'
["dewPoint"].unit = 'K'
["windChill"].unit = 'K'
["heatIndex"].unit = 'K'
["apparentTemperature"].unit = 'K'
= .windUnits
["windSpeed"].unit =
= plugin.framework.variableManager #so you get the variableManager
= []
for reference in [
{name: "temperature", expression: .temperatureRef},
{name: "humidity", expression: .humidityRef}
{name: "windSpeed", expression: .windSpeedRef}
]
do (reference) =>
name = reference.name
info = null
evaluate = ( =>
# wait till VariableManager is ready
return Promise.delay(10).then( =>
unless info?
info = .parseVariableExpression(reference.expression)
.notifyOnChange(info.tokens, evaluate)
.push evaluate
switch info.datatype
when "numeric" then .evaluateNumericExpression(info.tokens)
when "string" then .evaluateStringExpression(info.tokens)
else
assert false
).then((val) =>
if val
env.logger.debug name, val
name, val
return @[name]
)
)
super()
destroy: () ->
.cancelNotifyOnChange(cl) for cl in
super()
calcHeatIndex: (t, rh, dp) ->
if t < 27 or rh < 40 or dp < 12
t
else
c1 = -8.784695
c2 = 1.61139411
c3 = 2.338549
c4 = -0.14611605
c5 = -1.2308094 * 0.01
c6 = -1.6424828 * 0.01
c7 = 2.211732 * 0.001
c8 = 7.2546 * 0.0001
c9 = -3.582 * 0.000001
prh = rh * rh
pt = t * t
c1 + c2*t + c3*rh + c4*t*rh + c5*pt + c6*prh + c7*pt*rh + c8*t*prh + c9*pt*prh
calcWindChill: (t, vw) ->
if t >= 10
t
else
if vw >= 4.8 and vw <= 177
13.12 + 0.6215 * t + (0.3965 * t - 11.37) * Math.pow(vw, 0.16)
else if vw < 4.8
t + 0.2 * (0.1345 * t - 1.59) * vw
else
t
calcValues: ->
t =
# dewPoint
if t >= 0
a = 7.5
b = 237.3
else
a = 7.6
b = 240.7
sdd = 6.1078 * Math.pow(10, (a * t) / (b + t))
dd = sdd * ( / 100)
v = Math.log(dd / 6.1078) / Math.log(10)
td = (b * v) / (a - v)
env.logger.debug 'dewPoint',
'dewPoint',
# absHumidity
ah = 2.16679 * ((100 * dd) / (273.15 + t))
env.logger.debug 'absHumidity', ah
'absHumidity', ah
# windChill
vw =
wct = t, vw
env.logger.debug 'windChill',
'windChill',
# heatIndex
hit =
env.logger.debug 'heatIndex',
'heatIndex',
# apparentTemperature
at = if t < 10 then vw else hit
env.logger.debug 'apparentTemperature',
'apparentTemperature',
_setAttribute: (attributeName, value) ->
@[attributeName] = value
attributeName, value
_fromUnitTemperature: (t) ->
if is "imperial"
return t
else if is "standard"
return t
else
return t
_toUnitTemperature: (t) ->
if is "imperial"
return t
else if is "standard"
return t
else
return t
_fromUnitWindSpeed: (vw) ->
if is "mp/h"
return vw
else if is "m/s"
return vw
else if is "ft/s"
return vw
else if is "knots"
return vw
else
return vw
_fahrenheitToCelsius: (fahrenheit) ->
return (fahrenheit - 32) * 5 / 9
_celsiusToFahrenheit: (celsius) ->
return celsius * 9 / 5 + 32
_kelvinToCelsius: (kelvin) ->
return kelvin - 273.15
_celsiusToKelvin: (celsius) ->
return celsius + 273.15
_milesToKm: (miles) ->
return (miles * 1.60934)
_msToKm: (ms) ->
return (ms * 3.6)
_ftsToKm: (fts) ->
return (fts * 1.09728)
_knotsToKm: (knots) ->
return (knots * 1.852)
# getters for temperature & humidity are created by the constructor using method
getDewPoint: -> Promise.resolve()
getAbsHumidity: -> Promise.resolve()
getWindChill: -> Promise.resolve()
getHeatIndex: -> Promise.resolve()
getApparentTemperature: -> Promise.resolve()
return plugin