@Emil Sorry. Didn't write that it's when I'm in my car on my way home.😉
-
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 have Home Assistant running on my Raspberry Pi 4B as the OS (not docker).
Directly underneath my RPi4 is my Flic Hub LR. Both are connected to my router via Ethernet.
Given that Home Assistant is so popular I, foolishly, believed it would be straightforward to connect the two via the Home Assistant Integrations... it isn't!
Home Assistant is asking for a "pairing code". Flic support in this forum is saying that the code is not available.
I don't have a HomePod and don't want to spend £100 on buying one, though I am an Apple user (phone, tablet, and Macbook).
Surely, there must be a simple way to be able to use my Flic Hub LR within Home Assistant... But how?
-
Half the time I use my Flic button to run play, pause, next, previous, or play playlist on my iPhone, I get the message 'Spotify Web Connect: No active playback session'. This even happens if I've been playing songs on Spotify for more than a few minutes. Reopening the app fixes the problem, but this totally defeats the point of having a button to run these commands.
And forget about playing something from scratch, even if I choose a specific device on which to play it. Are there iOS/Spotify API limitations at play here, or is a Flic-related fix coming?
-
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.