A tiny JavaScript library which provides a consistent API for interacting with Pine64 PinePhone devices.
- Reading device model name (Is it an OG or Pro PinePhone?)
- Reading device model version (Which mainboard version is this?)
- Getting current statuses (Are WiFi/BT currently enabled?)
- Enable / Disable (Control WiFi/BT radios at software level)
- Toggling individual status LEDs (R, G, and B LEDs located at the top-left of the phone)
- Set status color (white, red, yellow, green, cyan, purple, blue, black/off)
- Create notifications with titles and optional bodies (bodies can only be viewed while the phone is unlocked)
# NPM (https://nodejs.org)
npm install pinephone
# Bun (https://bun.sh)
bun upgrade --canary && bun install pinephone
Various builds of pinephone.js are now provided. You can import the library in CommonJS or ES module format.
// CommonJS (Classic node.js)
const pinephone = require('pinephone')
// ES Module, importing the entire library
import * as pinephone from 'pinephone'
// ES Module, importing only what's needed (allows tree-shaking)
import { getModelName, getBluetoothStatus } from 'pinephone'
import * as pinephone from 'pinephone'
pinephone.setLEDColor(pinephone.Color_YELLOW)
pinephone.notify(`Hello, ${pinephone.getModelName()}!`, 'Hello from pinephone.js!')
console.log('Device information:', pinephone.getDeviceInfo())
console.log('WiFi status:', pinephone.getWifiStatus())
console.log('Bluetooth status:', pinephone.getBluetoothStatus())
If you just want to know if the device is an OG PinePhone or a Pro, use the following:
console.log( pinephone.getModelName() )
// output -> 'Pine64 PinePhone' OR 'Pine64 PinePhonePro'
You can also get the version (helps to identify the phone's mainboard):
console.log( pinephone.getModelVersion() )
// output -> '1.0', '1.1', '1.2', etc...
If you want these values returned as an object:
console.log( pinephone.getDeviceInfo() )
// output -> (example Object):
// {
// modelName: 'Pine64 PinePhonePro',
// modelVersion: '1.0'
// }
If you want to hand-pick returned values, you can pass an array of constants like this (see full list of options):
console.log(pinephone.getDeviceInfo( [pinephone.Model_VersionNumber] ))
// output -> (example Object):
// {
// modelVersion: '1.0'
// }
Find out if WiFi or Bluetooth are enabled, both at the software level and also the hardware level (via the kill switch located inside the back cover of the phone).
console.log( pinephone.getWifiStatus() )
// output -> (example Object):
// {
// softwareEnabled: true,
// hardwareEnabled: true
// }
console.log( pinephone.getBluetoothStatus() )
// output -> (example Object):
// {
// softwareEnabled: false,
// hardwareEnabled: true
// }
This one is pretty self-explanitory. Just remember not to test disabling WiFi if you are connected to the phone via SSH!
// Controlling WiFi (software)
pinephone.enableWifi()
pinephone.disableWifi()
// Controlling Bluetooth (software)
pinephone.enableBluetooth()
pinephone.disableBluetooth()
There is a group of three status LEDs located at the top-left corner of the device. Since they are grouped closely together, toggling certain LEDs lets you "mix" the R, G, and B LEDs to produce a few other colors. You have a few options for controlling these status LEDs:
// Different method for each action
pinephone.enableRedLED()
pinephone.disableRedLED()
// Passing in a boolean (similar to Arduio's digitalWrite function)
pinephone.enableGreenLED(true)
pinephone.enableGreenLED(false)
// Pass in colors as pinephone.js constants
pinephone.setLEDColor(pinephone.Color_WHITE)
pinephone.setLEDColor(pinephone.Color_BLACK) // Color_OFF works too
// Pass in colors as strings
pinephone.setLEDColor('yellow')
pinephone.setLEDColor('black') // 'off' works too
Here is a simple example showing how to blink between two colors, once per second:
import * as pinephone from 'pinephone'
let toggle = false
setInterval(() => {
toggle = !toggle
if (toggle) {
pinephone.setLEDColor(pinephone.Color_BLUE)
} else {
pinephone.setLEDColor(pinephone.Color_YELLOW)
}
}, 500)
Note: You cannot read the currently set color at this time, though this is definitely something that could be implemented easily. Software PWM is not possible, and likely will not be, without a faster, lower-level method of controlling the LEDs.
You can issue notifications based on the Freedesktop.org specification. The most basic notifications contain just a title/subject, but you can also add a body if you feel up for the challenge :)
pinephone.notify('Title Text')
pinephone.notify('Title Text', 'Body text - feeling adventurous today!')
Sensor data is accessed via EventEmitter events. By default, sensors are sampled every 50ms.
Reading sensor data is currently not implemented in this version of pinephone.js, but the feature will be made available very soon.
See src/test-scripts/read-x-accel.mjs for example code if you are curious and want to implement something right away.
Many of the currently available applications that run on PinePhones weren't designed specifically for mobile devices. They can be desktop applications which were updated to be more adaptive and therefore mobile-friendly through the use of libraries such as Gnome's libhandy. Many of them are being written in C, C++, or Rust. The PinePhone community offers several ways to get started writing apps. Initially, you'll likely be faced with choosing a framework, such as GTK (Typically found on Phosh or Gnome-Mobile), or the KDE frameworks which utilize QT (common on Plasma Mobile).
Your choice of framework isn't really that critical to the end-user, as most apps will build and run perfectly fine under any mobile Linux environment. You can basically just choose the framework that works best for you. When it comes to JavaScript, here are just three options to consider, with Node-GTK being my personal recommendation.
- Node-GTK - If you prefer working entirely in JavaScript and don't want to ship an entire browser, Node-GTK might be a great solution for you. It offers bindings to GTK 3 and 4, allows use of WebKitGTK (and therefore WebGL/GPU), and lets you make use of npm modules (something gjs does not offer). As it is GTK-based, you can build front-ends using Glade and all apps built with Node-GTK will feel right at home with other apps in Phosh or Gnome-mobile.
- Tauri - Seems like a good step in the right direction, as it is much lighter than Electron and produces a much smaller bundle. Tauri apps will likely not feel very at-home next to other PinePhone apps though, as it doesn't use GTK or QT. Also Tauri still seems to be working on a way to bundle as a flatpak app. Tauri's "main" process is typically written in rust, and while it is possible to use deno or node instead, it requires some additional configuration.
- Electron - Definitely seen as an attractive option for JS devs, due to how simple it is to start developing, testing, and packaging. But Electron has many downsides which need to be considered. It relies on Chromium, leading to large app bundle sizes and high memory usage. Many end users don't like the idea of Chromium (owned by Google) being a requirement for their app to work. It can also cause your app to feel downright sluggish, especially on devices like the original PinePhone with it's limited memory.
Bun, an optimized JavaScript runtime using JavaScriptCore, is now supported!
To use pinephone.js with bun, make sure bun is already installed (via the install script on their website), and then run the following:
bun upgrade --canary
As support for child_process was only recently added to bun, you will need to update to bun's canary release to use pinephone.js as expected. Once Bun publishes an official release with this change included (which should only be a few weeks), you will no longer need to explicitly switch to their canary version.
The best way to contribute is by submitting issues and/or pull requests to the pinephone.js development repository. If you would like to contribute financially, a donation would be greatly appriciated (see next section).
This library was developed by a sole individual of the PinePhone community - I am not affiliated with Pine64, the company! Any donations you make here will go directly to me, not Pine64. If you are looking instead to support Pine64 (which I highly recommend, they're awesome), please consider purchasing one of their devices or donating to them.
That said, if you like pinephone.js or your project relies on it, a donation (even a small one) would help development quite a bit. Thank you!
Donate with fiat:
Donate with crypto:
- Monero (XMR): 4B12BmQuYNF9PHg275ASbfiD7iXy1vK2YJkxtK5V4xv9UTrxFv9ZQ4ASokykTvEVa7fL1NDbtKdLyEDHsMGrRht4FdHqiKj
- Litecoin (LTC): LLfE4awRw47ghQJzMvmn1WPvVykcXwqt9S
- Bitcoin Cash (BCH): qpzezt09wvry2tc30pzlsj9tk9npmu70kqht4x3xqg
DISCLAIMER: "PINE64 and the PINE64 pinecone logo are trademarked by Pine Store Limited." For more information, please visit this webpage. Please contact me if you have any issues or concerns involving this project's use of branding or imagery.