Mapping numeric value in OpenHab2 to String - homekit

I added Homematic HM-CC-RT-DN thermostats by connecting a CCU2 to OpenHab2.
Then I defined the thermostats as items for HomeKit to use:
Group gBedroomThermostat "Bedroom Thermostat" [ "Thermostat" ]
Number BedroomThermostatCurrentTemp "Bedroom Thermostat Current Temperature" (gBedroomThermostat) [ "CurrentTemperature" ] { channel="homematic:HM-CC-RT-DN:xxx:yyy:4#ACTUAL_TEMPERATURE" }
Number BedroomThermostatTargetTemperature "Bedroom Thermostat Target Temperature" (gBedroomThermostat) [ "TargetTemperature" ] { channel="homematic:HM-CC-RT-DN:xxx:yyy:4#SET_TEMPERATURE" }
String BedroomThermostatHeatingCoolingMode "Bedroom Thermostat Heating/Cooling Mode" (gBedroomThermostat) [ "homekit:HeatingCoolingMode" ]
This all works fine, except for the last line in this definition. The thermostats only supply a percentage value of the valve state (homematic:HM-CC-RT-DN:xxx:yyy:4#VALVE_STATE). What I would like to do is to map this percentage value to the String 'Off' if the valve state is 0% and to 'On' otherwise.
I looked into OpenHab2 Transformations (https://github.com/openhab/openhab/wiki/Transformations) but I'm neither sure how to use them in this case, nor where to apply them.

Related

Generic restructure of ruby hash (with depth of 4)

Is it possible to convert this HASH into an array of arrays based solely on the position of the key (rather than it's value). ie: I know ahead of time that the first Key will always be PROD/ALPHA, and the second Key will always be a country (that I would like to be able to change in the future at will)
The idea would be to group all servers of the same type (webservers) that are also in the same environment (production) but are located in different farms (UK, USA)
While any suggestions on how to do this are welcome, I'll be happy to just know that I'm not walking into a dead-end I won't be able to solve.
Here are some visuals to aid in my explanation:
{
"PROD": {
"USA": {
"generic": [
"nginx-240"
],
"WEB": [
"nginx-210",
"nginx-241",
"nginx-211",
"nginx-209"
],
"APP": [
"tomcat-269",
"tomcat-255",
"tomcat-119",
"tomcat-124"
]
},
"UK": {
"WEB": [
"nginx-249",
"nginx-250",
"nginx-246",
"nginx-247",
"nginx-248"
],
"generic": [
"tomcat-302"
],
"APP": [
"tomcat-396",
"tomcat-156",
"tomcat-157"
]
}
},
"ALPHA": {
"USA": {
"WEB": [
"nginx-144",
"nginx-146",
"nginx-145",
"nginx-175",
"nginx-173"
],
"APP": [
"tomcat-204",
"tomcat-206"
]
}
}
}
The expectation is that data from the lowest level in the hash would be grouped together.
Again the idea is that all Production app servers (both from UK and USA) are grouped together in the following kind of pattern:
PROD_UK_APP would be represented by
["tomcat-396","tomcat-156","tomcat-157"] as these are the lowest branches of the tree PROD->UK->applicationserver
[
[
[PROD_UK_APP],[PROD_USA_APP]
],
[
[PROD_UK_WEB],[PROD_USA_WEB]
]
]
New list..
[
[
[ALPHA_USA_WEB]
],
[
[ALPHA_USA_APP],
[
[
Again the idea is to keep this generic. Is this something that is practically achievable or am I likely to require some degree of hardcoding to ensure it always works? The idea is that if tomorrow UK becomes JAPAN, it will still work in exactly the same way, comparing between the APP and WEB tier of UK, and JAPAN (separating ALPHA from PROD).
EDIT: my attempt to try and sort it:
def walk
a = []
myhash.each do |env, data|
data.each do |dc, tier|
tier.each do |x, y|
a << y
end
end
end
p a
end
[["nginx240"], ["nginx210", "nginx241", "nginx211", "nginx209"], ["tomcat269", "tomcat255", "tomcat119", "tomcat124"], ["nginx249", "nginx250", "nginx246", "nginx247", "nginx248"], ["tomcat302"], ["tomcat396", "tomcat156", "tomcat157"], ["nginx144", "nginx146", "nginx145", "nginx175", "nginx173"], ["tomcat204", "tomcat206"]]
Thanks,
I think I follow what you're looking for and you should get what you're after with:
myhash.values.each_with_object([]) do |by_country, out_arr|
by_country.values.each do |by_type|
out_arr << by_type.values
end
end
which would return:
[
[
[
"nginx-240"
],
[
"nginx-210",
"nginx-241",
"nginx-211",
"nginx-209"
],
[
"tomcat-269",
"tomcat-255",
"tomcat-119",
"tomcat-124"
]
],
[
[
"nginx-249",
"nginx-250",
"nginx-246",
"nginx-247",
"nginx-248"
],
[
"tomcat-302"
],
[
"tomcat-396",
"tomcat-156",
"tomcat-157"
]
],
[
[
"nginx-144",
"nginx-146",
"nginx-145",
"nginx-175",
"nginx-173"
],
[
"tomcat-204",
"tomcat-206"
]
]
]
Piece by piece
Take your hash, disgard the keys and just create an array of values.
iterate over the values (array of hashes by country) and initialize an array to return.
for each hash that by_country points to, again take the values, to drop into the by type(?) hashes
iterate over your by_type hashes and again take the values of each
push each return array into the array you want to return

Dynamic layout additions in Rebol3

I would like to dynamically add a button to the layout of a view, with the actor causing this addition belonging to a button that is already part of the layout.
I started with this:
REBOL [title: "Dynamic Button Addition"]
tilesize: 60x60
curtile: 1
stylize [
p: button [
facets: [init-size: tilesize max-size: tilesize]
actors: [
on-action: [
++ curtile
append tiles compose [ p (to-string curtile) ]
print ? tiles/options/content
v/redraw
]
]
]
]
v: [
tiles: hgroup [ p "1" ]
]
view v
...which does not appear to have the value of tiles/options/content change with each click.
I can get it to change if make these changes:
append tiledata compose [ p (to-string curtile) ]
and
tiledata: [ p "1" ]
v: [
tiles: hgroup tiledata
However, this does not cause any change on screen. If I replace the last four lines with this:
v: view [
tiles: hgroup tiledata
]
...so that v is now the view rather than the view's layout, I get this error when I click:
** Script error: v has no value
** Where: actor all foreach do-actor unless do-face if actor all foreach do-actor if do-event do-event if do-event either -apply- wake-up loop -apply- wait forever try do-events if view do either either either -apply-
** Near: actor face :data
This makes sense to me, because v is not yet done being defined, until I exit the program, IIUC.
How, then, can I make changes to v before the program ends, but after it's been passed to view?
Not very nice, but working if you replace
v/redraw
with these two lines
unview/all
view v
And there is a real dynamic example on how to update a layout that has already be viewed
I will simplify it
stylize [
tbox: hpanel [
actors: [
on-make: [
append face/options [
content: [
]
]
do-actor/style face 'on-make none 'hpanel
]
]
]
]
view/across [
button "button 1"
on-action [
append-content test compose [
button ( join "button " 2 + length? test/gob)
]
]
test: tbox
]

Evaluating code blocks in Rebol3

I'm trying to improve the Sliding Tile Puzzle example by making the starting positions random.
There's a better way to do this--"It is considered bad practice to convert values to strings and join them together to pass to do for evaluation."--but the approach I took was to try to generate Rebol3 source, and then evaluate it. I have it generating correctly, I think:
random/seed now
arr: random collect [ repeat tilenum 9 [ keep tilenum ] ]
hgroup-data: copy {}
repeat pos 9 [
curtile: (pick arr pos)
append hgroup-data either curtile = 9
[ reduce "x: box tilesize gameback " ]
[ rejoin [ { p "} curtile {" } ] ]
if all [(pos // 3) = 0 pos != 9] [ append hgroup-data " return^/" ]
]
print hgroup-data
...outputs something like:
p "4" x: box tilesize gameback p "5" return
p "3" p "7" p "1" return
p "2" p "8" p "6"
...which if I then copy and paste into this part, works correctly:
view/options [
hgroup [
PASTE-HERE
]
] [bg-color: gameback]
However, if I try to do it dynamically:
view/options [
hgroup [
hgroup-data
]
] [bg-color: gameback]
...(also print hgroup-data, do hgroup-data, and load hgroup-data), I get this error:
** GUI ERROR: Cannot parse the GUI dialect at: hgroup-data
...(or at: print hgroup-data, etc., depending on which variation I tried.)
If I try load [ hgroup-data ] I get:
** Script error: extend-face does not allow none! for its face argument
** Where: either if forever -apply- apply init-layout make-layout actor all foreach do-actor unless -apply- apply all build-face -apply- apply init-layout make-layout actor all foreach do-actor if build-face -apply- apply init-layout make-layout actor all foreach do-actor unless make-face -apply- apply case view do either either either -apply-
** Near: either all [
word? act: dial/1
block? body: get dial...
However, if I use the syntax hgroup do [ hgroup-data ], the program runs, but there are no buttons: it appears to be somehow over-evaluated, so that the return values of the functions p and box and so on are put straight into the hgroup as code.
Surely I'm missing an easy syntax error here. What is it?
First, I would say it's better to construct a block directly, instead of constructing a string and converting it to a block. But if you really want to do that, this should do the trick:
view/options compose/only [
hgroup (load hgroup-data)
] [bg-color: gameback]

Detecting a mouse click / mouse up in NetLogo

Using mouse-down? for mouse actions in NetLogo often results in the action happening too many times. For example, if you want to let users click to create new turtles, you could hook a forever button up to a procedure like:
to add-turtle
if mouse-down? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
end
The problem is that this usually results in many turtles being created per click. I'd like to do something like:
to add-turtle
if mouse-clicked? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
end
Where mouse-clicked? is true right as the person clicks (right after they left up the mouse button).
Unfortunately, you do have to keep track of it yourself, but the good news is that its not hard.
The key is to create a global called mouse-was-down? that is set to true if mouse-down? was true in the last time your mouse-oriented procedure was called. Then mouse-clicked? can be defined as follows:
to-report mouse-clicked?
report (mouse-was-down? = true and not mouse-down?)
end
It seems to work well in conjunction with a central mouse-management procedure that calls the other click-based procedures. For example:
globals [ mouse-was-down? ]
to-report mouse-clicked?
report (mouse-was-down? = true and not mouse-down?)
end
to mouse-manager
let mouse-is-down? mouse-down?
if mouse-clicked? [
add-turtle
; Other procedures that should be run on mouse-click
]
set mouse-was-down? mouse-is-down?
end
to add-turtle
crt 1 [ setxy mouse-xcor mouse-ycor ]
end
Using the mouse-is-down? local variable keeps the behavior more consistent if the mouse button is released before mouse-manager finishes.
Hello probably this is a bit late for your purpose, but in case anyone has the same problem, this is how I solved this in my models for now.
to add-turtle
if mouse-down? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
stop
]
end
The workaround here is to add a "stop" command to kill the "create-new-turtle" forever button. The only tradeoff is that you will have to press again to create another new turtle.
Bryan Head's answer didn't work for me, it still created multiple turtles in one click.
Alternative:
A (non-forever) button which does crt 1 [ setxy mouse-xcor mouse-ycor ] with action key A.
Now all you need to do to add a turtle is press or hold A with your left hand while mousing with your right hand.
Here is an alternative to Bryan's code, in case you want to perform an action as soon as the mouse button is clicked (instead of waiting until it's released):
globals [ mouse-clicked? ]
to mouse-manager
ifelse mouse-down? [
if not mouse-clicked? [
set mouse-clicked? true
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
] [
set mouse-clicked? false
]
end

How to store a collection of floats in postgres?

I'm building a script that saves tweets to a postgres database using ruby/pg/ActiveRecord/TweetStream (gem).
This script works fine..
...
TweetStream::Client.new.track(SEARCH_TERMS) do |t|
puts "#{t.text}"
attributes = {
tweetid: t[:id],
text: t.text,
in_reply_to_user_id: t.in_reply_to_user_id,
in_reply_to_status_id: t.in_reply_to_status_id,
received_at: t.created_at,
user_statuses_count: t.user.statuses_count,
user_followers_count: t.user.followers_count,
user_profile_image_url: t.user.profile_image_url,
user_screen_name: t.user.screen_name,
user_timezone: t.user.time_zone,
user_location: t.user.location,
user_lang: t.lang,
user_id_str: t.user.id,
user_name: t.user.name,
user_url: t.user.url,
user_created_at: t.user.created_at,
user_geo_enabled: t.user.geo_enabled,
}
if StoreTweet.create(attributes)
puts "saved"
else
puts "error"
end
end
until I add
user_geo_enabled: t.user.geo_enabled,
coordinates: t.coordinates.coordinates,}
I also tried
t.coordinates
t.coordiantes[:coordinates]
t[:coordinates] #this allows me to save but is always blank if when geo_enabled is 'true'
Twitter dev center (https://dev.twitter.com/docs/platform-objects/tweets) tells me that 'coordinates' is a collection of floats like:
"coordinates":
{
"coordinates":
[
-75.14310264,
40.05701649
],
"type":"Point"
}
for the moment i use a 'text' field. Which type should i give to the field in order to store both values together?

Resources