I’m currently running a recent snapshot of OH2.5.
According to the docs the following should work:
windrose.items:
Number:Angle Wx_OWM_Current_Wind_Direction "Wind Direction [%d %unit%]" <wind> (gWeatherCurrent) {channel="openweathermap:weather-and-forecast:<api_key>:local:current#wind-direction"}
String Wx_OWM_Current_Wind_Direction_Simplified "Wind Direction [%s]" <wind> (gWeatherCurrent)
Number:Dimensionless Wx_OWM_Current_Wind_Direction_Windrose "Wind Direction [SCALE(windrose-32.map):%s]" <windrose> (gWeatherCurrent)
The idea is to map the 32 wind rose points plus the “indeterminate wind direction” point to the [0, 100] range used in Number:Dimensionless
items. Here 0
refers to “indeterminate wind direction” and all 32 wind rose points are evenly spaced (in steps of 3).
I also created a set of 32+2 wind rose icons for dynamic icon support. They’re located in /etc/openhab2/icons/classic/
and have the following names:
- windrose.svg (catch-all for UNDEF state - represents unknown wind direction
- windrose-N.svg (where N is 0 for variable wind direction, 3 for ‘North’, 6 for ‘North by East’, … 96 for ‘North by West’)
The idea is to transform a given wind direction (range 0°-360°) to a number in the range 0-32, and then to multiply this number by 3 and adding an offset of 3 so state 0 can be used for reporting indeterminate wind direction (different from unknown/undefined wind direction, as those correspond to an error states). In other words:
-
windDirectionDegrees
:=(Wx_OWM_Current_Wind_Direction.state as Number).doubleValue
-
windDirectionForWindrose
:=windDirectionDegrees / 360.0 * 32.0
-
windDirectionWindroseDynamicIconState
:= 3 + (3 *Math.round( windDirectionForWindrose )
postUpdate(Wx_OWM_Current_Wind_Direction_Windrose, windDirectionWindroseDynamicIconState)
(I’m using Math.round()
here as .intValue
truncates a floating point number (as with Math.floor()
) rather than rounding it to the nearest integer.
windrose-32.map:
0=Variable
3=N
6=NbE
9=NNE
12=NEbN
15=NE
18=NEbE
21=ENE
24=EbN
27=E
30=EbS
33=ESE
36=SEbE
39=SEbE
42=SEbS
45=SSE
48=SbE
51=S
54=SbW
57=SSW
60=SWbS
63=SWbS
66=SWbW
69=WSW
72=WbS
75=W
78=WbN
81=WNW
84=NWbW
87=NWbW
90=NWbN
93=NNW
96=NbW
windrose-32.rule:
/* Provide the compass wind direction in a proxy Item (Wx_OWM_Current_Wind_Direction_Simplified) for displaying on the sitemap.
* This is needed as the SCALE transform only accepts numeric values, whereas the OWM binding sometimes reports non-numeric values
* in cases where the wind direction is undefined or hard to define.
*/
rule "Update simplified wind direction"
when
Item Wx_OWM_Current_Wind_Direction changed
then
val String ruleTitle = "UpdateOWMCurrentWindDirection"
// Update Wx_OWM_Current_Wind_Direction_Simplified if Wx_OWM_Wind_Speed is numeric
var String status = "" // Will hold the textual representation of the wind direction (Wx_OWM_Current_Wind_Direction_Simplified)
var boolean error = true
var int windrose = 0 // Will hold the integer state for using dynamic 32-point windrose icon representing the wind direction (can also be used in a MAP(windrose-32.map) transform)
if (Wx_OWM_Current_Wind_Direction.state == NULL) {
status = "(not set)"
} else if (Wx_OWM_Current_Wind_Direction.state == UNDEF) {
status = "(undefined)"
} else if (Wx_OWM_Current_Wind_Direction.state.toString == "-") {
status = "(-)"
} else if (Wx_OWM_Current_Wind_Direction.state instanceof Number) {
logInfo(ruleTitle, "Wx_OWM_Current_Wind_Direction has state '{}' - will be transformed", Wx_OWM_Current_Wind_Direction.state)
// We can now safely use the SCALE() transform as the input is a Number
status = transform("SCALE", "wind.scale", Wx_OWM_Current_Wind_Direction.state.toString())
// Now we will compute the 32-point wind rose offset for use with dynamic icons
// First convert the wind direction (degrees) to a scale of 0-32:
val Number x = (Wx_OWM_Current_Wind_Direction.state as Number).doubleValue / (360.0 / 32.0)
logInfo(ruleTitle, "Wx_OWM_Current_Wind_Direction has state '{}' - transformed to '{}' (x = {})",
Wx_OWM_Current_Wind_Direction.state, status, x)
// Now we round this windrose value to the nearest integer
val int y = Math.round(x.floatValue)
logInfo(ruleTitle, "Wx_OWM_Current_Wind_Direction has state '{}' - transformed to '{}' (y = {})",
Wx_OWM_Current_Wind_Direction.state, status, y)
// Now we compute the dynamic state offset value (multiply by 3 and add start offset of 3). We also handle negative degrees by adding 32 to the windrose integer value (and use modulo calculus to get back to the range [0..32[)
windrose = ((y + 32) % 32) * 3 + 3
logInfo(ruleTitle, "Wx_OWM_Current_Wind_Direction has state '{}' - transformed to '{}' (x = {}) (y = {}) (windrose value {})",
Wx_OWM_Current_Wind_Direction.state, status, x, y, windrose)
error = false
} else { // Unexpected state type
status = "(invalid: " + Wx_OWM_Current_Wind_Direction.state.toString() + ")"
}
if (error) {
logInfo(ruleTitle, "{} has state '{}', expecting Number", Wx_OWM_Current_Wind_Direction.name, Wx_OWM_Current_Wind_Direction.state.toString())
} else {
logDebug(ruleTitle, "{} has Number state '{}'", Wx_OWM_Current_Wind_Direction.name, Wx_OWM_Current_Wind_Direction.state.toString())
}
postUpdate(Wx_OWM_Current_Wind_Direction_Simplified, status)
postUpdate(Wx_OWM_Current_Wind_Direction_Windrose, windrose.intValue.toString)
end
This, in theory, should work.
However, adding the following in the sitemap doesn’t yield the expected result:
// The followng doesn't properly handle 'UNDEF' state, hence replaced with Wx_OWM_Current_Wind_Direction_Simplified:
// Default item=Wx_OWM_Current_Wind_Direction
// Computed by means of a rule whenever Wx_OWM_Current_Wind_Direction changes;
Default item=Wx_OWM_Current_Wind_Direction_Simplified
Default item=Wx_OWM_Current_Wind_Direction_Windrose
Instead, I get an error:
2019-06-14 13:58:21.570 [WARN ] [rm.AbstractFileTransformationService] - Could not transform '51.0' with the file 'windrose.map' : Target value not found in map for '51.0'
This happens, irrespective of the type used in postUpdate()
. I tried:
-
postUpdate(Wx_OWM_Current_Wind_Direction_Windrose, windrose)
→ doesn’t work (yieldsfloat
) -
postUpdate(Wx_OWM_Current_Wind_Direction_Windrose, windrose.intValue)
→ doesn’t work (yieldsfloat
) -
postUpdate(Wx_OWM_Current_Wind_Direction_Windrose, windrose.intValue.toString)
→ doesn’t work (yieldsfloat
)
I eventually generated a SCALE()
transform so I would at least already get the wind directions mapped to the textual representations of the wind direction - here’s windrose-32.scale:
[0..1.5]=(var)
]1.5..4.5]=N
]4.5..7.5]=NbE
]7.5..10.5]=NNE
]10.5..13.5]=NEbN
]13.5..16.5]=NE
]16.5..19.5]=NEbE
]19.5..22.5]=ENE
]22.5..25.5]=EbN
]25.5..28.5]=E
]28.5..31.5]=EbS
]31.5..34.5]=ESE
]34.5..37.5]=SEbE
]37.5..40.5]=SEbE
]40.5..43.5]=SEbS
]43.5..46.5]=SSE
]46.5..49.5]=SbE
]49.5..52.5]=S
]52.5..55.5]=SbW
]55.5..58.5]=SSW
]58.5..61.5]=SWbS
]61.5..64.5]=SWbS
]64.5..67.5]=SWbW
]67.5..70.5]=WSW
]70.5..73.5]=WbS
]73.5..76.5]=W
]76.5..79.5]=WbN
]79.5..82.5]=WNW
]82.5..85.5]=NWbW
]85.5..88.5]=NWbW
]88.5..91.5]=NWbN
]91.5..94.5]=NNW
]94.5..97.5]=NbW
Now the 32-point wind rose names are correctly rendered, but the dynamic state icons still won’t work. In fact, none is ever shown.
What am I doing wrong?