Direct Flic BUTTON via Flic Hub MQTT integration - Cloudless

Thanks Ingo, your updated script seems to have fixed the issues of persistence following a reboot and the annoying MQTT “error” message. Thanks for all your work on this (and those who contributed, too!)

Hi,
I have set up & seeing my buttons in HA
. However they don’t show up as device - They show up as sensor’s “button action” “button state” . I’m not good with YAML & I tend to use GUI for setting up automations so i’m unsure how get these to switch on something or action something… Any advice would appreciated…

Thanks for this by the way, i might have some use for these buttons after a couple of years of ownership…

It appears we all do our logic differently. I’ve never used the Device as a trigger, I always use the State as a Trigger. If you use that, it definitely appears:

Ingo, Thanks that works perfect… Everyday is a school day…

I’m having issues since the latest update. I noticed one of the break changes is,

“MQTT device trackers no longer always update when there is no state change. This means that automations that trigger on a state change no longer are triggered if there is no change.”

Not sure if I need to change something or if I simply need to wait for the next update to fix the issue.
Thanks again for all the support!!

Thanks for the worlk on this integration (on the Flic Hub side).

A small note to international users:
If you use UTF8 chars in your button name, they will not show up in HA!
As I’m in Denmark, I named my buttons in the Flic App with danish letters.

Ex:
Køkken (kitchen)
Soveværelse (bedroom)

Changing the none-ascii character to ascii, makes them show up.

Not shure if it’s possible to mark the Name as UTF8, and make it work…?

Anyone know if Flic Twist support can be added to flic2hass? I just received my Twist and see that the flic2hass module already recognizes the button presses but unsurprisingly none of the twist actions work. Hopefully the Flic devs didn’t make it too difficult to integrate. I do suspect it will be a tough challenge.

It should be possible once they add it to the SDK, but I don’t yet see any mention of Twist on Flic Hub SDK Documentation – that’s assuming the Twist uses the same Flic Hub to communicate to the world? I don’t own one, so I really don’t know.

Once they add that, someone should be able to add the code to this, test it, and submit it for inclusion.

1 Like

I just got this in an email from Flic:

Twist SDKs

Twist support will come to the Flic Hub SDK, however, we do not yet have a release date as we are still processing feedback and working on the details of the API. This is to ensure that we can provide a solution that would make sense both for fully custom integrations, where the SDK more or less assumes total control over the Twist, and also scenarios where you might want to extend the functionality and work well with other integrations configured in the app. Fulfilling these two demands, and everything in between, has turned out to be less simple than it seems at first glance.

Home Assistant

We have seen a big demand for an official Home Assistant integration so this is definitely something that we will look into. However, at this time we cannot promise any timeline for this integration since we need to finish the Matter integration first and also investigate exactly how an integration would be created that would work for both Twists and buttons.

I got my Flic Twists this morning. They connect to my existing hub perfectly fine (thus meaning I spent €19 on a new hub, but I might try it out anyway) and the button records click, double_click and hold perfectly fine. It’s just the twisting bit that doesn’t register. So that should be the only part that needs any work.

Oh, as an aside, remember to give the Twist a name in the Flic app, or it gets registered as a null device.

Just in case someone runs into this issue: renaming a flic or twist button in the flic app requires (sometimes / all the time?) stopping & restarting the MQTT module on the flic hub to make the new name available to home assistant

I’ve been reading through and not sure what I did wrong but I keep getting the following;

  'Error' event
  
  ReferenceError: identifier 'mqtt' undefined
      at [anon] (duk_js_var.c:1236) internal
      at [anon] (root/MQTT/mqtt.js:586) preventsyield

I’ve been reading through and my details are as follows;

  var server = "mqtt://core-mosquitto:1883";
  var hatopic = "[email protected]";
  var flictopic = "flic";
  var mqtt = require("./mqtt").create(server);

I don’t know if I’ve missed something but I can access the FlickHub via the SDK and I’ve added all the code/files.

If it helps, I’m using Zigbee2MQTT with a SONOFF Universal Zigbee 3.0 USB Dongle.

I was having a play with the Twist and I discovered the ring of lights only works when the action is linked to a device, such as a dimmer or volume control. I figured this was why the action wasn’t being picked up, so I linked it to my remaining Hue controlled light and watched for any output in the SDK. Nothing was registered, which I guess is no surprise as it doesn’t look like the code is set up to report it. But I’m also thinking that there’d need to be some sort of two way communication between the Twist and whatever it was controlling for the lights to work properly. Either way, Flic have clearly done something to the Twist to make the dial option work in a different way to the button.

This would be so much easier if Flic opened their hub up to be directly supported in HA. I guess this is too much to ask…

The Flic SDK has zero support for the Twist at all right now. I’ve looked through the documentation, inspected the javascript objects, etc. and… there’s nothing to suggest these events can be received by the SDK. I sent an email to their support asking about it.

Very disappointed about this, considering the Flic Twist Indiegogo page still clearly states that the SDK can be used at launch. This makes the whole product worthless to me… I already have plenty of smart buttons, and I don’t use any of the integrations natively supported by Flic.

This is what my main.js looks like at the top:

var server = "mqtt.home";
var hatopic = "homeassistant";
var flictopic = "flic";

It looks like your hatopic is incorrect, at least.

Thanks for building the integration, made my life much easier!
I was contemplating using flicd but it introduced extra complexities as i’m running HASS in the multinode Kubernetes cluster and the need to use a dedicated Bluetooth adapter instead of the proxy was a bummer. This integration however changes everything.
I’ve opened a PR with a few minor cosmetic suggestions: Server Options and Readme Updates by maxim-mityutko · Pull Request #7 · id628/flic2hass · GitHub

Hi all, very new to all of this so please bear with me!

Installed as described above using the code below and getting the following error in the Flic SDK -

‘Error’ event

Error: Crashed
at [anon] (root/MQTT/main.js:229) preventsyield

Have tested the MQTT broker on HA and it appears to be working.

Can anyone advise. I copied and pasted the code exactly as it was on GitHub. Have removed password for obvious reasons! TIA :slight_smile:

main.js -

var server = “192.168.68.124”;
var server_options = {
//“username”: “mqtt-user”,
//“password”: “********”,
“port”: 1883,
}
var hatopic = “homeassistant”;
var flictopic = “flic”;
var mqtt = require(“./mqtt”).create(server, server_options);

var buttonManager = require(“buttons”);
var myButtons = {}; //Dictionary of Button objects

//This runs when a button is added via Flic’s interface
buttonManager.on(“buttonAdded”, function(obj){
var button = buttonManager.getButton(obj.bdaddr);

console.log("\nNew button Added! "+button.serialNumber+" "+button.name);
console.log("\nRAW obj info: " + JSON.stringify(obj, null, 4) + "\n");
console.log("\nRAW button info: " + JSON.stringify(button, null, 4) + "\n");

if (button.serialNumber == null || button.name == null) {
	return;
}

if (button.serialNumber) {
	register_button(button);
}

});

//This runs when a button is added via Flic’s interface
buttonManager.on(“buttonConnected”, function(obj){
var button = buttonManager.getButton(obj.bdaddr);

console.log("\nNew button Connected! "+button.serialNumber+" "+button.name);
console.log("\nRAW obj info: " + JSON.stringify(obj, null, 4) + "\n");
console.log("\nRAW button info: " + JSON.stringify(button, null, 4) + "\n");


if (button.serialNumber == null || button.name == null) {
	return;
}

if (button.serialNumber) {
	register_button(button);
}

});

//This runs when a button is added via Flic’s interface
buttonManager.on(“buttonReady”, function(obj){
var button = buttonManager.getButton(obj.bdaddr);

console.log("\nNew button Ready! "+button.serialNumber+" "+button.name);
console.log("\nRAW obj info: " + JSON.stringify(obj, null, 4) + "\n");
console.log("\nRAW button info: " + JSON.stringify(button, null, 4) + "\n");

if (button.serialNumber == null || button.name == null) {
	return;
}

if (button.serialNumber) {
	register_button(button);
}

});

//This runs when a button is deleted via Flic’s interface
buttonManager.on(“buttonDeleted”, function(obj){
//Well crap. If it’s deleted, it doesn’t have an object anymore.
//So that means I have to save it by address, not ID so I can delete it from MQTT later
console.log("\nDeleting button “+obj.bdaddr);
console.log(”\nRAW obj info: " + JSON.stringify(obj, null, 4) + “\n”);

var configtopic = hatopic+"/sensor/"+myButtons[obj.bdaddr].serialNumber;
mqtt.publish(configtopic+"/action/config", null, {retain: false } );
if (myButtons[obj.bdaddr]) {
	delete myButtons[obj.bdaddr];
	//myButtons.delete[obj.bdaddr];
}

});

buttonManager.on(“buttonSingleOrDoubleClickOrHold”, function(obj) {
//This runs when a button is clicked.

var button = buttonManager.getButton(obj.bdaddr);
console.log("\nRAW obj info: " + JSON.stringify(obj, null, 4) + "\n");
console.log("\nRAW button info: " + JSON.stringify(button, null, 4) + "\n");

var clickType = obj.isSingleClick ? "click" : obj.isDoubleClick ? "double_click" : "hold";
var sn = button.serialNumber;

if (!myButtons[button.bdaddr]) {
	console.log("**** Found an unregistered button. It must be new! ***")
	register_button(button);
}

if (myButtons[button.bdaddr].name!=button.name) {
	console.log("*Name changed from "+myButtons[button.bdaddr].name+" to "+button.name);
	register_button(button);
}

var btntopic = flictopic+"/"+sn;

mqtt.publish(btntopic + "/action", clickType);
console.log(btntopic+"/action:   \t"+clickType);
mqtt.publish(btntopic + "/action", "ok");
mqtt.publish(btntopic + "/battery", button.batteryStatus+""); //NOW it seems to just want text, no decoration!
console.log(btntopic+"/battery:  \t"+button.batteryStatus);

});

buttonManager.on(“buttonUp”, function(obj) {
var button = buttonManager.getButton(obj.bdaddr);
console.log("\nButton Released! " + button.serialNumber + " " + button.name);
publishButtonState(button, “released”);
});

buttonManager.on(“buttonDown”, function(obj) {
var button = buttonManager.getButton(obj.bdaddr);
console.log("\nButton Pressed! " + button.serialNumber + " " + button.name);
publishButtonState(button, “pressed”);
});

function publishButtonState(button, state) {
var sn = button.serialNumber;
var statetopic = flictopic + “/” + sn + “/state”;
mqtt.publish(statetopic, state);
console.log(statetopic + “: \t” + state);
}

function register_button(button) {
//Register one button
//This sets up the MQTT Discovery topics to publish the Flic button

//NOTE: When a button is added via the Flic interface, it gets named 
//a bit late. So the API returns null for the name.
//What's worse, it continues to return null until you restart the 
//Javascript app running in the Hub. I have no idea how to get the 
//new name without crashing the app and relying on the 
//"Restart after crash" flag - and I'm not willing to do that.
//TL;DR - new buttons are named "null" until the Hub is rebooted.

console.log("\nRAW device info: " + JSON.stringify(button, null, 4) + "\n");

if (button.serialNumber == null) {
    console.log("**Error registering button, still no serialNumber")
    return;
}

var configtopic = hatopic + "/sensor/" + button.serialNumber;
var buttontopic = flictopic + "/" + button.serialNumber;
var obj = {};

obj.device = {
    name: button.name,
    identifiers: [button.serialNumber],
    manufacturer: "Flic",
    model: "Button"
};

//Setup config and destination for button press
obj.name = "Button Action";
obj.state_topic = buttontopic + "/action";
obj.unique_id = "Flic_" + button.serialNumber + "_action";

payload = JSON.stringify(obj, null, 4);
console.log(configtopic + "/action/config:\t" + payload);
mqtt.publish(configtopic + "/action/config", payload, {
    retain: true
});

// Setup config and destination for button state
obj.name = "Button State";
obj.state_topic = buttontopic + "/state";
obj.unique_id = "Flic_" + button.serialNumber + "_state";

payload = JSON.stringify(obj, null, 4);
console.log(configtopic + "/state/config:\t" + payload);
mqtt.publish(configtopic + "/state/config", payload, {
	retain: true
});

//Setup config and destination for battery level report
obj.name = "Battery Level";
obj.state_topic = buttontopic + "/battery";
obj.unique_id = "Flic_" + button.serialNumber + "_battery";
obj.device_class = "battery";
obj.entity_category = "diagnostic";
//obj.unit_of_measurement = "%"; //It doesn't seem to actually like this.

payload = JSON.stringify(obj, null, 4);
console.log(configtopic + "/battery/config:\t" + payload);
mqtt.publish(configtopic + "/battery/config", payload, {
    retain: true
});
myButtons[button.bdaddr] = {
    name: button.name,
    serialNumber: button.serialNumber
};

console.log("\nmyButtons: " + JSON.stringify(myButtons, null, 4) + "\n");

}

//This sets up the MQTT Discovery topics to publish all the Flic buttons
function register_allbuttons(){
var buttons = buttonManager.getButtons();
for (var i = 0; i < buttons.length; i++) {
register_button(buttons[i]);
}
}

//This runs on startup, it performs the actual “discovery” announcement for
//HomeAssistant to add the device to its inventory
mqtt.on(‘connected’, function(){
console.log(“\n’Connected’ event\n”);
register_allbuttons();
});

// Added by heroash88 to reconnect if MQTT server goes down
mqtt.on(‘disconnected’, function() {
console.log(“\n’Disconnected’ event\n”);
mqtt.connect();
});

mqtt.on(‘error’, function () {
console.log(“\n’Error’ event\n”);
setTimeout(function () {
throw new Error(“Crashed”)
}, 1000);
});

mqtt.connect();


mqtt.js -

/* Copyright (c) 2013 Gordon Williams, Pur3 Ltd


All sections of code within this repository are licensed under an MIT License:

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


Modified by Flic Shortcut Labs.

*/

/** ‘private’ constants */
var C = {
PROTOCOL_LEVEL: 4, // MQTT protocol level
DEF_PORT : 1883, // MQTT default server port
DEF_KEEP_ALIVE: 60 // Default keep_alive (s)
};

/** Control packet types */
var TYPE = {
CONNECT : 1,
CONNACK : 2,
PUBLISH : 3,
PUBACK : 4,
PUBREC : 5,
PUBREL : 6,
PUBCOMP : 7,
SUBSCRIBE : 8,
SUBACK : 9,
UNSUBSCRIBE: 10,
UNSUBACK : 11,
PINGREQ : 12,
PINGRESP : 13,
DISCONNECT : 14
};

var pakId = Math.floor(Math.random() * 65534);

Uint8Array.prototype.charCodeAt = function(a,b) {
return this.toString().charCodeAt(a,b);
}

/**
Return Codes
MQTT Version 3.1.1
**/
var RETURN_CODES = {
0: ‘ACCEPTED’,
1: ‘UNACCEPTABLE_PROTOCOL_VERSION’,
2: ‘IDENTIFIER_REJECTED’,
3: ‘SERVER_UNAVAILABLE’,
4: ‘BAD_USER_NAME_OR_PASSWORD’,
5: ‘NOT_AUTHORIZED’
};

/** MQTT constructor /
function MQTT(server, options) {
this.server = server;
options = options || {};
this.port = options.port || C.DEF_PORT;
this.client_id = options.client_id || mqttUid();
this.keep_alive = options.keep_alive || C.DEF_KEEP_ALIVE;
this.clean_session = options.clean_session || true;
this.username = options.username;
this.password = options.password;
this.client = false;
this.connected = false;
/
if keep_alive is less than the ping interval we need to use
a shorter ping interval, otherwise we’ll just time out! */
this.ping_interval =
this.keep_alive < this.C.PING_INTERVAL ? (this.keep_alive - 5) : this.C.PING_INTERVAL;
this.protocol_name = options.protocol_name || “MQTT”;
this.protocol_level = (options.protocol_level || C.PROTOCOL_LEVEL);

if (typeof this.client_id == ‘string’) {
var payloadarray =[this.client_id.length >> 8, this.client_id.length & 255];
var i = 2;
var messagearray = this.client_id.split(‘’);
for (var j = 0; j < this.client_id.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}
this.client_id = payloadarray;
}
if (this.password) {
var payloadarray =[this.password.length >> 8, this.password.length & 255];
var i = 2;
var messagearray = this.password.split(‘’);
for (var j = 0; j < this.password.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}
this.password = payloadarray;
}
if (this.username) {
var payloadarray =[this.username.length >> 8, this.username.length & 255];
var i = 2;
var messagearray = this.username.split(‘’);
for (var j = 0; j < this.username.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}
this.username = payloadarray;
}
}

var __listeners = {};

Object.prototype.on = function(type, fn) {
if (!__listeners[type]) {
__listeners[type] = ;
}
__listeners[type].push(fn);
}

Object.prototype.emit = function (type, data) {
if (__listeners[type]) {
__listeners[type].map(function (fn) {
fn(data);
});
}
}

if (!Buffer.from) {
Buffer.from = function(a) {
return new Buffer(a);
}
}

/** ‘public’ constants here */
MQTT.prototype.C = {
DEF_QOS : 0, // Default QOS level
CONNECT_TIMEOUT: 10000, // Time (ms) to wait for CONNACK
PING_INTERVAL : 40 // Server ping interval (s)
};

/* Utility functions ***************************/

var fromCharCode = String.fromCharCode;

/** MQTT string (length MSB, LSB + data) */
function mqttStr(s) {
var payloadarray =[s.length >> 8, s.length & 255];
var i = 2;
var messagearray = s.split(‘’);
for (var j = 0; j < s.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}

return payloadarray;
}

/** MQTT packet length formatter - algorithm from reference docs */
function mqttPacketLength(length) {
var encLength = ;
var i = 0;
do {
var encByte = length & 127;
length = length >> 7;
// if there are more data to encode, set the top bit of this byte
if (length > 0) {
encByte += 128;
}
encLength[i] = encByte;
i++;
} while (length > 0);
return encLength;
}

/** MQTT packet length decoder - algorithm from reference docs */
function mqttPacketLengthDec(length) {
var bytes = 0;
var decL = 0;
var lb = 0;
do {
lb = length[bytes];
decL |= (lb & 127) << (bytes++*7);
} while ((lb & 128) && (bytes < 4))
return {“decLen”: decL, “lenBy”: bytes};
}

/** MQTT standard packet formatter */
function mqttPacket(cmd, variable, payload) {
var cmdAndLengthArray = [cmd].concat(mqttPacketLength(variable.length + payload.length));
var headerAndPayloadArray = cmdAndLengthArray.concat(variable).concat(payload);
var messageBuffer = Buffer.from(headerAndPayloadArray);
return messageBuffer;
}

/** Generate random UID */
var mqttUid = (function () {
function s4() {
var numberstring = Math.floor((1 + Math.random()*10));
if (numberstring == 10) numberstring = 9;
numberstring = 97 + numberstring;
return numberstring;
}

return function () {
var output = [0, 12, s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4(), s4()];
return output;
};
})();

/** Generate PID */
function mqttPid() {
pakId = pakId > 65534 ? 1 : ++pakId;
return [pakId >> 8, pakId & 0xFF];
}

/** Get PID from message */
function getPid(data) {
return data.slice(0,2);
}

/** PUBLISH control packet */
function mqttPublish(topic, message, qos, flags) {
var cmd = TYPE.PUBLISH << 4 | (qos << 1) | flags;
var variable = mqttStr(topic);
// Packet id must be included for QOS > 0
if (qos > 0) {
var newvariable = variable.concat(mqttPid());
return mqttPacket(cmd, newvariable, message);
} else {
return mqttPacket(cmd, variable, message);
}
}

/** SUBSCRIBE control packet */
function mqttSubscribe(topic, qos) {
var cmd = TYPE.SUBSCRIBE << 4 | 2;
var payloadarray =;
var i = 0;
var messagearray = topic.split(‘’);
for (var j = 0; j < topic.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}
return mqttPacket(cmd,
mqttPid(),
mqttStr(topic).concat([qos]));
}

/** UNSUBSCRIBE control packet */
function mqttUnsubscribe(topic) {
var cmd = TYPE.UNSUBSCRIBE << 4 | 2;
return mqttPacket(cmd,
mqttPid(),
mqttStr(topic));
}

/** Create escaped hex value from number */
function createEscapedHex(number) {
return fromCharCode(parseInt(number.toString(16), 16));
}

// Handle a single packet of data
MQTT.prototype.packetHandler = function(data) {

// if we had some data left over from last
// time, add it on
if (this.partData && this.partData.length > 0) {
data = Buffer.from(Array.prototype.slice.call(this.partData).concat(Array.prototype.slice.call(data)));
this.partData = ;
}

// Figure out packet length…
var dLen = mqttPacketLengthDec(data.slice(1, data.length));
var pLen = dLen.decLen + dLen.lenBy + 1;
// less than one packet?
if (data.length < pLen) {
this.partData = data;
return;
}
// Get the data for this packet
var pData = data.slice(1 + dLen.lenBy, pLen);
// more than one packet? re-emit it so we handle it later
if (data.length > pLen) {
this.client.emit(‘data’, data.slice(pLen, data.length));
}
// Now handle this MQTT packet
var cmd = data[0];
var type = cmd >> 4;
var flag = cmd & 15;
if (type === TYPE.PUBLISH) {
var qos = (cmd & 0x6) >> 1;
var topic_len = pData[0] << 8 | pData[1];
var msg_start = 2 + topic_len + (qos ? 2 : 0);
var parsedData = {
topic : pData.slice(2, 2 + topic_len),
message: pData.slice(msg_start, pData.length),
dup : (cmd & 0x8) >> 3,
qos : qos,
pid : qos?pData.slice(2+topic_len,4+topic_len):0,
retain : cmd & 0x1
};
if (parsedData.qos) {
this.client.write([((parsedData.qos == 1)?TYPE.PUBACK:TYPE.PUBREC) << 4, 2, parsedData.pid]);
}
this.emit(‘publish’, parsedData);
this.emit(‘message’, parsedData.topic, parsedData.message);
} else if (type === TYPE.PUBACK) {
this.emit(‘puback’, data.charCodeAt(2) << 8 | data.charCodeAt(3));
} else if (type === TYPE.PUBREC) {
var pubrelArray = [(TYPE.PUBREL << 4 | 2), 2];
var pidArray = Array.prototype.slice.call(getPid(pData));
var pubrecResponse = pubrelArray.concat(pidArray);
this.client.write(Buffer.from(pubrecResponse));
} else if (type === TYPE.PUBREL) {
var pubcompArray = [(TYPE.PUBCOMP << 4), 2];
var pidArray = Array.prototype.slice.call(getPid(pData));
var pubrelResponse = pubcompArray.concat(pidArray);
this.client.write(pubrelResponse);
} else if (type === TYPE.PUBCOMP) {
this.emit(‘pubcomp’, data.charCodeAt(2) << 8 | data.charCodeAt(3));
} else if (type === TYPE.SUBACK) {
if(pData.length > 0) {
if(pData[pData.length - 1] == 0x80) {
this.emit(‘subscribed_fail’);
} else {
this.emit(‘subscribed’);
}
}
} else if (type === TYPE.UNSUBACK) {
this.emit(‘unsubscribed’);
} else if (type === TYPE.PINGREQ) {
this.client.write([(TYPE.PINGRESP << 4), 0]);
} else if (type === TYPE.PINGRESP) {
this.emit(‘ping_reply’);
} else if (type === TYPE.CONNACK) {
if (this.ctimo) clearTimeout(this.ctimo);
this.ctimo = undefined;
this.partData = ;
var returnCode = pData[1];
if (RETURN_CODES[returnCode] === ‘ACCEPTED’) {
this.connected = true;
// start pinging
if (this.pintr) clearInterval(this.pintr);
this.pintr = setInterval(this.ping.bind(this), this.ping_interval * 1000);
// emit connected events
this.emit(‘connected’);
this.emit(‘connect’);
} else {
var mqttError = "Connection refused, ";
this.connected = false;
if (returnCode > 0 && returnCode < 6) {
mqttError += RETURN_CODES[returnCode];
} else {
mqttError += "unknown return code: " + returnCode + “.”;
}
this.emit(‘error’, mqttError);
}
} else {
this.emit(‘error’, "MQTT unsupported packet type: " + type);
}
};

/* Public interface ****************************/

/** Establish connection and set up keep_alive ping */
MQTT.prototype.connect = function (client) {
if (this.connected) return;
var mqo = this;
var onConnect = function () {
mqo.client = client;
// write connection message
var teststring = mqo.mqttConnect(mqo.client_id)
client.write(teststring);
// handle connection timeout if too slow
mqo.ctimo = setTimeout(function () {
mqo.ctimo = undefined;
mqo.emit(‘disconnected’);
mqo.disconnect();
}, mqo.C.CONNECT_TIMEOUT);
// Incoming data
client.on(‘data’, mqo.packetHandler.bind(mqo));
// Socket closed
client.on(‘end’, function () {
mqo._scktClosed();
});
};
if (client) {
onConnect();
} else {
try {
var self = this;
client = require(“net”).Socket().connect({ host: mqo.server, port: mqo.port }, onConnect);
client.on(‘error’, function (err) {
self.emit(‘error’, err.message);
});
} catch (e) {
this.client = false;
this.emit(‘error’, e.message);
}
}
};

/** Called internally when the connection closes */
MQTT.prototype._scktClosed = function () {
if (this.connected) {
this.connected = false;
this.client = false;
if (this.pintr) clearInterval(this.pintr);
if (this.ctimo) clearTimeout(this.ctimo);
this.pintr = this.ctimo = undefined;
this.emit(‘disconnected’);
this.emit(‘close’);
}
};

/** Disconnect from server */
MQTT.prototype.disconnect = function () {
if (!this.client) return;
try {
this.client.write(Buffer.from([(TYPE.DISCONNECT << 4), 0]));
} catch (e) {
return this._scktClosed();
}
this.client.end();
this.client = false;
};

/** Publish message using specified topic.
opts = {
retain: bool // the server should retain this message and send it out again to new subscribers
dup : bool // indicate the message is a duplicate because original wasn’t ACKed (QoS > 0 only)
}
*/
MQTT.prototype.publish = function (topic, message, opts) {
if (!this.client) return;
opts = opts || {};
try {
var payloadarray =;
var i = 0;
var messagearray = message.split(‘’);
for (var j = 0; j < message.length; j++) {
var char = messagearray[j];
var numberrepres = char.charCodeAt(0);
payloadarray[i] = numberrepres;
i = i + 1;
}
var publishMessage = mqttPublish(topic, payloadarray, opts.qos || this.C.DEF_QOS, (opts.retain ? 1 : 0) | (opts.dup ? 8 : 0));
this.client.write(publishMessage);
} catch (e) {
this._scktClosed();
}
};

/** Subscribe to topic (filter) */
MQTT.prototype.subscribe = function (topics, opts) {
if (!this.client) return;
opts = opts || {};

var subs = ;
if (‘string’ === typeof topics) {
topics = [topics];
}
if (Array.isArray(topics)) {
topics.forEach(function (topic) {
subs.push({
topic: topic,
qos : opts.qos || this.C.DEF_QOS
});
}.bind(this));
} else {
Object
.keys(topics)
.forEach(function (k) {
subs.push({
topic: k,
qos : topics[k]
});
});
}

subs.forEach(function (sub) {
var subpacket = mqttSubscribe(sub.topic, sub.qos);
this.client.write(subpacket);
}.bind(this));
};

/** Unsubscribe to topic (filter) */
MQTT.prototype.unsubscribe = function (topic) {
if (!this.client) return;
this.client.write(mqttUnsubscribe(topic));
};

/** Send ping request to server */
MQTT.prototype.ping = function () {
if (!this.client) return;
try {
this.client.write(Buffer.from([TYPE.PINGREQ << 4, 0]));
} catch (e) {
this._scktClosed();
}
};

/* Packet specific functions *******************/

/** Create connection flags */
MQTT.prototype.createFlagsForConnection = function (options) {
var flags = 0;
flags |= ( this.username ) ? 0x80 : 0;
flags |= ( this.username && this.password ) ? 0x40 : 0;
flags |= ( options.clean_session ) ? 0x02 : 0;
return flags;
};

/** CONNECT control packet
Clean Session and Userid/Password are currently only supported
connect flag. Wills are not
currently supported.
*/
MQTT.prototype.mqttConnect = function (clean) {
var cmd = TYPE.CONNECT << 4;
var flags = this.createFlagsForConnection({
clean_session: clean
});

var keep_alive = [this.keep_alive >> 8, this.keep_alive & 255];

/* payload */
var payload = this.client_id;
if (this.username) {
payload = payload.concat(this.username);
if (this.password) {
payload = payload.concat(this.password);
}
}
return mqttPacket(cmd,
mqttStr(this.protocol_name)/protocol name/.concat(
[this.protocol_level]) /protocol level/.concat(
[flags]).concat(keep_alive),
payload);
};

/* Exports *************************************/

/** This is ‘exported’ so it can be used with require('MQTT.js').create(server, options) */
exports.create = function (server, options) {
return new MQTT(server, options);
};

exports.connect = function (options) {
var mqtt = new MQTT(options.host, options);
mqtt.connect();
return mqtt;
};

Hi Ingo
This is brilliant. After a few error-40 I made it work. I have a mix of version 1 and version 2 buttons. The version 1 buttons battery level are a bit of a mix up. Some show null, some shows up with the 3 as a fixed value. I have tried to add “flicVersion” to the messages or attributes? or perhaps even better force the battery value of the version 1 buttons to show 101?
(you then know it is a bad battery read out and it does not trigger a low battery action).
Can you perhaps point in the direction of where to put in a “if version 1 then xx else yyy”?
There are a lot of publish places…
Again thanks a million.

Trying to use flic buttons through flic SDK MQTT. All looks good in the flic SDK where I see the buttons. In Home Assistant the MQTT Logbook says within seconds that the State, Button, Connectivity, etc, became unknown or disconnected. One button worked once for a few minutes. Any suggestions

Hello, I have successfully installed the SDK and all button events and their battery statuses are also displayed on the flic page.
Unfortunately, no new devices are found in MQTT.
What am I doing wrong? Here is my MQTT protocol: