Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added Support for Hue Tap dial switch #10

Merged
merged 2 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ _Please make sure that you deactivate other Hue-related nodes in Node-RED and me
- [Hue Temperature](#hue-temperature)
- [Hue Brightness](#hue-brightness)
- [Hue Rule](#hue-rule)
- [Hue Dial](#hue-dial)

### Examples

Expand Down Expand Up @@ -814,8 +815,72 @@ Contains the complete object (see output values above) of the last status before

If the status of the node has changed via a certain command, the entire command that was executed is also output. Otherwise this object will not be output by the node.

## Hue Dial
The "Hue Dial" node receives rotation events from input devices(like Hue tap dial switch) connected to the bridge. Note: For buttons actions use [Hue Buttons](#hue-buttons)

### Node-RED Setup Instructions

Select the pre-configured Hue Bridge and hit the search button to find all the available dial switches. If you already know the ID of the dial switch, you can also enter it here manually. You can either assign a new name for the dial switch internally or keep the predefined name of the device.

### Get status

Outputs the current status of the dial switch as soon as a `msg.payload` object with the following content has been passed to the node. Alternatively, you can also press the button in the Node-RED interface without having to pass a message to the node beforehand.

|Property|Description|
|--|--|
| status (boolean) | Returns the current status of the switch/button |

### Status messages from the node

As soon as a rotation has been performed, the following status message is returned by the node:

#### Status object under `msg.payload`

|Property|Description|
|--|--|
| reachable (boolean / string) | `true` if the dial switch is connected to the bridge, `unknown` if the connection status deviates |
| connectionStatus (string) | The current connection status with the bridge in the form of a string. Can contain `connected`, `disconnected`, `connectivity_issue` or `unidirectional_incoming` as a value |
| action (string) | `start` on start of rotation or `repeat` when the rotation is continued in the form of a string |
| rotation (object) | Object with the information of `direction`, `steps` and `duration` |
| rotation.direction (string) | `counter_clock_wise` or `clock_wise` in the form of a string |
| rotation.steps (int) | count of steps when dial is rotated in the form of a number. |
| rotation.duration (int) ||
| updated (string) | Time of the last update of the resource by HueMagic (ISO 8601) |

#### Information about the switch/button under `msg.info`

|Property|Description|
|--|--|
| id (string) | Indicates the new ID of the dial switch |
| idV1 (string / boolean) | Indicates the old ID of the dial switch |
| uniqueId (string) | The unique ID of the dial switch |
| deviceId (string) | The unique ID of the device |
| name (string) | The currently set name of the switch/button |
| type (string) | The type of the switch/button (always `dial`) |
| softwareVersion (string) | The current firmware of the switch/button |
| battery (float / boolean) | The current battery level of the switch/button, `false`, when there is no battery |
| batteryState (string / boolean) | The current status of the battery level. Can contain `normal`, `low` or `critical` as a value, `false`, when there is no battery |
| model (object) | Contains the model information of the switch/button under `id` , `manufacturer`, `name`, `type` and `certified` |

#### Status changes under `msg.updated`

Lists individual parameters in the form of an object that have changed compared to the last state of the dial switch. If no changes have been registered, this object is empty.

#### Last status of the dial switch under `msg.lastState`

Contains the complete status object (see output values above) of the last status before the last registered change. If the last state of HueMagic has not yet been registered, it will output `false`.

#### Last command under `msg.command` (optional)

If the status of the node has changed via a certain command, the entire command that was executed is also output. Otherwise this object will not be output by the node.


# Changelog

### v4-next0

* Added Support for Hue Tap dial switch

### v4.2.6 (latest)

* Fork contrib migration instructions added to README
Expand Down
21 changes: 19 additions & 2 deletions huemagic/hue-bridge-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ module.exports = function(RED)
HueTemperatureMessage,
HueBrightnessMessage,
HueButtonsMessage,
HueRulesMessage
HueRulesMessage,
HueDialMessage
} = require('./utils/messages');

function HueBridge(config)
Expand Down Expand Up @@ -463,6 +464,21 @@ module.exports = function(RED)
return false;
}
}
else if (type == "relative_rotary")
{
try {
const message = new HueDialMessage(targetResource, options);
// GET & SAVE LAST STATE AND DIFFERENCES
let currentState = message.msg;
scope.lastStates[type + targetResource.id] = Object.assign({}, currentState);
currentState.updated = (lastState === false) ? {} : diff(lastState, currentState);
currentState.lastState = lastState;

return currentState;
} catch (error) {
return false;
}
}
else
{
return false;
Expand Down Expand Up @@ -596,7 +612,8 @@ module.exports = function(RED)
"light_level": ["light_level", "zigbee_connectivity", "zgp_connectivity", "device_power", "device"],
"button": ["button", "zigbee_connectivity", "zgp_connectivity", "device_power", "device"],
"group": ["group", "light", "grouped_light"],
"rule": ["rule"]
"rule": ["rule"],
"relative_rotary": ["relative_rotary", "zigbee_connectivity", "zgp_connectivity", "device_power", "device"],
};

if(!id)
Expand Down
209 changes: 209 additions & 0 deletions huemagic/hue-dial.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<script type="text/x-red" data-template-name="hue-dial">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="hue-dial.config.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]hue-dial.config.input-name" style="width: calc(100% - 105px)">
</div>
<div class="form-row">
<label for="node-input-bridge"><i class="fa fa-server"></i> Bridge</label>
<input type="text" id="node-input-bridge" style="width: calc(100% - 105px)">
</div>
<div class="form-row">
<label for="node-input-dialid"><i class="fa fa-dialbulb-o"></i> <span data-i18n="hue-dial.config.dial"></span></label>
<div style="display: inline-flex; width: calc(100% - 105px)">
<div id="input-select-toggle" style="flex-grow: 1;">
<input type="text" id="node-input-dialid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%"/>
</div>
<button id="node-config-input-scan-dials" type="button" class="red-ui-button" style="margin-left: 10px;">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="form-row" style="margin-top: 30px">
<div style="display: inline-flex; width: calc(100% - 105px)">
<input type="checkbox" id="node-input-skipevents" style="flex: 15px;">
<span data-i18n="hue-dial.config.skipevents-node" style="width: 100%; margin-left: 10px;"></span>
</div>
</div>
<div class="form-row">
<div style="display: inline-flex; width: calc(100% - 105px)">
<input type="checkbox" id="node-input-initevents" style="flex: 15px;">
<span data-i18n="hue-dial.config.sendinitevents-node" style="width: 100%; margin-left: 10px;"></span>
</div>
</div>

</script>

<script type="text/javascript">
RED.nodes.registerType('hue-dial', {
category: 'HueMagic',
color: '#03a9f4',
defaults: {
name: {value: ""},
bridge: {type: "hue-bridge", required: true},
dialid: {value: "", required: false},
skipevents: { value: false },
initevents: { value: false },
},
align: 'left',
icon: "hue-dial.png",
inputs: 1,
outputs: 1,
label: function () {
return this.name || this._("hue-dial.node.title");
},
paletteLabel: function () {
return this._("hue-dial.node.title");
},
inputLabels: function () {
return this._("hue-dial.node.input");
},
outputLabels: function () {
return this._("hue-dial.node.output");
},
oneditprepare: function () {
const scope = this;
let options = [];

function manualInput() {
// GET CURRENT SELECTED VALUE
var current = $('#node-input-dialid').val();

// REMOVE SELECT FIELD
$('#input-select-toggle').empty();

// CREATE NEW INPUT FIELD
$('#input-select-toggle').append('<input type="text" id="node-input-dialid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%" value="' + current + '" />');

// CHANGE BUTTON ICON
var button = $("#node-config-input-scan-dials");
var buttonIcon = button.find("i");
buttonIcon.removeClass("fa-pencil");
buttonIcon.addClass("fa-search");
}

function sortResourcesBy(prop, resources) {
return resources.sort((a, b) => {
if (a[prop] < b[prop]) {
return -1;
}
if (a[prop] > b[prop]) {
return 1;
}
return 0;
});
}

function searchAndSelect() {
// GET CURRENT BRIDGE CONFIGURATION
var bridgeConfig = RED.nodes.node($('#node-input-bridge option:selected').val());
if (!bridgeConfig) {
return false;
}

// GET CURRENT SELECTED VALUE
var current = $('#node-input-dialid').val();

// TRIGGER SEARCHING NOTIFICATION
var notification = RED.notify(scope._("hue-dial.config.searching"), {
type: "compact",
modal: true,
fixed: true
});

// GET THE dialS
$.get('hue/resources', {bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "button"})
.done(function (data) {
var allResources = JSON.parse(data);
allResources = sortResourcesBy('name', allResources);
if (allResources.length <= 0) {
notification.close();
RED.notify(scope._("hue-dial.config.none-found"), {type: "error"});
return false;
}

// SET OPTIONS
allResources.forEach(function (resource) {
if (resource.model) {
if (resource.model == 'Hue tap dial switch') {
options[resource.id] = {
value: resource.id,
label: resource.name + " (" + resource.model + ")"
};
}
} else {
options[resource.id] = {value: resource.id, label: resource.name};
}
});

// SELECT CURRENT VALUE
$("#node-input-dialid").typedInput({
types: [
{
value: current,
options: Object.values(options)
}
]
});

// CHANGE BUTTON ICON
var button = $("#node-config-input-scan-dials");
var buttonIcon = button.find("i");
buttonIcon.removeClass("fa-search");
buttonIcon.addClass("fa-pencil");

// CLOSE SEARCH NOTIFICATION
notification.close();
})
.fail(function () {
notification.close();
RED.notify(scope._("hue-dial.config.unknown-error"), "error");
});
}

// CHANGED dial ID? -> REPLACE NAME (IF POSSIBLE)
$(document).on('change', '#node-input-dialid', function (e) {
let currentSelectedOptionID = $(e.currentTarget).val();
let currentSelectedOptionValue = (currentSelectedOptionID.length > 0 && options[currentSelectedOptionID]) ? options[currentSelectedOptionID].label : false;

if (currentSelectedOptionValue !== false) {
$('#node-input-name').val(currentSelectedOptionValue.split(" (")[0]);
}
});

// TOGGLE SELECT/INPUT FIELD
$('#node-config-input-scan-dials').click(function () {
if ($('#input-select-toggle').find(".red-ui-typedInput-container").length > 0) {
manualInput();
} else {
searchAndSelect();
}
});
},
button: {
enabled: function () {
return (this.dialid && this.dialid.length > 1)
},
visible: function () {
return (this.dialid && this.dialid.length > 1)
},
onclick: function () {
const node = this;
if (node.bridge) {
$.ajax({
url: "inject/" + node.id,
type: "POST",
data: JSON.stringify({__user_inject_props__: "status"}),
contentType: "application/json; charset=utf-8",
success: function (resp) {
RED.notify(node.name + ": " + node._("hue-dial.node.statusmsg"), {
type: "success",
id: "status",
timeout: 2000
});
}
});
}
}
}
});
</script>
Loading