package module
Version: v0.0.0-...-512d48b Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2023 License: MIT Imports: 25 Imported by: 4



This is a go library for interfacing with InfiniTime firmware over BLE on Linux.

Go Reference


This library's import path is


This library requires dbus, and bluez to function. These allow the library to use bluetooth, control media, control volume, etc.

sudo pacman -S dbus bluez --needed
sudo apt install dbus bluez
sudo dnf install dbus bluez


This library currently supports the following features:

  • Notifications
  • Heart rate monitoring
  • Setting time
  • Battery level
  • Music control
  • OTA firmware upgrades
  • Navigation


The DFU process used in this library was created with the help of siglo's source code. Specifically, this file:




View Source
const (
	DFUCtrlPointChar = "00001531-1212-efde-1523-785feabcd123" // UUID of Control Point characteristic
	DFUPacketChar    = "00001532-1212-efde-1523-785feabcd123" // UUID of Packet characteristic
View Source
const (
	DFUSegmentSize     = 20 // Size of each firmware packet
	DFUPktRecvInterval = 10 // Amount of packets to send before checking for receipt
View Source
const (
	NewAlertChar    = "00002a46-0000-1000-8000-00805f9b34fb"
	NotifEventChar  = "00020001-78fc-48fe-8e23-433b3a1942d0"
	StepCountChar   = "00030001-78fc-48fe-8e23-433b3a1942d0"
	MotionValChar   = "00030002-78fc-48fe-8e23-433b3a1942d0"
	FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb"
	CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb"
	LocalTimeChar   = "00002a0f-0000-1000-8000-00805f9b34fb"
	BatteryLvlChar  = "00002a19-0000-1000-8000-00805f9b34fb"
	HeartRateChar   = "00002a37-0000-1000-8000-00805f9b34fb"
	FSTransferChar  = "adaf0200-4669-6c65-5472-616e73666572"
	FSVersionChar   = "adaf0100-4669-6c65-5472-616e73666572"
	WeatherDataChar = "00040001-78fc-48fe-8e23-433b3a1942d0"
View Source
const (
	CallStatusDeclined uint8 = iota

These constants represent the possible call statuses selected by the user

View Source
const (
	MusicEventChar  = "00000001-78fc-48fe-8e23-433b3a1942d0"
	MusicStatusChar = "00000002-78fc-48fe-8e23-433b3a1942d0"
	MusicArtistChar = "00000003-78fc-48fe-8e23-433b3a1942d0"
	MusicTrackChar  = "00000004-78fc-48fe-8e23-433b3a1942d0"
	MusicAlbumChar  = "00000005-78fc-48fe-8e23-433b3a1942d0"
View Source
const (
	NavFlagsChar     = "00010001-78fc-48fe-8e23-433b3a1942d0"
	NavNarrativeChar = "00010002-78fc-48fe-8e23-433b3a1942d0"
	NavManDistChar   = "00010003-78fc-48fe-8e23-433b3a1942d0"
	NavProgressChar  = "00010004-78fc-48fe-8e23-433b3a1942d0"
View Source
const (
	// ResourceOperationUpload represents the upload phase
	// of resource loading
	ResourceOperationUpload = iota
	// ResourceOperationRemoveObsolete represents the obsolete
	// file removal phase of resource loading
View Source
const BTName = "InfiniTime"


View Source
var (
	DFUCmdStart              = []byte{0x01, 0x04}
	DFUCmdRecvInitPkt        = []byte{0x02, 0x00}
	DFUCmdInitPktComplete    = []byte{0x02, 0x01}
	DFUCmdPktReceiptInterval = []byte{0x08, 0x0A}
	DFUCmdRecvFirmware       = []byte{0x03}
	DFUCmdValidate           = []byte{0x04}
	DFUCmdActivateReset      = []byte{0x05}
View Source
var (
	DFUResponseStart            = []byte{0x10, 0x01, 0x01}
	DFUResponseInitParams       = []byte{0x10, 0x02, 0x01}
	DFUResponseRecvFwImgSuccess = []byte{0x10, 0x03, 0x01}
	DFUResponseValidate         = []byte{0x10, 0x04, 0x01}
View Source
var (
	ErrDFUInvalidInput    = errors.New("input file invalid, must be a .bin file")
	ErrDFUTimeout         = errors.New("timed out waiting for response")
	ErrDFUNoFilesLoaded   = errors.New("no files are loaded")
	ErrDFUInvalidResponse = errors.New("invalid response returned")
	ErrDFUSizeMismatch    = errors.New("amount of bytes sent does not match amount received")
View Source
var (
	ErrNoDevices        = errors.New("no InfiniTime devices found")
	ErrNotFound         = errors.New("could not find any advertising InfiniTime devices")
	ErrNotConnected     = errors.New("not connected")
	ErrNoTimelineHeader = errors.New("events must contain the timeline header")
	ErrPairTimeout      = errors.New("reached timeout while pairing")
View Source
var DFUNotifPktRecvd = []byte{0x11}
View Source
var DefaultOptions = &Options{
	AttemptReconnect: true,
	WhitelistEnabled: false,
	Logger:           logger.NewNop(),
View Source
var ErrNavProgress = errors.New("progress needs to be between 0 and 100")


func Exit

func Exit() error

func Init

func Init(adapterID string)

func LoadResources

func LoadResources(file *os.File, fs *blefs.FS) (<-chan ResourceLoadProgress, error)

LoadResources accepts a resources zip file and a BLE FS. It loads the resources from the zip onto the FS.


type Agent

type Agent struct {
	ReqPasskey func() (uint32, error)

Agent implements the agent.Agent1Client interface. It only requires RequestPasskey as that is all InfiniTime will use.

func (*Agent) AuthorizeService

func (*Agent) AuthorizeService(device dbus.ObjectPath, uuid string) *dbus.Error

AuthorizeService returns nil

func (*Agent) Cancel

func (*Agent) Cancel() *dbus.Error

Cancel returns nil

func (*Agent) DisplayPasskey

func (*Agent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint16) *dbus.Error

DisplayPasskey returns nil

func (*Agent) DisplayPinCode

func (*Agent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error

DisplayPinCode returns nil

func (*Agent) Interface

func (*Agent) Interface() string

Interface returns "org.bluez.Agent1"

func (*Agent) Path

func (*Agent) Path() dbus.ObjectPath

Path returns "/dev/arsenm/infinitime/Agent"

func (*Agent) Release

func (*Agent) Release() *dbus.Error

Release returns nil

func (*Agent) RequestAuthorization

func (*Agent) RequestAuthorization(device dbus.ObjectPath) *dbus.Error

RequestAuthorization returns nil

func (*Agent) RequestConfirmation

func (*Agent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error

RequestConfirmation returns nil

func (*Agent) RequestPasskey

func (a *Agent) RequestPasskey(device dbus.ObjectPath) (uint32, *dbus.Error)

RequestPasskey runs Agent.ReqPasskey and returns the result

func (*Agent) RequestPinCode

func (*Agent) RequestPinCode(device dbus.ObjectPath) (pincode string, err *dbus.Error)

RequestPinCode returns an empty string and nil

type DFU

type DFU struct {
	// contains filtered or unexported fields

DFU stores everything required for doing firmware upgrades

func (*DFU) LoadArchive

func (dfu *DFU) LoadArchive(archivePath string) error

LoadArchive loads an init packet and firmware image from a zip archive using a maifest.json also stored in the archive.

func (*DFU) LoadFiles

func (dfu *DFU) LoadFiles(initPath, fwPath string) error

LoadFiles loads an init packet (.dat) and firmware image (.bin)

func (*DFU) Progress

func (dfu *DFU) Progress() <-chan DFUProgress

func (*DFU) Reset

func (dfu *DFU) Reset()

Reset reverts all values back to default to prepare for the next DFU.

func (*DFU) Start

func (dfu *DFU) Start() error

Start DFU process

type DFUProgress

type DFUProgress struct {
	Sent     int   `json:"sent"`
	Received int   `json:"recvd"`
	Total    int64 `json:"total"`

type Device

type Device struct {
	Music      MusicCtrl
	Navigation NavigationService
	DFU        DFU
	// contains filtered or unexported fields

func Connect

func Connect(ctx context.Context, opts *Options) (*Device, error)

Connect will attempt to connect to a paired InfiniTime device. If none are paired, it will attempt to discover and pair one.

It will also attempt to reconnect to the device if it disconnects and that is enabled in the options.

func (*Device) AddWeatherEvent

func (i *Device) AddWeatherEvent(event interface{}) error

AddWeatherEvent adds one of the event structs from the weather package to the timeline. Input must be a struct containing TimelineHeader.

func (*Device) Address

func (i *Device) Address() string

Address returns the InfiniTime's bluetooth address

func (*Device) BatteryLevel

func (i *Device) BatteryLevel() (uint8, error)

BatteryLevel gets the watch's battery level via the Battery Service

func (*Device) FS

func (i *Device) FS() (*blefs.FS, error)

FS creates and returns a new filesystem from the device

func (*Device) HeartRate

func (i *Device) HeartRate() (uint8, error)

func (*Device) Motion

func (i *Device) Motion() (MotionValues, error)

func (*Device) Notify

func (i *Device) Notify(title, body string) error

Notify sends a notification to InfiniTime via the Alert Notification Service (ANS)

func (*Device) NotifyCall

func (i *Device) NotifyCall(from string) (<-chan uint8, error)

NotifyCall sends a call notification to the PineTime and returns a channel. This channel will contain the user's response to the call notification.

func (*Device) SetTime

func (i *Device) SetTime(t time.Time) error

SetTime sets the watch's * time using the Current Time Service's current time characteristic * timezone information using the CTS's local time characteristic

func (*Device) StepCount

func (i *Device) StepCount() (uint32, error)

func (*Device) Version

func (i *Device) Version() (string, error)

Version returns InfiniTime's reported firmware version string

func (*Device) WatchBatteryLevel

func (i *Device) WatchBatteryLevel(ctx context.Context) (<-chan uint8, error)

func (*Device) WatchHeartRate

func (i *Device) WatchHeartRate(ctx context.Context) (<-chan uint8, error)

func (*Device) WatchMotion

func (i *Device) WatchMotion(ctx context.Context) (<-chan MotionValues, error)

func (*Device) WatchStepCount

func (i *Device) WatchStepCount(ctx context.Context) (<-chan uint32, error)

type ErrCharNotAvail

type ErrCharNotAvail struct {
	// contains filtered or unexported fields

func (ErrCharNotAvail) Error

func (e ErrCharNotAvail) Error() string

type MotionValues

type MotionValues struct {
	X int16
	Y int16
	Z int16

type MusicCtrl

type MusicCtrl struct {
	// contains filtered or unexported fields

MusicCtrl stores everything required to control music

func (MusicCtrl) SetAlbum

func (mc MusicCtrl) SetAlbum(album string) error

SetAlbum sets the album on InfniTime

func (MusicCtrl) SetArtist

func (mc MusicCtrl) SetArtist(artist string) error

SetArtist sets the artist on InfniTime

func (MusicCtrl) SetStatus

func (mc MusicCtrl) SetStatus(playing bool) error

SetStatus sets the playing status

func (MusicCtrl) SetTrack

func (mc MusicCtrl) SetTrack(track string) error

SetTrack sets the track name on InfniTime

func (MusicCtrl) WatchEvents

func (mc MusicCtrl) WatchEvents() (<-chan MusicEvent, error)

WatchEvents watches music events from InfiniTime

type MusicEvent

type MusicEvent uint8
const (
	MusicEventOpen    MusicEvent = 0xe0
	MusicEventPlay    MusicEvent = 0x00
	MusicEventPause   MusicEvent = 0x01
	MusicEventNext    MusicEvent = 0x03
	MusicEventPrev    MusicEvent = 0x04
	MusicEventVolUp   MusicEvent = 0x05
	MusicEventVolDown MusicEvent = 0x06
type NavFlag string
const (
	NavFlagArrive                  NavFlag = "arrive"
	NavFlagArriveLeft              NavFlag = "arrive-left"
	NavFlagArriveRight             NavFlag = "arrive-right"
	NavFlagArriveStraight          NavFlag = "arrive-straight"
	NavFlagClose                   NavFlag = "close"
	NavFlagContinue                NavFlag = "continue"
	NavFlagContinueLeft            NavFlag = "continue-left"
	NavFlagContinueRight           NavFlag = "continue-right"
	NavFlagContinueSlightLeft      NavFlag = "continue-slight-left"
	NavFlagContinueSlightRight     NavFlag = "continue-slight-right"
	NavFlagContinueStraight        NavFlag = "continue-straight"
	NavFlagContinueUturn           NavFlag = "continue-uturn"
	NavFlagDepart                  NavFlag = "depart"
	NavFlagDepartLeft              NavFlag = "depart-left"
	NavFlagDepartRight             NavFlag = "depart-right"
	NavFlagDepartStraight          NavFlag = "depart-straight"
	NavFlagEndOfRoadLeft           NavFlag = "end-of-road-left"
	NavFlagEndOfRoadRight          NavFlag = "end-of-road-right"
	NavFlagFerry                   NavFlag = "ferry"
	NavFlagFlag                    NavFlag = "flag"
	NavFlagFork                    NavFlag = "fork"
	NavFlagForkLeft                NavFlag = "fork-left"
	NavFlagForkRight               NavFlag = "fork-right"
	NavFlagForkSlightLeft          NavFlag = "fork-slight-left"
	NavFlagForkSlightRight         NavFlag = "fork-slight-right"
	NavFlagForkStraight            NavFlag = "fork-straight"
	NavFlagInvalid                 NavFlag = "invalid"
	NavFlagInvalidLeft             NavFlag = "invalid-left"
	NavFlagInvalidRight            NavFlag = "invalid-right"
	NavFlagInvalidSlightLeft       NavFlag = "invalid-slight-left"
	NavFlagInvalidSlightRight      NavFlag = "invalid-slight-right"
	NavFlagInvalidStraight         NavFlag = "invalid-straight"
	NavFlagInvalidUturn            NavFlag = "invalid-uturn"
	NavFlagMergeLeft               NavFlag = "merge-left"
	NavFlagMergeRight              NavFlag = "merge-right"
	NavFlagMergeSlightLeft         NavFlag = "merge-slight-left"
	NavFlagMergeSlightRight        NavFlag = "merge-slight-right"
	NavFlagMergeStraight           NavFlag = "merge-straight"
	NavFlagNewNameLeft             NavFlag = "new-name-left"
	NavFlagNewNameRight            NavFlag = "new-name-right"
	NavFlagNewNameSharpLeft        NavFlag = "new-name-sharp-left"
	NavFlagNewNameSharpRight       NavFlag = "new-name-sharp-right"
	NavFlagNewNameSlightLeft       NavFlag = "new-name-slight-left"
	NavFlagNewNameSlightRight      NavFlag = "new-name-slight-right"
	NavFlagNewNameStraight         NavFlag = "new-name-straight"
	NavFlagNotificationLeft        NavFlag = "notification-left"
	NavFlagNotificationRight       NavFlag = "notification-right"
	NavFlagNotificationSharpLeft   NavFlag = "notification-sharp-left"
	NavFlagNotificationSharpRight  NavFlag = "notification-sharp-right"
	NavFlagNotificationSlightLeft  NavFlag = "notification-slight-left"
	NavFlagNotificationSlightRight NavFlag = "notification-slight-right"
	NavFlagNotificationStraight    NavFlag = "notification-straight"
	NavFlagOffRampLeft             NavFlag = "off-ramp-left"
	NavFlagOffRampRight            NavFlag = "off-ramp-right"
	NavFlagOffRampSharpLeft        NavFlag = "off-ramp-sharp-left"
	NavFlagOffRampSharpRight       NavFlag = "off-ramp-sharp-right"
	NavFlagOffRampSlightLeft       NavFlag = "off-ramp-slight-left"
	NavFlagOffRampSlightRight      NavFlag = "off-ramp-slight-right"
	NavFlagOffRampStraight         NavFlag = "off-ramp-straight"
	NavFlagOnRampLeft              NavFlag = "on-ramp-left"
	NavFlagOnRampRight             NavFlag = "on-ramp-right"
	NavFlagOnRampSharpLeft         NavFlag = "on-ramp-sharp-left"
	NavFlagOnRampSharpRight        NavFlag = "on-ramp-sharp-right"
	NavFlagOnRampSlightLeft        NavFlag = "on-ramp-slight-left"
	NavFlagOnRampSlightRight       NavFlag = "on-ramp-slight-right"
	NavFlagOnRampStraight          NavFlag = "on-ramp-straight"
	NavFlagRotary                  NavFlag = "rotary"
	NavFlagRotaryLeft              NavFlag = "rotary-left"
	NavFlagRotaryRight             NavFlag = "rotary-right"
	NavFlagRotarySharpLeft         NavFlag = "rotary-sharp-left"
	NavFlagRotarySharpRight        NavFlag = "rotary-sharp-right"
	NavFlagRotarySlightLeft        NavFlag = "rotary-slight-left"
	NavFlagRotarySlightRight       NavFlag = "rotary-slight-right"
	NavFlagRotaryStraight          NavFlag = "rotary-straight"
	NavFlagRoundabout              NavFlag = "roundabout"
	NavFlagRoundaboutLeft          NavFlag = "roundabout-left"
	NavFlagRoundaboutRight         NavFlag = "roundabout-right"
	NavFlagRoundaboutSharpLeft     NavFlag = "roundabout-sharp-left"
	NavFlagRoundaboutSharpRight    NavFlag = "roundabout-sharp-right"
	NavFlagRoundaboutSlightLeft    NavFlag = "roundabout-slight-left"
	NavFlagRoundaboutSlightRight   NavFlag = "roundabout-slight-right"
	NavFlagRoundaboutStraight      NavFlag = "roundabout-straight"
	NavFlagTurnLeft                NavFlag = "turn-left"
	NavFlagTurnRight               NavFlag = "turn-right"
	NavFlagTurnSharpLeft           NavFlag = "turn-sharp-left"
	NavFlagTurnSharpRight          NavFlag = "turn-sharp-right"
	NavFlagTurnSlightLeft          NavFlag = "turn-slight-left"
	NavFlagTurnSlightRight         NavFlag = "turn-slight-right"
	NavFlagTurnStright             NavFlag = "turn-stright"
	NavFlagUpdown                  NavFlag = "updown"
	NavFlagUturn                   NavFlag = "uturn"
type NavigationService struct {
	// contains filtered or unexported fields
func (n *NavigationService) SetFlag(flag NavFlag) error
func (n *NavigationService) SetManDist(manDist string) error
func (n *NavigationService) SetNarrative(narrative string) error
func (n *NavigationService) SetProgress(progress uint8) error

type ObsoleteResource

type ObsoleteResource struct {
	Path  string `json:"path"`
	Since string `json:"since"`

ObsoleteResource represents an obsolete file entry in the manifest

type Options

type Options struct {
	AttemptReconnect bool
	WhitelistEnabled bool
	Whitelist        []string
	OnReqPasskey     func() (uint32, error)
	OnReconnect      func()
	Logger           logger.Logger
	LogLevel         logger.LogLevel

type Resource

type Resource struct {
	Name string `json:"filename"`
	Path string `json:"path"`

Resource represents a resource entry in the manifest

type ResourceLoadProgress

type ResourceLoadProgress struct {
	Operation ResourceOperation
	Name      string
	Total     int64
	Sent      int64
	Err       error

ResourceLoadProgress contains information on the progress of a resource load

type ResourceManifest

type ResourceManifest struct {
	Resources []Resource         `json:"resources"`
	Obsolete  []ObsoleteResource `json:"obsolete_files"`

ResourceManifest is the structure of the resource manifest file

type ResourceOperation

type ResourceOperation uint8

ResourceOperation represents an operation performed during resource loading


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL