@fetchbot/node-red-contrib-ikea-home-smart
Version:
Node-RED node to utilize IKEA smart home devices.
483 lines (432 loc) • 23.1 kB
HTML
<!-- Config Node -->
<script type="text/javascript">
RED.nodes.registerType('ikea-connection',{
category: 'config',
defaults: {
address: { value:"", required:true },
name: { value:"" }
},
credentials: {
securityCode: { type:"text" },
identity: { type:"text" },
psk: { type:"text" }
},
label: function() {
return this.name || "ikea@" + this.address;
}
});
</script>
<script type="text/x-red" data-template-name="ikea-connection">
<div class="form-row">
<label for="node-config-input-address"><i class="fa fa-globe"></i> Address</label>
<input type="text" id="node-config-input-address">
</div>
<div class="form-row">
<label for="node-config-input-securityCode"><i class="fa fa-gears"></i> Security Code</label>
<input type="text" id="node-config-input-securityCode">
</div>
<div class="form-row">
<label for="node-config-input-identity"><i class="fa fa-address-card-o"></i> Identity</label>
<input type="text" id="node-config-input-identity">
</div>
<div class="form-row">
<label for="node-config-input-psk"><i class="fa fa-lock"></i> Pre-shared key</label>
<input type="text" id="node-config-input-psk">
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name">
</div>
</script>
<script type="text/x-red" data-help-name="ikea-connection">
<ul>
<li><i class="fa fa-globe"></i> <b>Address:</b> The address to your gateway.</li>
<li><i class="fa fa-gears"></i> <b>Security code:</b> The security code on the backside of your gateway. You only need to enter this if you <em>don't</em> have an identiy and PSK. In that case identity and PSK will be generated for you using the security key.</li>
<li><i class="fa fa-address-card-o"></i> <b>Identity:</b> The identity used to access the gateway. Used together with a PSK.</li>
<li><i class="fa fa-lock"></i> <b>Pre-shared key:</b> The passkey used to access the gateway. Used together with an identity.</li>
</ul>
</script>
<!--- Homesmart Node -->
<script type="text/javascript">
var updateDevices = (currentDeviceId) => {
let configNodeId = $('#node-input-connection').find(":selected").val();
if (configNodeId == null || configNodeId === '_ADD_') { return; }
let deviceType = $('#node-input-deviceType').find(":selected").val();
if (deviceType == null || deviceType === '_ADD_') {
$('#node-input-deviceId').find('option').not(':first').remove();
$('#node-input-deviceId-icon').attr('class', 'fa fa-home');
$('#deviceObserve').hide();
$('#sceneId').hide();
return;
}
$.get('ikea/' + deviceType, {nodeId: configNodeId}, function(resp) {
let devices = JSON.parse(resp);
$('#node-input-deviceId').find('option').not(':first').remove();
if (!Array.isArray(devices) || devices.length <= 0) { return; }
//$('#node-input-deviceId').find('option').not(':first').remove();
$.each(devices, function(i, device) {
let opt = {};
opt.value = device.id;
opt.text = device.name;
if (device.id === currentDeviceId) {
opt.selected = "selected";
}
$('#node-input-deviceId').append($('<option>', opt));
});
});
if (deviceType === 'ikea-groups') {
$.get('ikea/ikea-scenes', {nodeId: configNodeId}, function(resp) {
let scenes = JSON.parse(resp);
if (!Array.isArray(scenes) || scenes.length <= 0) { return; }
$('#node-option-scene').find('tr').not(':first').remove();
$.each(scenes, function(i, scene) {
if (scene.group === $('#node-input-deviceId').find(":selected").val()) {
$('#node-option-scene').append('<tr><td>' + scene.name + '</td><td><code>{"sceneId": ' + scene.id + '}</code></td></tr>');
}
});
});
}
switch (deviceType) {
case "ikea-blinds":
$('#node-input-deviceId-icon').attr('class', 'fa fa-window-maximize');
$('#deviceObserve').show();
$('#sceneId').hide();
break;
case "ikea-lights":
$('#node-input-deviceId-icon').attr('class', 'fa fa-lightbulb-o');
$('#deviceObserve').show();
$('#sceneId').hide();
break;
case "ikea-plugs":
$('#node-input-deviceId-icon').attr('class', 'fa fa-plug');
$('#deviceObserve').show();
$('#sceneId').hide();
break;
case "ikea-sensors":
$('#node-input-deviceId-icon').attr('class', 'fa fa-bullseye');
$('#deviceObserve').hide();
$('#sceneId').hide();
break;
case "ikea-remotes":
$('#node-input-deviceId-icon').attr('class', 'fa fa-power-off');
$('#deviceObserve').hide();
$('#sceneId').hide();
break;
case "ikea-groups":
$('#node-input-deviceId-icon').attr('class', 'fa fa-sitemap');
$('#deviceObserve').show();
$('#sceneId').show();
break;
default:
break;
}
};
RED.nodes.registerType('ikea-homesmart', {
category: 'homesmart',
color: '#FFDA1A',
defaults: {
name: { value: "" },
deviceId: { value: "", required: true, validate: RED.validators.number() },
deviceName: { value: "" },
deviceType: { value: "" },
deviceIcon: { value: "" },
connection: { value: "", type: "ikea-connection" },
observe: { value: false, required: true }
},
align: 'left',
inputs: 1,
outputs: 1,
icon: function() {
return this.deviceIcon || 'font-awesome/fa-home';
},
label: function() {
if (this.name !== "") {
return this.name;
} else {
return this.deviceName || "ikea-homesmart";
}
},
oneditprepare: function() {
var node = this;
$('#deviceObserve').hide();
$('#sceneId').hide();
$('#node-input-connection').change(function(){
updateDevices(node.deviceId);
});
$('#node-input-deviceType').change(function(){
updateDevices(node.deviceId);
});
$('#ikea-deviceId-refresh-spinner').click(function(){
updateDevices(node.deviceId);
});
$(document).ready(function() {
$('#node-input-msgPassthrough').prop('checked', node.observe);
});
},
oneditsave: function() {
this.deviceType = $('#node-input-deviceType').find(":selected").text();
this.deviceName = $('#node-input-deviceId').find(":selected").text();
this.deviceIcon = 'font-awesome/' + $('#node-input-deviceId-icon').attr('class').substring(3);
}
});
</script>
<style>
th { text-align: left; }
</style>
<script type="text/x-red" data-template-name="ikea-homesmart">
<div class="form-row" id="deviceConnection">
<label for="node-input-connection"><i class="fa fa-cog"></i> Connection</label>
<input type="text" id="node-input-connection" placeholder="Connection">
</div>
<div class="form-row" id="deviceType">
<label for="node-input-deviceType"><i class="fa fa-home"></i> Device Type</label>
<div style="display: inline-block; position: relative; width: 70%; height: 20px;">
<div style="position: absolute; left: 0px; right: 40px;">
<select id="node-input-deviceType" style="width: 100%">
<option value="_ADD_" selected>None</option>
<option value="ikea-blinds">Blind</option>
<option value="ikea-lights">Light</option>
<option value="ikea-plugs">Plug</option>
<option value="ikea-sensors">Sensor</option>
<option value="ikea-remotes">Remote</option>
<option value="ikea-groups">Group</option>
</select>
</div>
</div>
</div>
<div class="form-row" id="deviceId">
<label for="node-input-deviceId"><i id="node-input-deviceId-icon" class="fa fa-home"></i> Device</label>
<div style="display: inline-block; position: relative; width: 70%; height: 20px;">
<div style="position: absolute; left: 0px; right: 40px;">
<select id="node-input-deviceId" style="width: 100%">
<option selected>None</option>
</select>
</div>
<a id="node-input-lookup-connection" class="editor-button" style="position: absolute; right: 0px; top: 0px;"><i id="ikea-deviceId-refresh-spinner" class="fa fa-refresh"></i></a>
</div>
</div>
<div class="form-row" id="deviceObserve">
<label> </label>
<input type="checkbox" id="node-input-observe" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-observe" style="width: 70%;" >Observe device?</label>
</div>
<div class="form-row" id="deviceName">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row" id="sceneId">
<label for="node-option-scene"><i class="fa fa-picture-o"></i> Available Scenes</label>
<div style="display: inline-block; position: relative; width: 70%; height: 20px;">
<div style="position: absolute; left: 0px; right: 40px;">
<table id="node-option-scene" style="width:100%">
<tr>
<th>Scene</th>
<th>command</th>
</tr>
</table>
</div>
</div>
</div>
</script>
<script type="text/x-red" data-help-name="ikea-homesmart">
<p>Interface for IKEA smart home devices</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">string | object</span></dt>
<dd>The payload can either be a single command to the node (such as a status update request) sent as a string or an object with one or more commands targeting the blind.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object</span></dt>
<dd>The status of the node.</dd>
<dt>topic<span class="property-type">string</span></dt>
</dl>
<h3>Details</h3>
The ikea-homesmart node can be set to control a blind, light, plug or group.
Nodes can be programmatically controlled by sending a message with <code>msg.payload</code> set to one of the following strings:
<ul>
<li><code>"status"</code> The node will output the current status of its target accessory.
</ul>
<h3>Controlling the blinds</h3>
A blind's position can be controled by sending an object with the following property as <code>msg.payload</code> to the node.
<ul>
<li><code>"position"</code>: number - The position in percent [0..100%].</li>
<li><code>"trigger"</code>: number - A trigger event to stop a moving blind [0.0].</li>
</ul>
<div><pre><code class="language-json">{ "position": number }
{ "trigger": number }</code></pre></div>
<h3>Controlling the lights</h3>
A light represents a single lightbulb and has several properties describing its state. The supported properties depend on the spectrum of the lightbulb.
<strong>All</strong> of them support the most basic properties, which can be controlled by sending an object with one or more of the following properties as <code>msg.payload</code> to the node.
<ul>
<li><code>"dimmer"</code>: number - The brightness in percent [0..100%].</li>
<li><code>"onOff"</code>: boolean - If the lightbulb is on (true) or off (false)</li>
<li><code>"transitionTime"</code>: number - The duration of state changes in seconds. Default 0.5s, not supported for on/off.</li>
</ul>
<strong>White spectrum</strong> lightbulbs also support
<ul>
<li><code>"colorTemperature"</code>: number - The color temperature in percent, where 0% equals cold white and 100% equals warm white.</li>
</ul>
<strong>RGB</strong> lightbulbs have the following properties:
<ul>
<li><code>"color"</code>: string - The 6 digit hex number representing the lightbulb's color. Don't use any prefixes like "#", only the hex number itself!</li>
<li><code>"hue"</code>: number - The color's hue [0..360°].</li>
<li><code>"saturation"</code>: number - The color's saturation [0..100%].</li>
</ul>
<div><pre><code class="language-json">{
"onOff": boolean,
"dimmer": number,
"transitionTime": number,
"colorTemperature": number,
"color": string,
"hue": number,
"saturation": number
}</code></pre></div>
<h3>Controlling the plugs</h3>
A plug represents a single outlet plug and has several properties describing its state.
And one property which can control the plugs by sending an object with the following property as <code>msg.payload</code> to the node.
<ul>
<li><code>"onOff"</code>: boolean - If the plug is on (true) or off (false).</li>
</ul>
<div><pre><code class="language-json">{ "onOff": boolean }</code></pre></div>
<h3>Controlling the groups</h3>
A group contains several devices, usually a remote control and either lightbulbs, plugs or blinds. To control the group send the following properties as <code>msg.payload</code> to the node:
<br>
<strong>For a group of lights</strong>
<ul>
<li><code>"dimmer"</code>: number - The brightness in percent [0..100%].</li>
<li><code>"onOff"</code>: boolean - If the lightbulb is on (true) or off (false)</li>
<li><code>"transitionTime"</code>: number - The duration of state changes in seconds. Default 0.5s, not supported for on/off.</li>
</ul>
Additionally, this property is also supported:
<ul>
<li><code>"sceneId"</code>: number - Set this to the instanceId of a scene (or "mood" as IKEA calls them), to activate it.</li>
</ul>
<div><pre><code class="language-json">{
"onOff": boolean,
"dimmer": number,
"transitionTime": number
}
{
"sceneId": number
}</code></pre></div>
<strong>For a group of blinds</strong>
<ul>
<li><code>"position"</code>: number - The position in percent [0..100%].</li>
<li><code>"trigger"</code>: number - A trigger event to stop a group of moving blinds [0.0].</li>
</ul>
Additionally, this property is also supported:
<ul>
<li><code>"sceneId"</code>: number - Set this to the instanceId of a scene (or "mood" as IKEA calls them), to activate it.</li>
</ul>
<div><pre><code class="language-json">{ "position": number }
{ "trigger": number }
{ "sceneId": number }</code></pre></div>
<strong>For a group of plugs</strong>
<ul>
<li><code>"onOff"</code>: boolean - If the plug is on (true) or off (false)</li>
</ul>
Additionally, this property is also supported:
<ul>
<li><code>"sceneId"</code>: number - Set this to the instanceId of a scene (or "mood" as IKEA calls them), to activate it.</li>
</ul>
<div><pre><code class="language-json">{ "onOff": boolean }
{ "sceneId": number }</code></pre></div>
<h3>Observing</h3>
If the node is set to observe it will send a message with the accessories current properties as <code>msg.payload</code> every time the node is updated or a status request <code>"status"</code> is sent.
<br><br>
<ul>
<li><code>"name"</code>: string - The name of this accessory.</li>
<li><code>"createdAt"</code>: number - The unix timestamp of the creation of the device.</li>
<li><code>"instanceId"</code>: number - The ID under which the accessory is known to the gateway.</li>
<li><code>"type"</code>: number - The type of the accessory.</li>
<ul>
<li>remote (0) - A "normal" remote</li>
<li>slaveRemote (1) - A remote which has been paired with another remote. You can find details here on how to achieve this configuration.</li>
<li>lightbulb (2) - A lightbulb</li>
<li>plug (3) - A smart plug</li>
<li>motionSensor (4) - A motion sensor</li>
<li>signalRepeater (6) - A signal repeater</li>
<li>blind (7) - A blind</li>
</ul>
<li><code>"alive"</code>: boolean - Whether the gateway considers this device as alive.</li>
<li><code>"lastSeen"</code>: number - The unix timestamp of the last communication with the gateway.</li>
<li><code>"otaUpdateState"</code>: number - Unknown. Might be a boolean</li>
<li><code>"deviceInfo"</code>: object - Some additional information about the device.</li>
<ul>
<li><code>"firmwareVersion"</code>: string - The firmware version of the device.</li>
<li><code>"manufacturer"</code>: string - The device manufacturer.</li>
<li><code>"modelNumber"</code>: string - The name/type of the device.</li>
<li><code>"power"</code>: number - How the device is powered.</li>
<ul>
<li>Unknown (0)</li>
<li>InternalBattery (1)</li>
<li>ExternalBattery (2)</li>
<li>Battery (3) - Although not in the specs, this is apparently used by the remote</li>
<li>PowerOverEthernet (4)</li>
<li>USB (5)</li>
<li>AC_Power (6)</li>
<li>Solar (7)</li>
</ul>
<li><code>"serialNumber"</code>: string - Not used currently. Always ""</li>
<li><code>"battery"</code>: number - The battery percentage of a device. Only present if the device is battery-powered.</li>
</ul>
<li><code>"blindList"</code>: array - An array of all blinds belonging to this accessory.</li>
<ul>
<li><code>"position"</code>: number - The position in percent [0..100%].</li>
</ul>
<li><code>"lightList"</code>: array - An array of all lights belonging to this accessory.</li>
<ul>
<li><code>"onOff"</code>: boolean - If the lightbulb is on (true) or off (false)</li>
<li><code>"dimmer"</code>: number - The brightness in percent [0..100%].</li>
<li><code>"transitionTime"</code>: number - The duration of state changes in seconds. Default 0.5s, not supported for on/off.</li>
<li><code>"colorTemperature"</code>: number - The color temperature in percent, where 0% equals cold white and 100% equals warm white.</li>
<li><code>"color"</code>: string - The 6 digit hex number representing the lightbulb's color.</li>
<li><code>"colorX"</code>: number - The x component of the xy-color</li>
<li><code>"colorY"</code>: number - The y component of the xy-color</li>
<li><code>"hue"</code>: number - The color's hue [0..360°].</li>
<li><code>"saturation"</code>: number - The color's saturation [0..100%].</li>
</ul>
<li><code>"plugList"</code>: array - An array of all plugs belonging to this accessory.</li>
<ul>
<li><code>"onOff"</code>: boolean - If the plug is on (true) or off (false).</li>
<li><code>"isSwitchable"</code>: boolean - Whether the plug supports on/off (always true).</li>
<li><code>"isDimmable"</code>: boolean - Whether the plug supports setting the dimmer value (always false for now).</li>
</ul>
<li><code>"sensorList"</code>: array - An array of all sensors belonging to this accessory.</li>
<ul>
<li><code>"name"</code>: string - Currently not supported.</li>
<li><code>"createdAt"</code>: number - Currently not supported.</li>
<li><code>"instanceId"</code>: number - Currently not supported.</li>
<li><code>"appType"</code>: string - Currently not supported.</li>
<li><code>"sensorType"</code>: string - Currently not supported.</li>
<li><code>"minMeasuredValue"</code>: number - Currently not supported.</li>
<li><code>"maxMeasuredValue"</code>: number - Currently not supported.</li>
<li><code>"minRangeValue"</code>: number - Currently not supported.</li>
<li><code>"maxRangeValue"</code>: number - Currently not supported.</li>
<li><code>"resetMinMaxMeasureValue"</code>: boolean - Currently not supported.</li>
<li><code>"sensorValue"</code>: number - Currently not supported.</li>
<li><code>"unit"</code>: string - Currently not supported.</li>
</ul>
<li><code>"switchList"</code>: array - An array of all remotes belonging to this accessory.</li>
<ul>
<li><code>"name"</code>: string - Currently not supported.</li>
<li><code>"createdAt"</code>: number - Currently not supported.</li>
<li><code>"instanceId"</code>: number - Currently not supported.</li>
</ul>
</ul>
Currently observing the control of a group is not possible, due to the gateway only reporting changes to every single device.
If a group is renamed, a device removed or added to it, a scene redefined or
manually a status request sent to the node, it will respond with a <code>msg.payload</code> containing the groups current properties.
<br><br>
<ul>
<li><code>"name"</code>: string - The name of this group.</li>
<li><code>"createdAt"</code>: number - The unix timestamp of the creation of the group.</li>
<li><code>"instanceId"</code>: number - The ID under which the group is known to the gateway.</li>
<li><code>"onOff"</code>: boolean - If the group is on (true) or off (false)
<li><code>"dimmer</code>: number - The brightness in percent [0..100%].
<li><code>"deviceIDs</code>: object - An array of all instanceIds of the devices in this group.</li>
<li><code>"sceneId</code>: number - the instanceId of the current scene (or "mood" as IKEA calls them).</li>
<li><code>"groupType"</code>: number - The type of the group.</li>
</ul>
</script>