Commit 3d88d32c authored by Erick Hitter's avatar Erick Hitter

Merge branch 'develop' into 'master'

Add support for "double-click" and "hold" click types

Closes #1

See merge request !2
parents b7bc4b1f 54fd6ba1
......@@ -30,11 +30,11 @@ If `flicd` and this script are not run from the same device, ensure that `flicd`
**Can I run `flicd` as a daemon/at startup?**
Yes. See https://ethtiter.com/ for a `systemd` service file.
Yes. See https://ethitter.com/p/9738/ for a `systemd` service file.
**How is the `buttons` property of `config.json` structured?**
Each object within the `buttons` property is structured according to how the button should respond to the entity's current state. For example, if the light is off, what should a button press do.
Each object within the `buttons` property is structured according what effect the click should have on an entity in its current state. For example, if the light is off, what should a single-click do.
"mac": {
"mac": "mac",
......@@ -42,15 +42,17 @@ Each object within the `buttons` property is structured according to how the but
"status": {
"entity_id": "switch.lamp"
},
"off": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
},
"on": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_off"
"single": {
"on": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_off"
},
"off": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
}
}
}
......@@ -62,21 +64,71 @@ The above example simply switches a light on and off, depending on its current s
"status": {
"entity_id": "alarm_control_panel.shm"
},
"disarmed": {
"entity": "alarm_control_panel",
"entity_id": "alarm_control_panel.shm",
"entity_action": "alarm_arm_home"
"single": {
"disarmed": {
"entity": "alarm_control_panel",
"entity_id": "alarm_control_panel.shm",
"entity_action": "alarm_arm_home"
},
"armed_home": {
"entity": "alarm_control_panel",
"entity_id": "alarm_control_panel.shm",
"entity_action": "alarm_disarm"
},
"armed_away": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
}
}
}
In this example, if the alarm isn't active, or is set to the "armed home" state, its Flic button will toggle between those states. If, however, one tried to use the button to disable the alarm when no one is home (motion sensors are active, among other changes), the button will simply turn on a light; it won't disarm the alarm, as that's something I only allow for those who have access to Home Assistant or SmartThings directly.
As three click types--`single`, `double`, and `hold`--are supported, adding responses to the new click types is a matter of updating `config.json` to define how those clicks are handled.
"mac": {
"mac": "mac",
"label": "Lamp",
"status": {
"entity_id": "switch.lamp"
},
"single": {
"on": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_off"
},
"off": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
}
},
"armed_home": {
"entity": "alarm_control_panel",
"entity_id": "alarm_control_panel.shm",
"entity_action": "alarm_disarm"
"double": {
"on": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_off"
},
"off": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
}
},
"armed_away": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
"hold": {
"on": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_off"
},
"off": {
"entity": "switch",
"entity_id": "switch.lamp",
"entity_action": "turn_on"
}
}
}
In this example, if the alarm isn't active, or is set to the "armed home" state, its Flic button will toggle between those states. If, however, one tried to use the button to disable the alarm when no one is home (motion sensors are active, among other changes), the button will simply turn on a light; it won't disarm the alarm, as that's something I only allow for those who have access to Home Assistant or SmartThings directly.
This isn't a particularly interesting example, as all three click types perform the same action, but it demonstrates what's possible.
......@@ -12,17 +12,43 @@
"status": {
"entity_id": ""
},
"on": {
"entity": "",
"entity_id": "",
"entity_action": ""
"single": {
"on": {
"entity": "",
"entity_id": "",
"entity_action": ""
},
"off": {
"entity": "",
"entity_id": "",
"entity_action": ""
}
},
"off": {
"entity": "",
"entity_id": "",
"entity_action": ""
"double": {
"on": {
"entity": "",
"entity_id": "",
"entity_action": ""
},
"off": {
"entity": "",
"entity_id": "",
"entity_action": ""
}
},
"hold": {
"on": {
"entity": "",
"entity_id": "",
"entity_action": ""
},
"off": {
"entity": "",
"entity_id": "",
"entity_action": ""
}
}
}
},
"config_version": 2
"config_version": 3
}
......@@ -13,8 +13,8 @@ var request = require( 'request' );
*/
var config = require( './config.json' );
if ( 'undefined' === typeof config.config_version || config.config_version < 2 ) {
console.error( 'Configuration is for v0.1.0 and must be updated to work with this release. Exiting!' );
if ( 'undefined' === typeof config.config_version || config.config_version < 3 ) {
console.error( 'Configuration is for a release prior to v0.2.5 and must be updated to work with this release. Exiting!' );
process.exit();
}
......@@ -40,24 +40,53 @@ client.on( 'newVerifiedButton', function( bdAddr ) {
* BUTTON REACTIONS
*/
function listenToButton( bdAddr ) {
// Not much to do if the button isn't configured
if ( "object" !== typeof config.buttons[bdAddr] ) {
console.error( 'No configuration for button ' + bdAddr );
return;
}
// Establish button connection
var cc = new FlicConnectionChannel( bdAddr );
client.addConnectionChannel( cc );
cc.on( 'buttonUpOrDown', function( clickType, wasQueued, timeDiff ) {
if ( 'ButtonDown' !== clickType ) {
// Bind to our desired click type; see ProtocolDocumentation.md in fliclib-linux-hci
cc.on( 'buttonSingleOrDoubleClickOrHold', function( clickType, wasQueued, timeDiff ) {
// Don't handle long-queued events, to avoid excessive toggling of HA entities
if ( wasQueued && timeDiff > 2 ) {
return;
}
if ( wasQueued && timeDiff > 2 ) {
// Convert the verbose click name to something convenient
// Also validates click type
var parsedClickType = '';
switch ( clickType ) {
case 'ButtonSingleClick':
parsedClickType = 'single';
break;
case 'ButtonDoubleClick':
parsedClickType = 'double';
break;
case 'ButtonHold':
parsedClickType = 'hold';
break;
default:
// Do nothing, this type isn't supported
break;
}
// Click type is unsupported
if ( 0 === parsedClickType.length ) {
console.error( 'Unsupported click type ' + clickType );
return;
}
buttonActivated( bdAddr );
// Time to pass the click to HA
buttonActivated( bdAddr, parsedClickType );
} );
}
......@@ -68,9 +97,24 @@ function listenToButton( bdAddr ) {
/**
* Make HTTP(s) requests to Home Assistant to toggle status of device assigned to pushed button
*/
function buttonActivated( mac ) {
var buttonConfig = config.buttons[mac];
function buttonActivated( mac, clickType ) {
// Check for a button config
if ( 'undefined' === typeof config.buttons[ mac ] ) {
console.error( 'No configuration for button ' + mac );
return;
}
var buttonConfig = config.buttons[ mac ];
// Check for a config for this click type
if ( 'undefined' === typeof buttonConfig[ clickType ] ) {
console.error( 'No configuration for click type `' + clickType + '` on button "' + buttonConfig.label + '"' );
return;
}
var buttonClickConfig = buttonConfig[ clickType ];
// Check current state of button's entity
var req = {
url: homeAssistantApiBase + 'states/' + buttonConfig.status.entity_id,
headers: {
......@@ -95,12 +139,13 @@ function buttonActivated( mac ) {
// HA only deals with JSON
body = JSON.parse( body );
// Use entity's current state to determine new state, per config
var service = 'services/',
postBody = {};
if ( 'object' === typeof buttonConfig[ body.state ] ) {
service += buttonConfig[ body.state ].entity + '/' + buttonConfig[ body.state ].entity_action;
postBody.entity_id = buttonConfig[ body.state ].entity_id;
if ( 'object' === typeof buttonClickConfig[ body.state ] ) {
service += buttonClickConfig[ body.state ].entity + '/' + buttonClickConfig[ body.state ].entity_action;
postBody.entity_id = buttonClickConfig[ body.state ].entity_id;
} else {
console.error( 'Unhandled state: ' + body.state );
return;
......@@ -111,7 +156,7 @@ function buttonActivated( mac ) {
req.method = 'POST';
req.body = JSON.stringify( postBody );
// Make request to change alarm status
// Make request to update entity status
request( req, function( err, res, body ) {
// Handle error states
if ( err ) {
......
{
"name": "flic-button-home-assistant-controller",
"version": "0.2.0",
"version": "0.2.5",
"description": "Control Home Assistant entities with Flic buttons",
"author": "Erick Hitter <contact@ethitter.com>",
"license": "GPL-2.0+",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment