forked from Elara6331/infinitime
Compare commits
21 Commits
f56be08106
...
ce90459491
Author | SHA1 | Date | |
---|---|---|---|
ce90459491 | |||
0c369dc5df | |||
|
5fb5cf4b92 | ||
|
76809726d5 | ||
72b558707e | |||
31f4c51a61 | |||
8e5bbafba8 | |||
e6fb402c0c | |||
1f5301f5de | |||
0ad671d3f5 | |||
54fdd19bec | |||
a01f1b9d92 | |||
bb017b471e | |||
01970b2bb7 | |||
b476853dc0 | |||
3e9957d419 | |||
b5d345cdec | |||
0eead333b7 | |||
e4c12d32a1 | |||
5af53d1dc6 | |||
d199fba93c |
@ -15,24 +15,24 @@ This library's import path is `go.arsenm.dev/infinitime`.
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
This library requires `dbus`, `bluez`, and `pactl` to function. These allow the library to use bluetooth, control media, control volume, etc.
|
This library requires `dbus`, and `bluez` to function. These allow the library to use bluetooth, control media, control volume, etc.
|
||||||
|
|
||||||
#### Arch
|
#### Arch
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo pacman -S dbus bluez libpulse --needed
|
sudo pacman -S dbus bluez --needed
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Debian/Ubuntu
|
#### Debian/Ubuntu
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt install dbus bluez pulseaudio-utils
|
sudo apt install dbus bluez
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Fedora
|
#### Fedora
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo dnf install dbus bluez pulseaudio-utils
|
sudo dnf install dbus bluez
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -47,6 +47,7 @@ This library currently supports the following features:
|
|||||||
- Battery level
|
- Battery level
|
||||||
- Music control
|
- Music control
|
||||||
- OTA firmware upgrades
|
- OTA firmware upgrades
|
||||||
|
- Navigation
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
95
blefs/all.go
Normal file
95
blefs/all.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package blefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (blefs *FS) RemoveAll(path string) error {
|
||||||
|
if path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Clean(path) == "/" {
|
||||||
|
return ErrNoRemoveRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := blefs.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
return blefs.removeAllChildren(path)
|
||||||
|
} else {
|
||||||
|
err = blefs.Remove(path)
|
||||||
|
|
||||||
|
var code int8
|
||||||
|
if err, ok := err.(FSError); ok {
|
||||||
|
code = err.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && code != -2 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blefs *FS) removeAllChildren(path string) error {
|
||||||
|
list, err := blefs.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range list {
|
||||||
|
name := entry.Name()
|
||||||
|
|
||||||
|
if name == "." || name == ".." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entryPath := filepath.Join(path, name)
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
err = blefs.removeAllChildren(entryPath)
|
||||||
|
} else {
|
||||||
|
err = blefs.Remove(entryPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var code int8
|
||||||
|
if err, ok := err.(FSError); ok {
|
||||||
|
code = err.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && code != -2 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blefs *FS) MkdirAll(path string) error {
|
||||||
|
if path == "" || path == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
splitPath := strings.Split(path, "/")
|
||||||
|
for i := 1; i < len(splitPath); i++ {
|
||||||
|
curPath := strings.Join(splitPath[0:i+1], "/")
|
||||||
|
|
||||||
|
err := blefs.Mkdir(curPath)
|
||||||
|
|
||||||
|
var code int8
|
||||||
|
if err, ok := err.(FSError); ok {
|
||||||
|
code = err.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && code != -17 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,5 +1,29 @@
|
|||||||
package blefs
|
package blefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stat does a ReadDir() and finds the current file in the output
|
||||||
|
func (blefs *FS) Stat(path string) (fs.FileInfo, error) {
|
||||||
|
// Get directory in filepath
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
// Read directory
|
||||||
|
dirEntries, err := blefs.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, entry := range dirEntries {
|
||||||
|
// If file name is base name of path
|
||||||
|
if entry.Name() == filepath.Base(path) {
|
||||||
|
// Return file info
|
||||||
|
return entry.Info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrFileNotExists
|
||||||
|
}
|
||||||
|
|
||||||
// Rename moves or renames a file or directory
|
// Rename moves or renames a file or directory
|
||||||
func (blefs *FS) Rename(old, new string) error {
|
func (blefs *FS) Rename(old, new string) error {
|
||||||
// Create move request
|
// Create move request
|
||||||
|
@ -13,6 +13,7 @@ var (
|
|||||||
ErrOffsetChanged = errors.New("offset has already been changed")
|
ErrOffsetChanged = errors.New("offset has already been changed")
|
||||||
ErrReadOpen = errors.New("only one file can be opened for reading at a time")
|
ErrReadOpen = errors.New("only one file can be opened for reading at a time")
|
||||||
ErrWriteOpen = errors.New("only one file can be opened for writing at a time")
|
ErrWriteOpen = errors.New("only one file can be opened for writing at a time")
|
||||||
|
ErrNoRemoveRoot = errors.New("refusing to remove root directory")
|
||||||
)
|
)
|
||||||
|
|
||||||
// FSError represents an error returned by BLE FS
|
// FSError represents an error returned by BLE FS
|
||||||
|
@ -3,7 +3,6 @@ package blefs
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -291,7 +290,7 @@ func (fl *File) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(fl.offsetCh)
|
close(fl.offsetCh)
|
||||||
return int(fl.offset), nil
|
return int(fl.amtTferd), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteString converts the string to []byte and calls Write()
|
// WriteString converts the string to []byte and calls Write()
|
||||||
@ -335,23 +334,9 @@ func (fl *File) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat does a RedDir() and finds the current file in the output
|
// Stat does a ReadDir() and finds the current file in the output
|
||||||
func (fl *File) Stat() (fs.FileInfo, error) {
|
func (fl *File) Stat() (fs.FileInfo, error) {
|
||||||
// Get directory in filepath
|
return fl.fs.Stat(fl.path)
|
||||||
dir := filepath.Dir(fl.path)
|
|
||||||
// Read directory
|
|
||||||
dirEntries, err := fl.fs.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, entry := range dirEntries {
|
|
||||||
// If file name is base name of path
|
|
||||||
if entry.Name() == filepath.Base(fl.path) {
|
|
||||||
// Return file info
|
|
||||||
return entry.Info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, ErrFileNotExists
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileReadResponse represents a response for a read request
|
// fileReadResponse represents a response for a read request
|
||||||
|
@ -199,7 +199,13 @@ func decode(data []byte, vals ...interface{}) error {
|
|||||||
// to send in a packet. Subtracting 20 ensures that the MTU
|
// to send in a packet. Subtracting 20 ensures that the MTU
|
||||||
// is never exceeded.
|
// is never exceeded.
|
||||||
func (blefs *FS) maxData() uint16 {
|
func (blefs *FS) maxData() uint16 {
|
||||||
return blefs.transferChar.Properties.MTU - 20
|
mtu := blefs.transferChar.Properties.MTU
|
||||||
|
// If MTU is zero, the current version of BlueZ likely
|
||||||
|
// doesn't support the MTU property, so assume 256.
|
||||||
|
if mtu == 0 {
|
||||||
|
mtu = 256
|
||||||
|
}
|
||||||
|
return mtu - 20
|
||||||
}
|
}
|
||||||
|
|
||||||
// padding returns a slice of len amount of 0x00.
|
// padding returns a slice of len amount of 0x00.
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0
|
github.com/fxamacker/cbor/v2 v2.4.0
|
||||||
github.com/godbus/dbus/v5 v5.0.6
|
github.com/godbus/dbus/v5 v5.0.6
|
||||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268
|
||||||
github.com/rs/zerolog v1.26.1
|
github.com/rs/zerolog v1.26.1
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -15,8 +15,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA=
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268 h1:kOnq7TfaAO2Vc/MHxPqFIXe00y1qBxJAvhctXdko6vo=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -54,6 +54,10 @@ var charNames = map[string]string{
|
|||||||
FSTransferChar: "Filesystem Transfer",
|
FSTransferChar: "Filesystem Transfer",
|
||||||
FSVersionChar: "Filesystem Version",
|
FSVersionChar: "Filesystem Version",
|
||||||
WeatherDataChar: "Weather Data",
|
WeatherDataChar: "Weather Data",
|
||||||
|
NavFlagsChar: "Navigation Icon",
|
||||||
|
NavNarrativeChar: "Navigation Instruction",
|
||||||
|
NavManDistChar: "Navigation Distance to next event",
|
||||||
|
NavProgressChar: "Navigation Progress",
|
||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
@ -70,9 +74,11 @@ type Device struct {
|
|||||||
fsVersionChar *gatt.GattCharacteristic1
|
fsVersionChar *gatt.GattCharacteristic1
|
||||||
fsTransferChar *gatt.GattCharacteristic1
|
fsTransferChar *gatt.GattCharacteristic1
|
||||||
weatherDataChar *gatt.GattCharacteristic1
|
weatherDataChar *gatt.GattCharacteristic1
|
||||||
|
weatherdataChar *gatt.GattCharacteristic1
|
||||||
notifEventCh chan uint8
|
notifEventCh chan uint8
|
||||||
notifEventDone bool
|
notifEventDone bool
|
||||||
Music MusicCtrl
|
Music MusicCtrl
|
||||||
|
Navigation NavigationService
|
||||||
DFU DFU
|
DFU DFU
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +139,7 @@ func Connect(ctx context.Context, opts *Options) (*Device, error) {
|
|||||||
|
|
||||||
// Create new device
|
// Create new device
|
||||||
out := &Device{device: btDev}
|
out := &Device{device: btDev}
|
||||||
|
out.Navigation = NavigationService{dev: out}
|
||||||
|
|
||||||
// Resolve characteristics
|
// Resolve characteristics
|
||||||
err = out.resolveChars()
|
err = out.resolveChars()
|
||||||
@ -397,6 +404,14 @@ func (i *Device) resolveChars() error {
|
|||||||
charResolved := true
|
charResolved := true
|
||||||
// Set correct characteristics
|
// Set correct characteristics
|
||||||
switch char.Properties.UUID {
|
switch char.Properties.UUID {
|
||||||
|
case NavFlagsChar:
|
||||||
|
i.Navigation.flagsChar = char
|
||||||
|
case NavNarrativeChar:
|
||||||
|
i.Navigation.narrativeChar = char
|
||||||
|
case NavManDistChar:
|
||||||
|
i.Navigation.mandistChar = char
|
||||||
|
case NavProgressChar:
|
||||||
|
i.Navigation.progressChar = char
|
||||||
case NewAlertChar:
|
case NewAlertChar:
|
||||||
i.newAlertChar = char
|
i.newAlertChar = char
|
||||||
case NotifEventChar:
|
case NotifEventChar:
|
||||||
|
151
navigation.go
Normal file
151
navigation.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package infinitime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/muka/go-bluetooth/bluez/profile/gatt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNavProgress = errors.New("progress needs to be between 0 and 100")
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NavigationService struct {
|
||||||
|
dev *Device
|
||||||
|
flagsChar *gatt.GattCharacteristic1
|
||||||
|
narrativeChar *gatt.GattCharacteristic1
|
||||||
|
mandistChar *gatt.GattCharacteristic1
|
||||||
|
progressChar *gatt.GattCharacteristic1
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (n *NavigationService) SetFlag(flag NavFlag) error {
|
||||||
|
log.Debug().Str("func", "SetFlag").Msg("Sending flag")
|
||||||
|
if err := n.dev.checkStatus(n.flagsChar, NavFlagsChar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return n.flagsChar.WriteValue([]byte(flag), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NavigationService) SetNarrative(narrative string) error {
|
||||||
|
log.Debug().Str("func", "SetNarrative").Msg("Sending narrative")
|
||||||
|
if err := n.dev.checkStatus(n.narrativeChar, NavNarrativeChar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return n.narrativeChar.WriteValue([]byte(narrative), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NavigationService) SetManDist(manDist string) error {
|
||||||
|
log.Debug().Str("func", "SetNarrative").Msg("Sending maneuver distance")
|
||||||
|
if err := n.dev.checkStatus(n.mandistChar, NavManDistChar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return n.mandistChar.WriteValue([]byte(manDist), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NavigationService) SetProgress(progress uint8) error {
|
||||||
|
log.Debug().Str("func", "SetNarrative").Msg("Sending progress")
|
||||||
|
if err := n.dev.checkStatus(n.progressChar, NavProgressChar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return n.progressChar.WriteValue([]byte{progress}, nil)
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package player
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolUp uses pactl to increase the volume of the default sink
|
|
||||||
func VolUp(percent uint) error {
|
|
||||||
return exec.Command("pactl", "set-sink-volume", "@DEFAULT_SINK@", fmt.Sprintf("+%d%%", percent)).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolDown uses pactl to decrease the volume of the default sink
|
|
||||||
func VolDown(percent uint) error {
|
|
||||||
return exec.Command("pactl", "set-sink-volume", "@DEFAULT_SINK@", fmt.Sprintf("-%d%%", percent)).Run()
|
|
||||||
}
|
|
@ -107,6 +107,46 @@ func Prev() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func VolUp(percent uint) error {
|
||||||
|
|
||||||
|
player, err := getPlayerObj()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if player != nil {
|
||||||
|
currentVal, err := player.GetProperty("org.mpris.MediaPlayer2.Player.Volume")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newVal := currentVal.Value().(float64) + (float64(percent) / 100)
|
||||||
|
err = player.SetProperty("org.mpris.MediaPlayer2.Player.Volume", newVal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VolDown(percent uint) error {
|
||||||
|
|
||||||
|
player, err := getPlayerObj()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if player != nil {
|
||||||
|
currentVal, err := player.GetProperty("org.mpris.MediaPlayer2.Player.Volume")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newVal := currentVal.Value().(float64) - (float64(percent) / 100)
|
||||||
|
err = player.SetProperty("org.mpris.MediaPlayer2.Player.Volume", newVal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type ChangeType int
|
type ChangeType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
209
resources.go
Normal file
209
resources.go
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
package infinitime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime/blefs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceOperation represents an operation performed during
|
||||||
|
// resource loading
|
||||||
|
type ResourceOperation uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ResourceOperationUpload represents the upload phase
|
||||||
|
// of resource loading
|
||||||
|
ResourceOperationUpload = iota
|
||||||
|
// ResourceOperationRemoveObsolete represents the obsolete
|
||||||
|
// file removal phase of resource loading
|
||||||
|
ResourceOperationRemoveObsolete
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceManifest is the structure of the resource manifest file
|
||||||
|
type ResourceManifest struct {
|
||||||
|
Resources []Resource `json:"resources"`
|
||||||
|
Obsolete []ObsoleteResource `json:"obsolete_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource represents a resource entry in the manifest
|
||||||
|
type Resource struct {
|
||||||
|
Name string `json:"filename"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObsoleteResource represents an obsolete file entry in the manifest
|
||||||
|
type ObsoleteResource struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Since string `json:"since"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceLoadProgress contains information on the progress of
|
||||||
|
// a resource load
|
||||||
|
type ResourceLoadProgress struct {
|
||||||
|
Operation ResourceOperation
|
||||||
|
Name string
|
||||||
|
Total int64
|
||||||
|
Sent int64
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadResources accepts a resources zip file and a BLE FS.
|
||||||
|
// It loads the resources from the zip onto the FS.
|
||||||
|
func LoadResources(file *os.File, fs *blefs.FS) (<-chan ResourceLoadProgress, error) {
|
||||||
|
out := make(chan ResourceLoadProgress, 10)
|
||||||
|
|
||||||
|
fi, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := zip.NewReader(file, fi.Size())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := r.Open("resources.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer m.Close()
|
||||||
|
|
||||||
|
var manifest ResourceManifest
|
||||||
|
err = json.NewDecoder(m).Decode(&manifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Close()
|
||||||
|
|
||||||
|
log.Debug().Msg("Decoded manifest file")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
|
||||||
|
for _, file := range manifest.Obsolete {
|
||||||
|
name := filepath.Base(file.Path)
|
||||||
|
|
||||||
|
log.Debug().Str("file", file.Path).Msg("Removing file")
|
||||||
|
|
||||||
|
err := fs.RemoveAll(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: name,
|
||||||
|
Operation: ResourceOperationRemoveObsolete,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("file", file.Path).Msg("Removed file")
|
||||||
|
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: name,
|
||||||
|
Operation: ResourceOperationRemoveObsolete,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range manifest.Resources {
|
||||||
|
src, err := r.Open(file.Name)
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFi, err := src.Stat()
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
src.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("file", file.Path).Msg("Making directories")
|
||||||
|
|
||||||
|
err = fs.MkdirAll(filepath.Dir(file.Path))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Err(err).Msg("Error making directories")
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
src.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().
|
||||||
|
Str("file", file.Path).
|
||||||
|
Int64("size", srcFi.Size()).
|
||||||
|
Msg("Creating file")
|
||||||
|
|
||||||
|
dst, err := fs.Create(file.Path, uint32(srcFi.Size()))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Err(err).Msg("Error creating file")
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
src.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progCh := dst.Progress()
|
||||||
|
go func() {
|
||||||
|
for sent := range progCh {
|
||||||
|
log.Debug().
|
||||||
|
Int64("total", srcFi.Size()).
|
||||||
|
Uint32("sent", sent).
|
||||||
|
Msg("Progress event sent")
|
||||||
|
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Sent: int64(sent),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sent == uint32(srcFi.Size()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
n, err := io.Copy(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Err(err).Msg("Error writing to file")
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Sent: n,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
src.Close()
|
||||||
|
dst.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
src.Close()
|
||||||
|
dst.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
@ -82,8 +82,12 @@ type TimelineHeader struct {
|
|||||||
// NewHeader creates and populates a new timeline header
|
// NewHeader creates and populates a new timeline header
|
||||||
// and returns it
|
// and returns it
|
||||||
func NewHeader(evtType EventType, expires time.Duration) TimelineHeader {
|
func NewHeader(evtType EventType, expires time.Duration) TimelineHeader {
|
||||||
|
now := time.Now()
|
||||||
|
_, offset := now.Zone()
|
||||||
|
now = now.Add(time.Duration(offset) * time.Second)
|
||||||
|
|
||||||
return TimelineHeader{
|
return TimelineHeader{
|
||||||
Timestamp: uint64(time.Now().Unix()),
|
Timestamp: uint64(now.Unix()),
|
||||||
Expires: uint32(expires.Seconds()),
|
Expires: uint32(expires.Seconds()),
|
||||||
EventType: evtType,
|
EventType: evtType,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user