UNPKG

node-red-contrib-cast

Version:

NodeRED nodes to for Chromecast and Google Home

266 lines (238 loc) 14.4 kB
<script type="text/html" data-template-name="cast-to-client"> <div class="form-row"> <label for="node-input-ip"><i class="fa fa-google"></i> IP</label> <input type="text" id="node-input-ip" placeholder="0.0.0.0" style="width:60%;" > <a id="node-config-lookup-ipaddress" class="btn"><i id="node-config-lookup-ipaddress-icon" class="fa fa-search"></i></a> </div> <div class="form-row"> <label for="node-input-port">Port</label> <input type="text" id="node-input-port" placeholder="(optional) 8009"> </div> <hr> <div class="form-row"> <label for="node-input-url"><i class="fa fa-file-audio-o"></i> Media Url</label> <input type="text" id="node-input-url" placeholder="(optional) Media Url"> </div> <div class="form-row"> <label for="node-input-contentType"><i class="fa fa-file-o"></i> Media Type</label> <input type="text" id="node-input-contentType" placeholder="audio/basic"> </div> <div class="form-row"> <label for="node-input-imageUrl"><i class="fa fa-picture-o"></i> Image Url</label> <input type="text" id="node-input-imageUrl" placeholder="(optional) Image Url (metadata)"> </div> <hr> <div class="form-row"> <label for="node-input-message"><i class="fa fa-commenting-o"></i> Message</label> <input type="text" id="node-input-message" placeholder="(optional) Text to speach"> </div> <div class="form-row"> <label for="node-input-language"><i class="fa fa-language"></i> language</label> <input type="text" id="node-input-language" placeholder="en"> </div> <hr> <div class="form-row"> <label for="node-input-volume"><i class="fa fa-volume-up"></i> Volume</label> <input type="text" id="node-input-volume" class="cast-volume" placeholder="(optional) 0-100"> </div> <div class="form-tips"><b>Tip:</b> You can also define all properties as part of the incoming message. This would be used priories than the settings here.<br /> <code>msg.url</code> or <code>msg.payload.url</code><br /> <code>msg.urlList</code> / <code>msg.payload.urlList</code><br /> <code>msg.contentType</code> / <code>msg.payload.contentType</code><br /> <code>msg.imageUrl</code> / <code>msg.payload.imageUrl</code><br /> <code>msg.message</code> / <code>msg.payload.message</code><br /> <code>msg.language</code> / <code>msg.payload.language</code><br /> <code>msg.ip</code> / <code>msg.payload.ip</code> the IP address of the device to cast <br /> <code>msg.port</code> / <code>msg.payload.port</code> the port of the device (if not given default <code>8009</code> will be used)<br /> <code>msg.volume</code> / <code>msg.payload.volume</code> (optional) a number 0-100 where the volume should be set <br /> </div> <hr> <!-- 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 of this Event"> </div> </script> <!-- Next, some simple help text is provided for the node. --> <script type="text/html" data-help-name="cast-to-client"> <h3>About</h3> <p>Plays a Media file on a chromecast or a Home device</p> <h3>Inputs</h3> <dl class="message-properties"> <dt>payload <span class="property-type">string | object</span> </dt> <dd> the payload of the message to publish. </dd> <dt>ip <span class="property-type">ip-address</span></dt> <dd> the IP address of the device to cast the media. Could also be defined in the configuration of the node.</dd> <dt class="optional">port <span class="property-type">number</span></dt> <dd> the port of the device (if not given default <code>8009</code> will be used). Could also be defined in the configuration of the node.</dd> <dt>url <span class="property-type">string</span></dt> <dd> url to a media file which should be cast to the cast device. For a chromecast this should be a media or a video file. For a Google Home device without a display this could only be an audio file.</dd> <dt class="optional">imageUrl <span class="property-type">string</span></dt> <dd> url to a image file which represents the artwork for the url which should be cast to the cast device. For a chromecast this should be an image file. For a Google Home device without a display this is irrelevant.</dd> <dt>contentType <span class="property-type">string</span></dt> <dd> the content type of the file in the url. This property is required if a url is given. Could also be defined in the configuration of the node.</dd> <dt class="optional">message <span class="property-type">string</span></dt> <dd> a text which should be send to the google tts engine to convert to a mp3 file.</dd> <dt class="optional">language <span class="property-type">string</span></dt> <dd> the language which should be used for converting the message to the media file.</dd> <dt class="optional">volume <span class="property-type">number 0-100</span></dt> <dd> will set the device volume to this level. (if left blank, the volume will be unchanged.)</dd> <dt class="optional">lowerVolumeLimit <span class="property-type">number 0-100</span></dt> <dd> will set the volume to this value, if the current volume is below this value.</dd> <dt class="optional">upperVolumeLimit <span class="property-type">number 0-100</span></dt> <dd> will set the volume to this value, if the current volume is above this limit.</dd> <dt class="optional">muted <span class="property-type">boolean</span></dt> <dd> the volume should be muted if set to false, otherwise the volume will be unmuted.</dd> <dt class="optional">status <span class="property-type">boolean</span></dt> <dd> if this property is set to true, **only** the status will be given as output and nothing else will be done.</dd> </dl> <h3>Outputs</h3> <ol class="node-ports"> <li>Standard output <dl class="message-properties"> <dt>payload <span class="property-type">string</span></dt> <dd>the given status result by the cast2-client library.</dd> </dl> </li> </ol> <h3>Details</h3> <p>there are 3 different use cases:</p> <ol> <li>play a media file <br /> For this the url and the content type needs to be specified. Thus can be done in the configuration or passed as a message property <code>msg.url</code> and <code>msg.contentType</code> or give a payload object with the two properties <code>msg.payload.url</code> and <code>msg.payload.contentType</code><br /></li> <li>play a queue of media file <br /> For this the urlList and the content type needs to be specified. Thus can be done in the configuration or passed as a message property <code>msg.urlList</code> and <code>msg.contentType</code> or give a payload object with the two properties <code>msg.payload.urlList</code> and <code>msg.payload.contentType</code><br /></li> <li>speak a given message, converted by google tts <br /> For this the message needs to be specified. Thus can be done in the configuration or pass as a message property <code>msg.message</code> or give a payload object with the property <code>msg.payload.message</code></li> <li>get the state of the cast device <br /> For this no url or message has to be specified.</li> </ol> <p>If there is a combination of the message and the url, first the media file given by URL will be streamed and then the message.</p> <p>If the payload is a string and no contentType is defined, the payload will be used as a message which should be spoken.</p> <h3>References</h3> <ul> <li><a href="https://github.com/Hypnos3/node-red-contrib-cast">GitHub</a> - the nodes github repository</li> </ul> </script> <!-- Finally, the node type is registered along with all of its properties --> <!-- The example below shows a small subset of the properties that can be set--> <script type="text/javascript"> RED.nodes.registerType('cast-to-client', { category: 'function', defaults: { name: { value: '', required: false }, url: { value: null, required: false }, contentType: { value: '', required: false }, message: { value: null, required: false }, language: { value: 'en', required: false, validate: function (v) { return RED.validators.regex(/^[a-zA-Z]{2,4}(-[a-zA-Z0-9]{2,5}(-[a-zA-Z0-9]{2,5})?(-(Standard|Wavenet)-[A-Z])?)?$/)( v) || (v === ''); // we are allow a little more characters as currently used in language codes to be save for future changes } }, ip: { value: '', required: false, validate: function (v) { // IPv4 // hostname // IPv6 return (v === '') || RED.validators.regex(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)(v) || RED.validators.regex(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/)(v) || RED.validators.regex(/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/)(v); } }, port: { value: '', required: false, validate: function (v) { return RED.validators.number()(v) || (v === ''); // RED.validators.regex(/^(\s*|\d+)$/) does not work, because can be empty } }, volume: { value: null, required: false, validate: function (v) { if (!v) { return true; // empty } else if (/^\d+$/.test(v)) { return (parseInt(v) >= 0 && parseInt(v) <= 100); } return false; } } }, inputs: 1, // set the number of inputs - only 0 or 1 outputs: 1, // set the number of outputs - 0 to n icon: 'default.png', // saved in icons/myicon.png - myicon.png color: '#FFFF00', label: function () { if (this.name) { return this.name; } return ('Cast '+ this.ip + ((this.port) ? ':'+ this.port : '')); }, labelStyle: function () { return this.name ? 'node_label_italic' : ''; }, paletteLabel: 'Cast', oneditprepare: function () { $('.cast-volume').spinner({ max: 100, min: 0 }); $('#node-config-lookup-ipaddress').click(() => { $('#node-config-lookup-ipaddress-icon').removeClass('fa-search'); $('#node-config-lookup-ipaddress-icon').addClass('spinner'); $('#node-config-lookup-ipaddress').addClass('disabled'); $.getJSON('ipaddresses',(data) => { $('#node-config-lookup-ipaddress-icon').addClass('fa-search'); $('#node-config-lookup-ipaddress-icon').removeClass('spinner'); $('#node-config-lookup-ipaddress').removeClass('disabled'); const items = []; const unique = data.filter( (value, index, self) => { return self.findIndex(i => (i.ip === value.ip && i.port === value.port)) === index; }); // returns ['a', 1, 2, '1'] $.each(unique, (i, element) => { items.push(element.label + ' (' + element.ip + ':' + element.port + ')'); }); $('#node-input-ip').autocomplete({ source:items, minLength:0, close: function( _event, _ui ) { const str = this.value; const elements = str.substring(str.lastIndexOf('(') + 1, str.lastIndexOf(')')).split(':'); $('#node-input-ip').val(elements[0]); $('#node-input-port').val(elements[1]); $('#node-input-ip').autocomplete( 'destroy' ); } }).autocomplete('search',''); }); }); } });</script>