peoplecounter, IR, spacestatus, proxy config, amplifier
parent
a02aee8dc4
commit
c2bf6d60e4
29
README.txt
29
README.txt
|
@ -1,4 +1,13 @@
|
|||
An openHAB implementaiton for the level2 hackerspace
|
||||
|
||||
Dump this into /etc/openhab2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Installing proxy for paperUI
|
||||
|
||||
sudo bash
|
||||
apt-get update
|
||||
apt-get install nginx
|
||||
|
@ -8,13 +17,29 @@ cat >/etc/nginx/sites-available/openhab <<"EOF"
|
|||
server {
|
||||
listen 80;
|
||||
server_name lights.level2.lu;
|
||||
|
||||
# https://community.openhab.org/t/occasional-offline-in-basicui-when-proxying-via-nginx-connection-timed-out/63347
|
||||
location / {
|
||||
proxy_pass http://localhost:8080/basicui/;
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host:$server_port;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
client_body_buffer_size 0;
|
||||
client_max_body_size 0;
|
||||
proxy_max_temp_file_size 0;
|
||||
proxy_read_timeout 18000;
|
||||
proxy_send_timeout 18000;
|
||||
|
||||
gzip off;
|
||||
}
|
||||
location ~ ^/(paperui|habmin) {
|
||||
# return 301 /basicui/app;
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
|
@ -12,14 +12,16 @@ Group area42 "Area42" (main_floor)
|
|||
//-------------------------------------------------------------------------------
|
||||
|
||||
Number entrance_people_counter "People Counter [%s]" <parents_1_1> (entrance)
|
||||
Number entrance_people_counter_max "People Counter Max [%s]" <parents_1_1> (entrance)
|
||||
Switch entrance_door_status "Door Status" { channel="mqtt:topic:entrance_door_status:status" }
|
||||
Switch entrance_ceiling_lamps "Ceiling Lamps" <lightbulb> (entrance) { channel="mqtt:topic:entrance_ceiling_lamps:power" }
|
||||
Switch entrance_marbleadder_lamps "Marbleadder Lamps" <lightbulb> (entrance) { channel="mqtt:topic:mosquitto:entrance_marbleadder_lamps:power" }
|
||||
Switch chill_zone_lamps "Lamps" <lightbulb> (chill_zone) { channel="mqtt:topic:chill_zone_lamps:power" }
|
||||
Switch engineering_table_lamps "Table Lamps" <lightbulb> (engineering) { channel="mqtt:topic:engineering_table_lamps:power" }
|
||||
Switch engineering_ceiling_lamps "Ceiling Lamps" <lightbulb> (engineering) { channel="mqtt:topic:engineering_ceiling_lamps:power" }
|
||||
Switch engineering_status_lamp "Status Lamp" <lightbulb> (engineering) { channel="mqtt:topic:engineering_status_lamp:power" }
|
||||
Switch engineering_ceiling_phone_flash "Ceiling Phone Flash" <lightbulb> (engineering) { channel="mqtt:topic:engineering_phone_flash:pulse" }
|
||||
Switch area42_status_lamp "Status Lamp" <lightbulb> (area42) { channel="mqtt:topic:area42_status_lamp:power" }
|
||||
Switch area42_status_lamp "Status Lamp" <lightbulb> (area42) { channel="mqtt:topic:mosquitto:area42_status_lamp:power" }
|
||||
Switch lab_soldering_table "Soldering table" <lightbulb> (lab) { channel="mqtt:topic:mosquitto:lab_soldering_table:power" }
|
||||
|
||||
|
||||
|
@ -41,6 +43,26 @@ String chill_zone_chromecast_status
|
|||
Switch chill_zone_beamer
|
||||
"Beamer"
|
||||
(chill_zone)
|
||||
String chill_zone_beamer_input "beamer input" (chill_zone)
|
||||
// please help: beamer current state can be optained like this:
|
||||
//wget -qO - 'http://10.2.113.7/tgi/return.tgi?query=info' |awk -F'[<>]' '/<info>/{print substr($3,33,2)}'
|
||||
// the awk extracts the info. It is in format '<return>NG</return>' or '<return><info>data</info></return>
|
||||
//NG = off
|
||||
// from data, the interesting part is chars 33 and 34
|
||||
// vv
|
||||
//2a3139c616503735303000000048e60102ac0c010076
|
||||
//00=no signal
|
||||
//01=VGA1
|
||||
//02=DVI (slideshow)
|
||||
//03=composite
|
||||
//04=s-video
|
||||
//05=component
|
||||
//09=VGA2
|
||||
//11=LAN
|
||||
//12=USB A (presentation to go)
|
||||
//13=USB B (WTF?)
|
||||
//15=hdmi2 (chromecast)
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -63,6 +85,6 @@ Switch chill_zone_screen_button_down
|
|||
Rollershutter chill_projection_screen "Projection Screen"
|
||||
|
||||
|
||||
String IRCode_01 "IR Code" { channel="mqtt:topic:mosquitto:irremote:ir_code" }
|
||||
String IRCode "IR Code" { channel="mqtt:topic:mosquitto:irremote:ir_code" }
|
||||
String proxyIR
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Switch all_status_lights "All Status Lights" <lightbulb> (engineering)
|
|
@ -4,4 +4,5 @@ Strategies {
|
|||
|
||||
Items {
|
||||
entrance_people_counter: strategy = everyChange
|
||||
entrance_people_counter_max: strategy = everyChange
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ then
|
|||
//all_engineering_lights.postUpdate("ON")
|
||||
chill_zone_lamps.sendCommand("ON")
|
||||
entrance_ceiling_lamps.sendCommand("ON")
|
||||
entrance_marbleadder_lamps.sendCommand("ON")
|
||||
end
|
||||
|
||||
|
||||
|
@ -18,6 +19,7 @@ then
|
|||
engineering_ceiling_lamps.sendCommand("OFF")
|
||||
chill_zone_lamps.sendCommand("OFF")
|
||||
entrance_ceiling_lamps.sendCommand("OFF")
|
||||
entrance_marbleadder_lamps.sendCommand("OFF")
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@ rule "proxy IR mqtt"
|
|||
when
|
||||
Item proxyIR received command
|
||||
then
|
||||
IRCode_01.sendCommand(transform("MAP", "ir.map", receivedCommand.toString))
|
||||
IRCode.sendCommand(transform("MAP", "ir.map", receivedCommand.toString))
|
||||
end
|
|
@ -1,35 +1,69 @@
|
|||
|
||||
var Timer ChromeCastOffTimer = null
|
||||
val peoplecountermax = 0 // if openhab is restarted, the max value is lost... do we need persistence, how does that work?
|
||||
val SpaceStatus = ''
|
||||
|
||||
rule "Startup"
|
||||
when
|
||||
System started
|
||||
then
|
||||
if (entrance_door_status.state.toString == 'OFF') {
|
||||
SpaceStatus = 'open'
|
||||
} else {
|
||||
SpaceStatus = 'closed'
|
||||
}
|
||||
end
|
||||
|
||||
rule "Door Status"
|
||||
when
|
||||
Item entrance_door_status changed
|
||||
then
|
||||
val state = entrance_door_status.state.toString
|
||||
|
||||
val peoplecountermax
|
||||
// the sensor state is inverted:
|
||||
// OFF means opened
|
||||
// ON means opened
|
||||
if (state == 'OFF') {
|
||||
logInfo('door', 'opened')
|
||||
SpaceStatus = 'open'
|
||||
entrance_ceiling_lamps.sendCommand("ON")
|
||||
entrance_marbleadder_lamps.sendCommand("ON")
|
||||
chill_zone_lamps.sendCommand("ON")
|
||||
engineering_table_lamps.sendCommand("ON")
|
||||
engineering_ceiling_lamps.sendCommand("ON")
|
||||
engineering_status_lamp.sendCommand("ON")
|
||||
area42_status_lamp.sendCommand("ON")
|
||||
executeCommandLine("/etc/openhab2/syn2cat/spaceapi-set-status.sh open")
|
||||
// needs current time in OpenTime
|
||||
// sendTweet("It's "+OpenTime+" and we are open \o/ \ncome in and create something awesome =) \nhttps://Level2.lu/openingTimes")
|
||||
|
||||
} else {
|
||||
logInfo('door', 'closed')
|
||||
SpaceStatus = 'closed'
|
||||
// turn the beamer off
|
||||
sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3102fd0660")
|
||||
chill_zone_screen_button_up.sendCommand("ON")
|
||||
entrance_ceiling_lamps.sendCommand("OFF")
|
||||
entrance_marbleadder_lamps.sendCommand("OFF")
|
||||
chill_zone_lamps.sendCommand("OFF")
|
||||
engineering_table_lamps.sendCommand("OFF")
|
||||
engineering_ceiling_lamps.sendCommand("OFF")
|
||||
engineering_status_lamp.sendCommand("OFF")
|
||||
area42_status_lamp.sendCommand("OFF")
|
||||
lab_soldering_table.sendCommand("OFF")
|
||||
IRCode.sendCommand(transform("MAP", "ir.map", "amp_off"))
|
||||
executeCommandLine("/etc/openhab2/syn2cat/spaceapi-set-status.sh closed")
|
||||
// needs current time in OpenTime
|
||||
// and number of people in peopleMax="\nThere were up to $peopleMax hackers today.\n"
|
||||
peoplecountermax=entrance_people_counter_max.state
|
||||
if(peoplecountermax>8) {
|
||||
peopleMax="\nThere were up to "+peoplecountermax+" hackers today.\n"
|
||||
} else {
|
||||
peopleMax=".".repeat(peoplecountermax)
|
||||
}
|
||||
logInfo("door", "twitter message: "+ OpenTime+peopleMax)
|
||||
// sendTweet("We just closed our doors at "+OpenTime+peopleMax+" See you very soon... \nhttps://Level2.lu/openingTimes")
|
||||
entrance_people_counter_max.postUpdate(0)
|
||||
}
|
||||
|
||||
|
||||
|
@ -47,10 +81,26 @@ then
|
|||
val count = java.lang.Integer.parseInt(
|
||||
transform("XPATH", "/table/tr[4]/td[2]/text()", response).trim()
|
||||
)
|
||||
if ( SpaceStatus == 'closed' ) {
|
||||
count=0
|
||||
}
|
||||
if ( count != Integer.parseInt(entrance_people_counter.previousState().state.toString)) {
|
||||
entrance_people_counter.postUpdate(count)
|
||||
// fresh run needs a max init, later we use persistence to init
|
||||
if (entrance_people_counter_max.state == NULL) {entrance_people_counter_max.postUpdate(count) }
|
||||
// count is bigger, so we got a new maximum
|
||||
if(entrance_people_counter_max.state < count) {
|
||||
entrance_people_counter_max.postUpdate(count)
|
||||
logInfo("door", "People counter maximum set to "+ count)
|
||||
}
|
||||
executeCommandLine("/etc/openhab2/syn2cat/spaceapi-update-peoplecount.sh "+count)
|
||||
}
|
||||
end
|
||||
// moved this to some external scripts, way easier with debugging and quoting
|
||||
// /usr/bin/curl --max-time 1 --silent --data key="$spaceapikey" --data-urlencode 'sensors={"sensors":{"people_now_present":[{"value":'"$presency"'}]}}' https://spaceapi.syn2cat.lu/sensor/set
|
||||
// executeCommandLine("/usr/bin/curl -v --max-time 1 --data key=$(cat /etc/openhab2/spaceapikey.txt) --data-urlencode 'sensors={\"sensors\":{\"people_now_present\":[{\"value\":'+count+'}]}}' https://spaceapi.syn2cat.lu/sensor/set >/tmp/c.log 2>&1")
|
||||
// needs import java.net.URLEncoder
|
||||
// sendHttpGetRequest("https://spaceapi.syn2cat.lu/sensor/set?"+URLEncoder::encode('key='+spaceapikey+'&sensors={sensors":{"people_now_present":[{"value":'+count+'}]}}')
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -58,7 +108,8 @@ rule "Tweet People Count"
|
|||
when
|
||||
Item entrance_people_counter changed
|
||||
then
|
||||
// sendTweet('There are currently ' + entrance_people_counter.state.toString + ' hackers present. Just pass by!')
|
||||
// val people = entrance_people_counter.state.toString
|
||||
// sendTweet('There are currently ' + people + ' hackers present. Just pass by!')
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -88,6 +139,7 @@ then
|
|||
if (state == 'ON') {
|
||||
// turn the beamer on
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3101fe0660")
|
||||
chill_zone_screen_button_down.sendCommand("ON")
|
||||
} else {
|
||||
// turn the beamer off
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3102fd0660")
|
||||
|
@ -96,6 +148,29 @@ then
|
|||
logInfo("beamer", "Beamer state: {}", beamerStatus)
|
||||
end
|
||||
|
||||
rule "Beamer input"
|
||||
when
|
||||
Item chill_zone_beamer_input received command
|
||||
then
|
||||
val state = chill_zone_beamer_input.state.toString
|
||||
var beamerResponse
|
||||
|
||||
if (state == 'hdmi1') {
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3109f6071475")
|
||||
} else if (state == 'hdmi2') {
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3109f6071576")
|
||||
} else if (state == 'dvi') {
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3109f6070566")
|
||||
} else if (state == 'vga1') {
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3109f6070162")
|
||||
} else if (state == 'vga2') {
|
||||
beamerResponse = sendHttpGetRequest("http://10.2.113.7/tgi/return.tgi?command=2a3109f6070263")
|
||||
}
|
||||
else { }
|
||||
val beamerStatus = transform("XPATH", "/return/text()", beamerResponse).replace("\n", '')
|
||||
logInfo("beamer", "Beamer state: {}", beamerStatus)
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
rule "Status Lights ON"
|
||||
when
|
||||
Item all_status_lights received update ON
|
||||
then
|
||||
engineering_status_lamp.sendCommand("ON")
|
||||
area42_status_lamp.sendCommand("ON")
|
||||
end
|
||||
|
||||
rule "Status Lights OFF"
|
||||
when
|
||||
Item all_status_lights received update OFF
|
||||
then
|
||||
engineering_status_lamp.sendCommand("OFF")
|
||||
area42_status_lamp.sendCommand("OFF")
|
||||
end
|
|
@ -26,7 +26,7 @@
|
|||
# Include legacy 1.x bindings. If set to true, it also allows the installation of 1.x bindings for which there is
|
||||
# already a 2.x version available (requires remote repo access, see above). (default is false)
|
||||
#
|
||||
legacy = true
|
||||
legacy = false
|
||||
|
||||
# A comma-separated list of bindings to install (e.g. "binding = sonos,knx,zwave")
|
||||
binding = chromecast,mqtt,http1,exec,systeminfo
|
||||
|
@ -38,7 +38,7 @@ ui = basic,habmin,habpanel,paper
|
|||
persistence = mapdb
|
||||
|
||||
# A comma-separated list of actions to install (e.g. "action = mail,pushover")
|
||||
action = mail,twitter,mqtt
|
||||
action = mail,twitter
|
||||
|
||||
# A comma-separated list of transformation services to install (e.g. "transformation = map,jsonpath")
|
||||
transformation = jsonpath,map,regex,scale,xpath
|
||||
|
|
|
@ -5,10 +5,12 @@ sitemap default label="Level2" {
|
|||
Switch item=all_lights label="All Lights" icon="light"
|
||||
}
|
||||
Frame label="Multimedia" {
|
||||
Switch item=chill_zone_beamer label="Beamer" icon="projector"
|
||||
Switch item=chill_zone_beamer label="Beamer" icon="projector" mappings=[ON="on",OFF="off"]
|
||||
Selection item=chill_zone_beamer_input label="input" mappings=['hdmi1'='HDMI','hdmi2'='Chromecast','dvi'='Slideshow','vga1'='VGA']
|
||||
// mir brauchen en screen rule mat 3 status dei een kann setzen
|
||||
// Switch item=chill_zone_screen_button_up label="Projection Screen (todo)" icon="screen" mappings=[up="up",stop="stop",down="down"]
|
||||
Switch item=chill_projection_screen icon="screen" mappings=[UP="up",STOP="stop",DOWN="down"]
|
||||
}
|
||||
|
||||
Frame label="Amplifier" {
|
||||
Switch item=proxyIR label="power" mappings=['amp_on'='on','amp_off'='off']
|
||||
|
@ -22,32 +24,32 @@ sitemap default label="Level2" {
|
|||
Switch item=proxyIR label="navigate" mappings=['br_left'='left','br_up'='up','br_down'='down','br_right'='right'] icon="movecontrol"
|
||||
Switch item=proxyIR label="language" mappings=['br_language'='audio','br_subtitle'='subtitles']
|
||||
}
|
||||
}
|
||||
Frame label="Other" {
|
||||
// een knaeppchen deen nemmen aus geht
|
||||
Switch item=lab_soldering_table label="Soldering Table" icon="poweroutlet" mappings=[OFF="off"]
|
||||
Switch item=engineering_ceiling_phone_flash label="Phone flashlight" icon="siren" mappings=[OFF="off"]
|
||||
// wei switchen ech dei 2 status lights?. Dat hei ass fir status un ze maachen falls se net un wiren
|
||||
Switch item=area42_status_lamp label="Status lights (todo)" mappings=[ON="ON"]
|
||||
Switch item=all_status_lights label="Status lights" mappings=[ON="on"]
|
||||
}
|
||||
Frame label="Main Floor" icon="firstfloor" {
|
||||
Group item=engineering
|
||||
// Group item=lab
|
||||
Group item=chill_zone
|
||||
Group item=entrance
|
||||
// Frame label="Main Floor" icon="firstfloor" {
|
||||
// Group item=engineering
|
||||
// // Group item=lab
|
||||
// Group item=chill_zone
|
||||
// Group item=entrance
|
||||
//
|
||||
// // Selection item=chill_zone_beamer_input
|
||||
// // label="Beamer Input"
|
||||
// // mappings=[
|
||||
// // 0="DVI", // Hackerspace video
|
||||
// // 1="HDMI 1",
|
||||
// // 2="HDMI 2", // Chromecast
|
||||
// // 3="VGA 1",
|
||||
// // 4="VGA 2"
|
||||
// // ]
|
||||
// }
|
||||
|
||||
// Selection item=chill_zone_beamer_input
|
||||
// label="Beamer Input"
|
||||
// mappings=[
|
||||
// 0="DVI", // Hackerspace video
|
||||
// 1="HDMI 1",
|
||||
// 2="HDMI 2", // Chromecast
|
||||
// 3="VGA 1",
|
||||
// 4="VGA 2"
|
||||
// ]
|
||||
}
|
||||
Frame label="Ground Floor" icon="groundfloor" {
|
||||
Group item=ground_floor
|
||||
// Image url="https://raw.githubusercontent.com/wiki/openhab/openhab/images/features.png"
|
||||
}
|
||||
// Frame label="Ground Floor" icon="groundfloor" {
|
||||
// Group item=ground_floor
|
||||
// // Image url="https://raw.githubusercontent.com/wiki/openhab/openhab/images/features.png"
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
spaceapikey.txt
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
set -x
|
||||
exec >>/tmp/c.log
|
||||
exec 2>&1
|
||||
if [ "$1" = "" ] || ( [ "$1" != "open" ] && [ "$1" != "closed" ] )
|
||||
then
|
||||
echo "usage: $0 {open|closed}"
|
||||
exit 1
|
||||
fi
|
||||
spaceapikey="$(cat "$(dirname $0)"/spaceapikey.txt)"
|
||||
nai=$(date +%s)
|
||||
case "$1" in
|
||||
"open" )
|
||||
openstate="true"
|
||||
;;
|
||||
"closed" )
|
||||
openstate="false"
|
||||
;;
|
||||
* ) echo "error"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
/usr/bin/curl --max-time 1 --silent --data key="$spaceapikey" --data-urlencode sensors='{"state":{"open":'"$openstate"',"lastchange":'"$nai"'}}' https://spaceapi.syn2cat.lu/sensor/set
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
set -x
|
||||
exec >>/tmp/c.log
|
||||
exec 2>&1
|
||||
if [ "$1" = "" ]
|
||||
then
|
||||
echo "usage: $0 number"
|
||||
exit 1
|
||||
fi
|
||||
spaceapikey="$(cat "$(dirname $0)"/spaceapikey.txt)"
|
||||
presency="$1"
|
||||
/usr/bin/curl --max-time 1 --silent --data key="$spaceapikey" --data-urlencode sensors='{"sensors":{"people_now_present":[{"value":'"$presency"'}]}}' https://spaceapi.syn2cat.lu/sensor/set
|
||||
|
|
@ -22,6 +22,13 @@ Bridge mqtt:broker:mosquitto "Mosquitto" [
|
|||
]
|
||||
}
|
||||
|
||||
Thing topic entrance_marbleadder_lamps "Entrance Marbleadder Lamps" @ "Entrance" {
|
||||
Channels:
|
||||
Type switch : power "Power" [
|
||||
commandTopic="cmnd/entrance/marbleadder_lamp/Power"
|
||||
]
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
Thing topic chill_zone_screen "Screen" @ "Chill Zone" {
|
||||
|
@ -45,6 +52,7 @@ Bridge mqtt:broker:mosquitto "Mosquitto" [
|
|||
commandTopic="cmnd/chill_zone/sonoffs/Power"
|
||||
]
|
||||
}
|
||||
Thing exec:command:beamerpower [command="/sbin/apcaccess -u", interval=5, timeout=2, autorun=false]
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -90,7 +98,7 @@ Bridge mqtt:broker:mosquitto "Mosquitto" [
|
|||
Thing topic lab_soldering_table "Soldering table" @ "Lab" {
|
||||
Channels:
|
||||
Type switch : power "Power" [
|
||||
commandTopic="cmnd/lab/soldering_table/Power", stateTopic="stat/lab/soldering_table/Power"
|
||||
commandTopic="cmnd/lab/soldering_table/Power", stateTopic="stat/lab/soldering_table/POWER"
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -100,7 +108,8 @@ Bridge mqtt:broker:mosquitto "Mosquitto" [
|
|||
Thing topic irremote "IR remote" @ "Chill Zone" {
|
||||
|
||||
Type string : ir_code "ircode"
|
||||
[ commandTopic="cmnd/chill_zone/irremote/IRSEND"
|
||||
[
|
||||
commandTopic="cmnd/chill_zone/irremote/IRSEND"
|
||||
]
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue