@Emil No noticable latency actually. I just did a A-B test where I compared the response time to a Flic 2 with Spotifty play/toggle, and I can't really tell the difference. The volume control is also smooth.
Andreas
@Emil No noticable latency actually. I just did a A-B test where I compared the response time to a Flic 2 with Spotifty play/toggle, and I can't really tell the difference. The volume control is also smooth.
Andreas
@wes I would look into the Matter libraries for Arduino. However the Flic hub Mini IS in fact a ESP32 S3. Access to the bootloader/flash etc. is literally cut off. Who would have known 
Hi,
Here's a module I programmed for controlling volume and basic Spotify playback functions with Flic Twist, in case someone else has any use for it. To use it, you'll have to register for a Spotify developer account and create a new app from their dashboard, in order to get access tokens etc.
@flichub @Emil: Is it possible to somehow get this added to the existing public Spotify "provider"? Seems a bit tedious/difficult for users without any programming experience to go down this particular rabbit hole.
// main.js
const flicapp = require('flicapp');
const http = require('http');
const datastore = require('datastore');
datastore.get('clientID', (err, key) => {
const clientID = key // store this first: (datastore.put('clientID', 'YOUR CLIENT ID')
datastore.get('clientSecret', (err, key) => {
const clientSecret = key // store this first: (datastore.put('clientSecret', 'YOUR CLIENT SECRET')
function percent(volume, decimalPlaces = 0) {
const percentage = (volume * 100).toFixed(decimalPlaces);
return `${percentage}`;
}
function spotifyStatus() {
datastore.get('accessToken', (err, key) => {
const accessToken = key
if (key !== null) {
const options = {
url: 'https://api.spotify.com/v1/me/player',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
};
const req = http.makeRequest(options, (error, result) => {
if (result.statusCode == 401) {
refreshToken();
}
if (result.statusCode == 204) {
var status = "No active sessions."
console.log(status);
}
if (result.statusCode == 200) {
var jresp = JSON.parse(result.content);
var status = jresp["is_playing"];
if (status === true) {
spotifyPause();
}
if (status === false) {
spotifyPlay();
}
}
})
}
})
};
function spotifyPlay() {
datastore.get('accessToken', (err, key) => {
const accessToken = key
if (key !== null) {
const options = {
url: 'https://api.spotify.com/v1/me/player/play',
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
};
const req = http.makeRequest(options, (error, result) => {
if (result.statusCode == 401) {
refreshToken();
}
})
}
})
};
function spotifyPause() {
datastore.get('accessToken', (err, key) => {
const accessToken = key
if (key !== null) {
const options = {
url: 'https://api.spotify.com/v1/me/player/pause',
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
};
const req = http.makeRequest(options, (error, result) => {
if (result.statusCode == 401) {
refreshToken();
}
})
}
})
};
function spotifyNext() {
datastore.get('accessToken', (err, key) => {
const accessToken = key
if (key !== null) {
const options = {
url: 'https://api.spotify.com/v1/me/player/next',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
};
const req = http.makeRequest(options, (error, result) => {
if (result.statusCode == 401) {
refreshToken();
}
})
}
})
};
function spotifyVolume(volume) {
datastore.get('accessToken', (err, key) => {
const accessToken = key
if (key !== null) {
const options = {
url: 'https://api.spotify.com/v1/me/player/volume?volume_percent=' + volume,
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
}
};
const req = http.makeRequest(options, (error, result) => {
if (result.statusCode == 401) {
refreshToken();
}
if (result.statusCode == 404) {
console.log("No active sessions.")
}
})
}
})
};
function refreshToken() {
datastore.get('refreshToken', (err, key) => {
const refreshToken = key
if (key !== null) {
const options = {
url: 'https://accounts.spotify.com/api/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
content: "refresh_token=" + refreshToken + "&scope=user-read-playback-state%2C+user-modify-playback-state%2C+user-follow-read%2C+user-library-read%2C+streaming%2C+user-read-playback-position%2C+user-top-read%2C+user-read-currently-playing%2C+&client_id=" + clientID + "&client_secret=" + clientSecret + "&grant_type=refresh_token"
};
const req = http.makeRequest(options, (error, result) => {
if (!error && result.statusCode === 200) {
var jresp = JSON.parse(result.content);
datastore.put('accessToken', jresp["access_token"])
spotifyStatus();
}
})
}
})
};
flicapp.on("actionMessage", function(message) {
if (message == 'playtoggle') {
spotifyStatus();
}
});
flicapp.on("actionMessage", function(message) {
if (message == 'next') {
spotifyNext();
}
});
flicapp.on("virtualDeviceUpdate", function(metaData, values) {
if (values.volume && metaData.virtualDeviceId == "spotify") {
var volume = percent(values.volume)
spotifyVolume(volume);
flicapp.virtualDeviceUpdateState("Speaker", "spotify", {
volume: values.volume
});
}
});
}); // clientID var
}); // clientSecret var
As you probably can see, I'm not exactly a Javascript wizard, but it works
Enjoy.
Andreas
That didn't age very well….
🫣
Here's a quick and dirty script I created to test volume control. Might be errors (even dragons) here, use at own risk. Maybe it's useful for someone sometime…I don't know.
const buttonManager = require("buttons");
const flicapp = require('flicapp');
const net = require("net");
const http = require("http");
const client = 'INSERT IP ADRESS HERE'
const port = 4710;
let client = new net.Socket();
var muteCommand = Buffer.from('set /devices/0/outputs/20/Mute/value/true \0', 'utf-8');
var volumeCommand = Buffer.from('set /devices/0/outputs/20/CRMonitorLevelTapered/value/ ');
flicapp.on("actionMessage", function(message) {
if(message == 'mute') {
sendToSocket(muteCommand);
}
// handle more (else) cases here
});
function sendToSocket(msg) {
client.connect(port, client, function() => {
client.write(msg);
})
};
flicapp.on("virtualDeviceUpdate", function(metaData, values) {
let virtualID = metaData.virtualDeviceId;
if(values.volume && values.volume != "") {
sendToSocket(volumeCommand + values.volume + ' \0');
}
// console.log(metaData.virtualDeviceId);
// console.log(values.volume);
});
Nice. I already got it up and running with a couple of custom volume controls. I use it in my home studio (remote controlling input/output volume on Universal Audio gear, controlling playback and recording when tracking vocals and drums etc), and one for my home surround system as well.
Although…
Can you PLEASE update the existing Spotify Connect "provider"/service to also include the volume (up, down, mute) endpoints as well as the existing ones?
It would require next to no time and effort for you to configure, but it would probably make a world of difference for a lot of the "average" end users of your products.
Think about it…the access token/authentication mechanisms are already there, the Spotify volume endpoints have been up and ready to serve since long before the dinosaurs, and it wouldn't require anything out of the ordinary to add it to your already working Spotify service provider.
Don't get me wrong. The Hub SDK is really great now with the latest updates. I enjoy and love that level of flexibility and all the customization goodies, but it's clearly not for everyone - as the learning curve can be somewhat steep and in my opinion it's a total overkill if you have no prior programming skills or interest, but still want to make use of your Flic hub, Twist, Flic 2 button, Duo or whatever. Yay or nay? Oh and btw, great work. I don't just say that 
(And please fix the Flic 2 Universal MIDI functionality, as it's useless at this point).

🤍
Andreas
@Emil I want to add one more thing to my wishlist, while we're at it.
The thing is: I didn't notice until now that the Flic Twist doesn't have a "Hold" action like the other buttons. I kind of took that for granted, based on the press release text and your Twist product page, so I just assumed it was there.
@Emil Although you could still open up for ALL connectivity regardless of provider, if you just exposed the twist action functionality in the SDK and documentation. To me, that seems like THE obvious thing to do, instead of adding support for yet another smart home proprietary protocol that isn't even available to the general market yet. Apart from money 
, I don't see any good reasons for not documenting it just as extensively as the Flic 2, and making (all of) the functionality availble so people can use it for what they want. That was why I got a Flic button in the first place, 9 years ago. Because it inspired creative solutions and new areas of use. This inspires turning on and off the light.
@Emil said in Flic Twist - not what I was hoping for:
If you have a priority list (order of importance for you), that would be even more helpful to help us prioritise.
Spotify Connect Volume Control
Chromecast volume control
User customizable rotary encoder (like «internet request» only wiith a scalable/dimmable value/parameter)
IKEA Trådfri dimmer
Thanks for clearing that up, @Emil !
Andreas
Hi,
From the Flic Twist promo text:
"Unlock the full potential of your smart home devices, from lights to music and blinds, with Flic Twist versatile functionality."
How can it be versatile, when there are
.The main functionality for other media players/music systems etc. (Spotify Connect, Bose, OSC, Chromecast/Android TV, VLC ++) is already there (!)
Anyway... I don't have a Sonos system (and I can't afford a new one), so at the moment my Flic Twist already has a fine layer of dust on it, and unfortunately it's a major disappointment. It has so much wasted potential though. 
This is awesome!
In lack of better Javascript skills, I actually configured a Flic 2 button (through the "front door" main app interface ), just to be able to trigger the record action without a phone. Even though it worked, it felt kind of "offensive" to the code (like using a MacBook Pro as a bookend
). I guess I'll hack around with the example code just to get everything in one place 
️
@emil It works! I don't know if it was significant or not, but I logged out of the Flic App on all devices, and logged into another user account. Then I reset the Flic hub, first by pushing the reset button for about 13 seconds. The orange light went out completely. Then I power cycled it, and held the reset button for another 4-5 seconds until it started blinking. Added Flic 2 and enabled Homekit support. I don't know what made it work this time, but I suspect that the new user account forced the app to ask for new network permissions. At least that's my qualified guess 
Andreas
@emil Thank you for the suggestions. However, I have no extra router or firewall in between, and multicast DNS (Bonjour) is not blocked. I can see the "hap" service running on the Flic hub, but the Home app somehow doesn't. When I enable Homekit for a button, there is no notification regarding access to the local network, access to Homekit data, or anything else for that matter. It just switches to the Homekit button interface momentarily (clearly not working as intended, in other words) 
Andras
@emil Hello. I have tried to re-add my Flic LR hub (and Flic 2) since october without any luck.
I have reset the hub at least 50 times (pin hole button, 13-15 seconds) and the hub is resetting OK. Then I add a Flic 2 button and receive a message saying the hub's firmware needs to be upgraded, which I do. The firmware upgrade is successful (3.0.12), but when I try to "Add to homekit" from the button configuration, the hub (and button) is not seen by HomeKit/Apple Home, and I never get any notifications regarding the Flic Hub requiring access to Homekit or network (that only happened once, the first time I went through this setup). There is no mention of Apple Home in the Flic app settings either.
I've also tried to add a new home in the Apple Home app. I have the latest iOS (15.x) installed on both iPhone and iPad. I have an iPad and Apple TV (also latest TvOS) configured as Home hubs. I've also removed the Flic app from all devices, and reinstalled it. No luck whatsoever. Any ideas?
Andreas
This action simply toggles macOS dark mode on and off....or light mode off and on, it depends if your glass is half full or half empty.
Anyways...long story short: it switches to the opposite of the current mode when the Flic is clicked. There, I said it!
Unzip and place the folder inside:
~/Library/Application\ Scripts/com.shortcutlabs.FlicMac/
Screenshot:

Hello,

Do you have any suggestions to how to accomplish that?

Hi,
Just wanted to mention that I bought a Bluetooth USB adapter at Clas Ohlson recently, that is working 100% on my Raspberry PI and both of my Macs (running macOS High Sierra).
My Macs are relatively new (2014 & 2016) so they both have Bluetooth LE, but I just wanted to give it a try, since the instruction manual & box clearly stated that ONLY Windows was supported.
Very misleading, considering the fact that it worked out of the box, on every single device I have 
Andreas