node-red-contrib-cec
Version:
Node-Red contributed nodes for interacting with cec-enabled devices on a HDMI switch, such as a TV or digital receiver
319 lines (279 loc) • 10.4 kB
HTML
<!--
node-red-contrib-cec - Node for Node Red that reads and writes to hdmi cec BUS
cec-state.html
Node-Red CEC Input Node
node-red-contrib-cec
15/8/17
Copyright (C) 2017 Damien Clark (damo.clarky@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<!-- The node type is registered along with all of its properties -->
<script type="text/javascript">
RED.nodes.registerType('cec-state',{
category: 'function',
color: '#3FADB5',
defaults: {
cec_adapter: {required: true, type:"cec-config" },
name: {value:"", required: false },
output: {value:"payload", required: true },
flow_name: {value:"cec", required: false },
flow: {value:false, required: true },
scan: {value: false, required: false }
},
inputs:1,
outputs:1,
icon: "hdmicec.png",
label: function() {
return this.name || "cec-state" ;
},
// Adds jquery accordion to edit html for UI 'on-editprepare'
oneditprepare: function() {
$(function() {
// If checkbox not ticked, disable text field on load
if(!$("#node-input-flow").prop('checked')) {
$("#node-input-flow_name").prop('disabled',true) ;
}
// Add event handler to toggle disabled property based on value of checkbox
$("#node-input-flow").click(function() {
var enable = $("#node-input-flow").prop('checked') ;
if(enable) {
$("#node-input-flow_name").prop('disabled', false);
}
else {
$("#node-input-flow_name").prop('disabled', true);
}
}) ;
}) ;
}
});
</script>
<!-- data-template-name identifies the node type this is for -->
<script type="text/x-red" data-template-name="cec-state">
<style>
.cec-checkbox-grid li {
display: inline-block;
width: 40%;
}
.cec-checkbox {
display: inline-block ;
width: auto ;
vertical-align: top ;
}
.cec-checkbox-label {
width: 70% ;
}
</style>
<!-- Each of the div.form-row elements creates a field in the edit dialog.-->
<!-- Generally, there should be an input for each property of the node. -->
<!-- The for and id attributes identify the corresponding property -->
<!-- (with the 'node-input-' prefix). -->
<!-- The available icon classes are defined Font Awesome Icons (FA Icons) -->
<div class="form-row">
<p><i class="fa fa-filter" aria-hidden="true"></i> Select CEC Adapter to use</p>
<label for="node-input-cec_adapter">CEC Adapter</label>
<input type="text" id="node-input-cec_adapter" placeholder="cec_adapter">
</div>
<div class="form-row">
<input type="checkbox" class="cec-checkbox" id="node-input-flow"/>
<label class="cec-checkbox-label" for="node-input-flow">Store in Flow</label>
</div>
<div class="form-row">
<label for="node-input-flow_name">Property Name</label>
<input type="text" id="node-input-flow_name" placeholder="Property name"/>
</div>
<div class="form-row">
<label for="node-input-output"><i class="fa fa-envelope"></i> Output Property: msg.</label>
<input type="text" id="node-input-output" placeholder="Property name"/>
</div>
<div class="form-row">
<input type="checkbox" class="cec-checkbox" id="node-input-scan"/>
<label class="cec-checkbox-label" for="node-input-scan">Scan on startup</label>
</div>
<!-- By convention, most nodes have a 'name' property. The following div -->
<!-- provides the necessary field. Should always be the last option -->
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="cec-state">
<!-- data-help-name identifies the node type this help is for -->
<!-- This content appears in the Info sidebar when a node is selected -->
<!-- The first <p> is used as the pop-up tool tip when hovering over a -->
<!-- node in the palette. -->
<p>This node tracks and stores the status of all the devices connected to the
HDMI CEC bus by listening to the cec messages.</p>
<p>By sending a message to its input, you can query a particular device by
its logical or physical address or, without an address, query state
information of all logical addresses</p>
<h3>Input Query Message Format</h3>
<p>To query a particular device, specify in the input message, the logical
(0 - 15) or physical address (of form 0.0.0.0) in <code>msg.address</code>
as shown below:</p>
<pre>
msg = {
"command": "getstate",
"address": "1.0.0.0"
}
</pre>
<p>Or:</p>
<pre>
msg = {
"command": "GETSTATE",
"address": "4"
}
</pre>
<p>Or using the equivalent logical address name - <code>playbackdevice1</code> device:</p>
<pre>
msg = {
"command": "GETSTATE",
"address": "playbackdevice1"
}
</pre>
<p>Following is the complete list of device names and their corresponding logical address.
Remember that a device can register more than one type of logical address.</p>
<ul>
<li>UNKNOWN: -1</li>
<li>TV: 0</li>
<li>RECORDINGDEVICE1: 1</li>
<li>RECORDINGDEVICE2: 2</li>
<li>TUNER1: 3</li>
<li>PLAYBACKDEVICE1: 4</li>
<li>AUDIOSYSTEM: 5</li>
<li>TUNER2: 6</li>
<li>TUNER3: 7</li>
<li>PLAYBACKDEVICE2: 8</li>
<li>RECORDINGDEVICE3: 9</li>
<li>TUNER4: 10</li>
<li>PLAYBACKDEVICE3: 11</li>
<li>RESERVED1: 12</li>
<li>RESERVED2: 13</li>
<li>FREEUSE: 14</li>
<li>UNREGISTERED: 15</li>
<li>BROADCAST: 15
</ul>
<p>If <code>command</code> is omitted, the default command is <code>getstate</code>.
There are many other specific commands, such as:</p>
<pre>
{
"command": "getpowerstatusname",
"address": "tv"
}
</pre>
<p>which will return a string representation of the power status of the TV, like <code>STANDBY</code>.</p>
<p>Other commands include:</p>
<ul>
<li><strong>getphysicaladdress</strong> - Get the physical address for this cec adapter</li>
<li><strong>getlogicaladdress</strong> - Get the primary logical address for this cec adapter</li>
<li><strong>getlogicaladdresses</strong> - Get all the logical addresses for this cec adapter</li>
<li><strong>logical2physical</strong> - Convert the logical address to its physical address (if known)</li>
<li><strong>physical2logical</strong> - Convert the physical address to its logical address (if known)</li>
<li><strong>getosdname</strong> - Get the on screen name (if known) for the given address e.g. <code>TV</code></li>
<li><strong>getpowerstatus</strong> - Get the power status code for the given address (if known) e.g. <code>0</code></li>
<li><strong>getpowerstatusname</strong> - Give the power status name for the given address (if known) e.g. <code>STANDBY</code></li>
<li><strong>getactivesource</strong> - Get the physical address of the active source (if known) e.g. <code>0.0.0.0</code></li>
</ul>
<p>Note, if any of the queries above are unknown, then <code>null</code> is returned.</p>
<h3>Output Format</h3>
<p>If a specific address is requested for <code>getstate</code>, as follows:</p>
<pre>
msg = {
"command": "getstate",
"address": "0"
}
</pre>
<p>then following js data structure shall be returned:</p>
<pre>
{
"physical": "0.0.0.0",
"power": 1,
"osdname": "TV"
}
</pre>
<p>If a value for a specific address is not known, then it will either be set to null, or an empty string:</p>
<pre>
{
"physical": null,
"power": null,
"osdname": ""
}
</pre>
<p>For <code>getsource</code>, if <code>msg.address</code> is omitted, then all devices' state is returned as an object keyed by logical address for all devices, as per below:</p>
<pre>
{
"0": {
"physical": "0.0.0.0",
"power": 1,
"osdname": "TV"
},
"1": {
"physical": "1.0.0.0",
"power": 0,
"osdname": "RasPlex"
},
"2": {
"physical": null,
"power": null,
"osdname": ""
},
"3": {
"physical": null,
"power": null,
"osdname": ""
},
"4": {
"physical": "3.0.0.0",
"power": null,
"osdname": "node-red"
},
"5": {
"physical": null,
"power": null,
"osdname": ""
},
...
"15": {
"physical": null,
"power": null,
"osdname": ""
}
}
</pre>
<h3>Property to Store State in Outgoing Message</h3>
<p>You can specify the property name to store the output in the outgoing <code>msg</code> object.
By default, it will be stored in <code>msg.payload</code> but you can store it anywhere.</p>
<h3>Storing State in Flow Context</h3>
<p>If you wish, you can also have cec-state store the state information of all devices in the context
of the flow in which the cec-state node resides. This makes this state information available to any
other node in the flow, including function nodes. You can specify the property name to use to store
the state information, which by default, is <code>cec</code>. To access the state data from a function
node, use the following syntax:</p>
<pre>
var cec = flow.get('cec') ;
msg.payload = cec.devices[0].osdname ;
return msg ;
</pre>
<p>This will retrieve the on screen display name for the TV (logical address 0) and store it in the payload
of the outgoing message from the function node.</p>
<pre>
var cec = flow.get('cec') ;
msg.payload = cec.active_source ;
return msg ;
</pre>
<p>This will retrieve the physical address of the currently known active source device.</p>
<h3>CEC Bus Scan</h3>
<p>Finally, you can ask the cec-state node to perform a bus scan on startup to try and identify all the
devices attached to the display device (on the CEC bus). Keep in mind that if you have non-CEC enabled
devices attached, the scan can cause confusion about which device is the active source. Your mileage
may vary.</p>
</script>