npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-native-flic2

v0.3.26

Published

React Native Flic plugin made with the Flic2 SDK (unofficial)

Downloads

59

Readme

react-native-flic2

npm version NPM npm GitHub issues

This plugin enables you to connect to a Flic2 button made by Shortcut Labs.

This plugin is supported by the Flic2 SDKs

  • Android: https://github.com/50ButtonsEach/flic2lib-android
  • iOS: https://github.com/50ButtonsEach/flic2lib-ios

Getting started

$ npm install react-native-flic2 --save

Mostly automatic installation

$ react-native link react-native-flic2

Usage

import Flic2 from 'react-native-flic2';

// Flic2 Module
Flic.start();                            // Starts the module, fixes auto bluetooth permission question
Flic2.isInitialized();                   // returns a boolean if the manager is initialized or not
Flic2.startScan();                       // start a scan
Flic2.stopScan();                        // stop a scan
Flic2.startService();                    // enable background capabilities through a service on Android, ignored by iOS
Flic2.addEventListener(event, fn);       // listen for events (manager & all buttons). Possible events are: managerInitialized, didReceiveButtonDown, didReceiveButtonUp, didReceiveButtonClick, didReceiveButtonDoubleClick, didReceiveButtonHold
Flic2.connectAllKnownButtons();          // connect to known buttons
Flic2.buttonConnect(uuid);               // connect to a button with this uuid
Flic2.buttonForget(uuid);                // disconnect and forget the button
Flic2.forgetAllButtons();                // disconnect and forget all known buttons
Flic2.buttonDisconnect(uuid);            // disconnect a button with this uuid
Flic2.disconnectAllKnownButtons();       // disconnect all known buttons
Flic2.getButtons();                      // array of Flic2Button instances
Flic2.getButton(uuid);                   // get a button by uuid, returns a Flic2Button instance
Flic2.buttonSetMode(uuid, mode);         // change the button trigger mode by uuid. Use the constants to change the mode (see example below).
Flic2.buttonSetName(uuid, name);         // change the nickname by uuid

// Flic2Button instance definition
Flic2Button.addEventListener(event, fn); // listen for button events for this particular button. Possible events are: didReceiveButtonDown, didReceiveButtonUp, didReceiveButtonClick, didReceiveButtonDoubleClick, didReceiveButtonHold
Flic2Button.connect()                   // connect this button
Flic2Button.disconnect();                // disconnect this button
Flic2Button.forget();                    // removes the button completely
Flic2Button.getUuid();                   // get the button uuid
Flic2Button.getBluetoothAddress();       // get the button bluetooth address
Flic2Button.getName();                   // get the button nickname
Flic2Button.getBatteryLevelIsOk();       // get the battery state (true, false),  True = battery is ok, False = battery should be changed soon
Flic2Button.getVoltage();                // get the estimated battery voltage
Flic2Button.getPressCount();             // get button count since last reset
Flic2Button.getFirmwareRevision();       // get current hardware version
Flic2Button.getSerialNumber();           // get the serial number of the button
Flic2Button.getIsReady();                // get the ready state of the button
Flic2Button.getIsUnpaired();             // get the unpaired state of the button
Flic2Button.setMode(mode);               // change the button trigger for this particular button. Use the constants to change the mode (see example below).
Flic2Button.setName(name);               // sets the nickname of the button


// Constants
//
// These are the possible result codes for the variable 'result' in the
// event 'scanResult' (see example below)
//
// These constants are available through Flic2.constants
SCAN_RESULT_SUCCESS                                                     = 0;
SCAN_RESULT_ERROR_ALREADY_RUNNING                                       = 1;
SCAN_RESULT_ERROR_BLUETOOTH_NOT_ACTIVATED                               = 2;
SCAN_RESULT_ERROR_UNKNOWN                                               = 3;
SCAN_RESULT_ERROR_NO_PUBLIC_BUTTON_DISCOVERED                           = 4;
SCAN_RESULT_ERROR_ALREADY_CONNECTED_TO_ANOTHER_DEVICE                   = 5;
SCAN_RESULT_ERROR_CONNECTION_TIMEOUT                                    = 6;
SCAN_RESULT_ERROR_INVALID_VERIFIER                                      = 7;
SCAN_RESULT_ERROR_BLE_PAIRING_FAILED_PREVIOUS_PAIRING_ALREADY_EXISTING  = 8;
SCAN_RESULT_ERROR_BLE_PAIRING_FAILED_USER_CANCELED                      = 9;
SCAN_RESULT_ERROR_BLE_PAIRING_FAILED_UNKNOWN_REASON                     = 10;
SCAN_RESULT_ERROR_APP_CREDENTIALS_DONT_MATCH                            = 11;
SCAN_RESULT_ERROR_USER_CANCELED                                         = 12;
SCAN_RESULT_ERROR_INVALID_BLUETOOTH_ADDRESS                             = 13;
SCAN_RESULT_ERROR_GENUINE_CHECK_FAILED                                  = 14;
SCAN_RESULT_ERROR_TOO_MANY_APPS                                         = 15;
SCAN_RESULT_ERROR_COULD_NOT_SET_BLUETOOTH_NOTIFY                        = 16;
SCAN_RESULT_ERROR_COULD_NOT_DISCOVER_BLUETOOTH_SERVICES                 = 17;
SCAN_RESULT_ERROR_BUTTON_DISCONNECTED_DURING_VERIFICATION               = 18;
SCAN_RESULT_ERROR_FAILED_TO_ESTABLISH                                   = 19;
SCAN_RESULT_ERROR_CONNECTION_LIMIT_REACHED                              = 20;
SCAN_RESULT_ERROR_NOT_IN_PUBLIC_MODE                                    = 21;

// These constants can be used to change the mode of the buttons
// These constants are available through Flic2.constants
BUTTON_TRIGGER_MODE_CLICK_AND_HOLD                                      = 0; 
BUTTON_TRIGGER_MODE_CLICK_AND_DOUBLE_CLICK                              = 1; 
BUTTON_TRIGGER_MODE_CLICK_AND_DOUBLE_CLICK_AND_HOLD                     = 2; 
BUTTON_TRIGGER_MODE_CLICK                                               = 3; 

// Scan result:
Flic2.addEventListener('scanResult', ((int) result, (Flic2Button) button) => {

  if (result === Flic2.constants.SCAN_RESULT_SUCCESS) {

    doSomethingWithButton(button);

  } else 
  if(result === Flic2.constants.SCAN_RESULT_ERROR_ALREADY_CONNECTED_TO_ANOTHER_DEVICE) {

    alert('This button is already connected to another device');

  }
  // ... etc

});

// Button events:
// 
// didReceiveButtonDown
// didReceiveButtonUp
// didReceiveButtonClick
// didReceiveButtonDoubleClick
// didReceiveButtonHold
Flic2.addEventListener('didReceiveButtonHold', ((object) eventData) => {

  // eventData:
  // { int age, bool queued, Flic2Button button }

});

Android only

Changing the title, text and icon of the service, Or the notification channel name and description. Place the following meta-data's inside the application tag of your manifest

    <application>
      <meta-data android:name="nl.xguard.flic2.notification_title" android:value="Flic 2 Button"/>
      <meta-data android:name="nl.xguard.flic2.notification_text" android:value="Service active"/>
      <meta-data android:name="nl.xguard.flic2.notification_icon" android:resource="@mipmap/xguard_adaptive_launcher"/>
      <meta-data android:name="nl.xguard.flic2.notification_channel_name" android:value="Flic 2 Channel Name"/>
      <meta-data android:name="nl.xguard.flic2.notification_channel_description" android:value="Flic 2 Channel Description"/>
    </application>

Example component

You can install and test this example at https://github.com/X-Guard/react-native-flic2-example

// react and react native imports
import React, { Component } from 'react';
import { View, FlatList, TouchableOpacity, Text, StyleSheet, StatusBar, Platform, Vibration } from 'react-native';

// the Flic2 module
import Flic2 from 'react-native-flic2';

// icons
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faPause, faPlay, faTrash, faEdit, faBatteryQuarter, faBatteryFull } from '@fortawesome/free-solid-svg-icons';

// plugins to make it more fancy
import prompt from 'react-native-prompt-android';
import { request as requestPermission, PERMISSIONS } from 'react-native-permissions';
import * as Animatable from 'react-native-animatable';
import Toast from 'react-native-root-toast';

export default class App extends Component {

  constructor(props) {

    // man
    super(props);

    // init state
    this.state = {
      buttons: [],
      scanning: false,
    };

    // bindings
    this.didReceiveButtonClickFunction = this.didReceiveButtonClick.bind(this);
    this.onScanResultFunction = this.onScanResult.bind(this);
    this.onInitializedFunction = this.onInitialized.bind(this);

  }

  componentDidMount() {

    // start module
    Flic2.start();

    if (typeof Flic2.isInitialized === 'function' && Flic2.isInitialized() === true) {
      this.onInitialized();
    } else {
      Flic2.addListener('managerInitialized', this.onInitializedFunction);
    }

    // listen bindings
    Flic2.addListener('didReceiveButtonClick', this.didReceiveButtonClickFunction);
    Flic2.addListener('scanResult', this.onScanResultFunction);

  }

  componentWillUnmount() {

    // remove bindings
    Flic2.removeListener('buttonEvent', this.handleButtonEventFunction);
    Flic2.removeListener('scanResult', this.onScanResultFunction);

  }

  onInitialized() {

      // connect to all known buttons
      Flic2.connectAllKnownButtons();

      // get the buttons
      this.getButtons();
  }

  async getButtons() {

    // async calls for init
    this.setState({
      buttons: await Flic2.getButtons(),
    });

  }

  async forgetAllButtons() {

    await Flic2.forgetAllButtons();
    this.getButtons();

  }

  async startScan() {

    // check os
    if (Platform.OS === 'android') {

      // on android we need the permission ACCESS_FINE_LOCATION first
      // we are just going to assume the permission is granted after calling this
      // in your real application, please create an actual permission check here
      await requestPermission(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION);

    }

    // set to scanning
    this.setState({
      scanning: true,
    });

    // go!
    Flic2.startScan();

  }

  stopScan() {

    this.setState({
      scanning: false,
    });

    Flic2.stopScan();

  }

  connectButton(button) {

    // connect it
    button.connect(button);

    // update our button list
    this.getButtons();

  }

  disconnectButton(button) {

    // disconnect it
    button.disconnect(button);

    // update our button list
    this.getButtons();

  }

  forgetButton(button) {

    // forget it
    button.forget();

    // update our button list
    this.getButtons();

  }

  editButtonName(button) {

    // use the prompt to change the name
    prompt(
      'Edit Flic nickname',
      'Choose a name you will recognize',
      [
        { text: 'Cancel', style: 'cancel' },
        {
          text: 'OK',
          onPress: value => {

            // save
            button.setName(value);

            // get new buttons
            this.getButtons();

          },
        },
      ],
      {
          type: 'plain-text',
          cancelable: true,
          defaultValue: button.getName(),
      }
    );

  }

  onScanResult(data) {

    if (data.event === 'completion') {

      this.setState({
        scanning: false,
      });

      // check
      if (data.error === false) {

        alert('The button has been added');
        this.getButtons();

      } else {

        if (data.result === Flic2.constants.SCAN_RESULT_ERROR_ALREADY_CONNECTED_TO_ANOTHER_DEVICE) {

          alert('This button is already connected to another device');

        } else
        if (data.result === Flic2.constants.SCAN_RESULT_ERROR_NO_PUBLIC_BUTTON_DISCOVERED) {

          alert('No buttons found');

        } else {

          alert(`Could not connect\n\nError code: ${data.result}`);

        }
      }
    }
  }

  didReceiveButtonClick(eventData) {

    console.log('Received click event', eventData);

    // update list
    this.getButtons();

    // do something with the click like showing a notification
    Toast.show(`Button ${eventData.button.getName()} has been pressed ${eventData.button.getPressCount()} times`);

    // wobble
    // we do this extensive check because when you develop the app with live reload, the _logoRef will break.
    if (typeof this._logoRef !== 'undefined' && this._logoRef !== null && typeof this._logoRef.wobble === 'function') {

      // wobble wobble
      this._logoRef.wobble();

    }

    // vibrate
    Vibration.vibrate(200);

  }

  getBatteryIcon(batteryPercentage) {

    if (batteryPercentage === true) {
      return faBatteryFull;
    } else {
      return faBatteryQuarter;
    }

  }


  render() {

    return (
      <View style={style.container}>
        <StatusBar barStyle="light-content" />

        {/* eslint-disable-next-line */}
        <Animatable.Image ref={ image => this._logoRef = image } style={style.logo} useNativeDriver={true} source={require('./images/flic-logo.png')} />

        {/* Scan button */}
        {this.state.scanning === false ?
          <TouchableOpacity onPress={this.startScan.bind(this)}>
            <View style={style.button}><Text style={style.buttonText}>Start scan</Text></View>
          </TouchableOpacity>
          :
          <TouchableOpacity onPress={this.stopScan.bind(this)}>
            <View style={style.button}><Text style={style.buttonText}>Scanning... (click to cancel)</Text></View>
          </TouchableOpacity> }

        <TouchableOpacity onPress={this.forgetAllButtons.bind(this)}>
          <View style={style.button}><Text style={style.buttonText}>Forget all buttons</Text></View>
        </TouchableOpacity>

        <View style={style.buttonContainer}>

          <Text style={style.heading}>Button list:</Text>

          {this.state.buttons.length > 0 ?
            <FlatList
              data={this.state.buttons}
              keyExtractor={item => item.uuid}
              renderItem={row => {

                // define button
                const button = row.item;

                // eslint-disable-next-line react-native/no-inline-styles
                return <View style={[style.listItem, { borderColor: button.getIsReady() ? '#006e1a' : '#b00000'}]}>
                  <FontAwesomeIcon style={style.icon} icon={this.getBatteryIcon(button.getBatteryLevelIsOk())} size={16} />
                  <Text style={style.pressCount}>{button.getPressCount()}</Text>
                  <Text style={style.listItemText}>{button.getName()}</Text>
                  <View style={style.icons}>
                    {button.getIsReady() === true ?
                      <TouchableOpacity onPress={this.disconnectButton.bind(this, button)}><FontAwesomeIcon icon={faPause} size={16} /></TouchableOpacity>
                      :
                      <TouchableOpacity onPress={this.connectButton.bind(this, button)}><FontAwesomeIcon icon={faPlay} size={16} /></TouchableOpacity>
                    }
                    <TouchableOpacity onPress={this.forgetButton.bind(this, button)}><FontAwesomeIcon icon={faTrash} size={16} /></TouchableOpacity>
                    <TouchableOpacity onPress={this.editButtonName.bind(this, button)}><FontAwesomeIcon icon={faEdit} size={16} /></TouchableOpacity>
                  </View>
                </View>;

              }}
            /> : <Text>There are no buttons paired to this app. Click 'start scan' and hold your flic button to add a new button.</Text>}

        </View>

      </View>
    );
  }
}


// define stylesheet
const style = StyleSheet.create({

  // container
  container: {
    paddingTop: 20,
    padding: 10,
    backgroundColor: '#45454d',
    flex: 1,
  },

  // logo
  logo: {
    width: 100,
    alignSelf: 'center',
    resizeMode: 'contain',
  },

  // button
  button: {
    padding: 10,
    borderRadius: 10,
    backgroundColor: '#ff0089',
    marginTop: 15,
  },
  buttonText: {
    color: 'white',
    textAlign: 'center',
    fontSize: 20,
  },

  // button container
  buttonContainer: {
    padding: 10,
    backgroundColor: 'rgba(255, 255, 255, 1)',
    borderRadius: 10,
    marginTop: 20,
  },

  // heading
  heading: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 10,
  },

  // list item
  listItem: {
    padding: 20,
    paddingLeft: 15,
    paddingRight: 15,
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
    borderRadius: 10,
    marginBottom: 10,
    backgroundColor: '#f3f9ff',
    borderWidth: 2,
  },
  listItemText: {
    flex: 1,
  },
  pressCount: {
    width: 25,
    color: 'rgba(40, 40, 40, 0.5)',
    fontSize: 10,
  },

  // icons
  icons: {
    width: 70,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    alignSelf: 'flex-end',
  },
  icon: {
    marginRight: 7,
  },

});

Collaborating

We are happy to receive PRs! We have not published an NPM package before and are eager to learn!