@Emil Sorry. Didn't write that it's when I'm in my car on my way home.😉
-
Hello,
I'm looking for some confirmations around a few things I've noticed while setting up some DUOs and a Hub LR.I've noticed that Duos setup with the Hub have different capabilities than Duos set up under my Phone. In the App, you choose between Phone or Hub before setting them up. When chatting with the Flic Hub Studio Assistant on ChatGPT, it told me that Flic 2 has the ability to use the hub as a fallback, so, as an example, a Hold command that is NOT defined on the phone, would be picked up by the Hub. However, the agent realized I was talking about the Duo, and said no, there is no fallback behavior. You can only assign a Duo to a phone OR a Hub. So, what is the truth here?
Since Twist for Volume is only allowed on Hub-set-up Duos, how can I control the volume of my phone via Twist? Is there a way to have the button toggle between Hub-connected and Phone-connected? That way I can control Volume, then flip it back to be media controls?
Various Actions-
"Change Config" - This seems like a neat action. Effectively swap between "action sets". Unfortunately, it seems like an obsolete action? Can't set it up.
"Flic Control" - What would be the purpose of this? Pause/Resume/Dis/Connect a Flic. Why?
"Delay" - When I go to use this, it warns me that the Flic will become Universal, and that settings will be written to the button itself. Does this mean I could create a "media controller" and connect it to another device I have over bluetooth? Are there only specific settings which would work in this mode?I'm trying to explore the use of Duo's twists. I'm watching the console in the hub through a script for displaying the "virtualDeviceUpdate" function. This only shows up for virtual devices. How can I see the messages coming in for non-virtual devices? Do I need to always have a duplicate virtual device for each device?
Thanks
-
I had my Matter Bridge commissioned to my Flic hub and all working great,
Accidentally I managed to decommision the bridge from the flic fabric via the iOS Matter settings but it didn't remove from the flic app.
Now I'm in a state where the flic app thinks there is a bridge (and all its associated devices) there but it shows as offline and I can't remove it.
I managed to recommision the Bridge back to flic but now I have a duplicate set of 22 device in my list!
Is there a way I can remove the old bridge from the Flic Hub?
24f86297-d670-4628-8f9d-09b3bef31c63-Screen Shot 2026-01-06 at 8.59.22 AM.png
-
What is it?
Hub TCP server for using the IR module via HTTP.
Both to record of new signals with a given name and to play them back.
Using net since SDK doesn't have the nodejs HTTP-module available.Why?
I really wanted to be able to call my IR module from my phone homescreen or Google Home (via IFTTT). I got it working so I wanted to share it if anyone else find it helpful 🙂The code is what could be called a POC, so code feedback and/or changes are totally welcome.
How does it work?
Record a signal GET {ip}:1338?record={name_of_signal} Plays a signal GET {ip}:1338?cmd={name_of_signal} Cancel recording GET {ip}:1338?cancelRecord=trueExample;
call GET 192.168.0.100:1338?record=volume_up Press Volume Up on remote towards IR module returns 200 OK Signal volume_up stored! call GET 192.168.0.100:1338?cmd=volume_up returns 200 OK Signal sent! Volume goes up 🙂The code
module.json
{ "name": "IRTCP", "version": "1.0.0" }main.js
// main.js const net = require("net"); const ir = require("ir"); const datastore = require("datastore"); const utils = require("./utils"); const respondToClient = function(c, statusCode, message) { c.write( "HTTP/1.1 " + statusCode + "\r\n" + "\r\n" ); c.write(message); c.end(); } const handleCmd = function(c, cmd) { datastore.get(cmd, function(err, strBuffer) { if (!err && strBuffer) { var signal = new Uint32Array(utils.str2ab(strBuffer)); if (!ArrayBuffer.isView(signal)) { return respondToClient(c, "422 Unprocessable Entity", "Unknown signal"); } ir.play(signal, function(err) { if (!err) { return respondToClient(c, "200 OK", "Signal sent"); } return respondToClient(c, "500 Internal Server Error", "Couldn't send signal"); }); } else { return respondToClient(c, "422 Unprocessable Entity", "Unknown cmd"); } }); } const handleRecord = function(c, name) { /* TODO: add a timeout for listening for complete */ ir.record(); console.log("recording signal..."); ir.on("recordComplete", function(signal) { datastore.put(name, utils.ab2str(signal.buffer), function(err) { if (!err) { console.log("Signal " + name + " stored!"); return respondToClient(c, "200 OK", "Signal " + name + " stored!"); } else { return respondToClient(c, "500 Internal Server Error", "Couldn't store signal"); } }); }); } var server = net.createServer(function(c) { console.log('server connected'); c.on('end', function() { console.log('server disconnected'); }); c.on('data', function(data) { console.log(data); var match = data.toString().match(/GET \/\?.[^ ]*/); if (!match) { return respondToClient(c, "403 Forbidden", "Forbidden method"); } var params = utils.parseParams(match[0].split("GET /?")[1]); if (params.cmd) { return handleCmd(c, params.cmd); } else if (params.record && params.record.length > 0) { return handleRecord(c, params.record); } else if (params.cancelRecord) { ir.cancelRecord(); console.log("recording canceled!"); return respondToClient(c, "200 OK", "Recording canceled!"); } else { return respondToClient(c, "403 Forbidden", "Forbidden params"); } }); }); server.listen(1338, "0.0.0.0", function() { console.log('server bound', server.address().port); });utils.js
const ab2str = function(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf)); } const str2ab = function(str) { var buf = new ArrayBuffer(str.length*2); var bufView = new Uint16Array(buf); for (var i=0, strLen=str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } const parseParams = function (s) { var obj = {}; var arr = s.split("&"); var temp; for (i = 0; i < arr.length; i++) { temp = arr[i].split("="); obj[temp[0]] = temp[1]; } return obj; }; exports.ab2str = ab2str; exports.str2ab = str2ab; exports.parseParams = parseParams; -
I just spent too long trying to figure this out since the documentation is not up to date and the chatGPT bot is just wrong.
Anyways I thought I would share how I am handling my first Flic Duo device to trigger different Home Assistant automations using the different buttons and webhooks . Hopefully this might save other people some time and effort.
main.js var buttonManager = require("buttons"); var http = require("http"); buttonManager.on("buttonSingleOrDoubleClickOrHold", function(obj) { var button = buttonManager.getButton(obj.bdaddr); var clickType = obj.isSingleClick ? "click" : obj.isDoubleClick ? "double_click" : "hold"; var payload = JSON.stringify({ button_name: button.name, click_type: clickType, }); var targetUrl = null; if (button.name === "lock") { targetUrl = "http://XXX.XXX.XX.XX:8123/api/webhook/flic-lock"; } else if (button.name === "auto1") { targetUrl = "http://XXX.XXX.XX.XX:8123/api/webhook/flic-leavingHome"; } else if (button.name === "duo1") { if (obj.buttonNumber === 0) { targetUrl = "http://XXX.XXX.XX.XX:8123/api/webhook/flic-duo1-big"; } else if (obj.buttonNumber === 1) { targetUrl = "http://XXX.XXX.XX.XX:8123/api/webhook/flic-duo1-small"; } } if (targetUrl) { http.makeRequest({ url: targetUrl, method: "POST", headers: {"Content-Type": "application/json"}, content: payload, }, function(err, res) { console.log(""); console.log("Sent to: " + targetUrl); console.log("Name: " + button.name); console.log("ID (bdaddr): " + button.bdaddr); console.log("Serial: " + button.serialNumber); console.log("Click Type: " + clickType); console.log("uuid: " + button.uuid); console.log("key: " + button.key); //console.log("Button #: " + obj.buttonNumber); console.log("Status: " + res.statusCode); }); } }); console.log("Script started");obj.buttonNumber returns 0 for the upper bigger button and 1 for the smaller lower button
You can also perform different automations depending on click type using an if statement where you change the keyword click using something like (YAML):
conditions: - condition: template value_template: "{{ trigger.json.click_type == 'click' }}"The HA automation webhook trigger YAML looks something like:
triggers: - allowed_methods: - POST - PUT local_only: true webhook_id: flic-leavingHome trigger: webhookThis might not be the best way to go about this but I would be interested to hear suggestions for improvements. Now do add the rest of the new buttons and model the dimensions as a 3D STL file. It would be nice if flic also provided this as well.