forked from Elara6331/infinitime
Merge branch 'master' into gatt_localtime
This commit is contained in:
commit
ce90459491
11
README.md
11
README.md
@ -15,24 +15,24 @@ This library's import path is `go.arsenm.dev/infinitime`.
|
||||
|
||||
### 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
|
||||
|
||||
```shell
|
||||
sudo pacman -S dbus bluez libpulse --needed
|
||||
sudo pacman -S dbus bluez --needed
|
||||
```
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
```shell
|
||||
sudo apt install dbus bluez pulseaudio-utils
|
||||
sudo apt install dbus bluez
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
||||
```shell
|
||||
sudo dnf install dbus bluez pulseaudio-utils
|
||||
sudo dnf install dbus bluez
|
||||
```
|
||||
|
||||
---
|
||||
@ -47,9 +47,10 @@ This library currently supports the following features:
|
||||
- Battery level
|
||||
- Music control
|
||||
- OTA firmware upgrades
|
||||
- Navigation
|
||||
|
||||
---
|
||||
|
||||
### Mentions
|
||||
|
||||
The DFU process used in this library was created with the help of [siglo](https://github.com/alexr4535/siglo)'s source code. Specifically, this file: [ble_dfu.py](https://github.com/alexr4535/siglo/blob/main/src/ble_dfu.py)
|
||||
The DFU process used in this library was created with the help of [siglo](https://github.com/alexr4535/siglo)'s source code. Specifically, this file: [ble_dfu.py](https://github.com/alexr4535/siglo/blob/main/src/ble_dfu.py)
|
||||
|
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
|
||||
|
||||
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
|
||||
func (blefs *FS) Rename(old, new string) error {
|
||||
// Create move request
|
||||
|
@ -13,6 +13,7 @@ var (
|
||||
ErrOffsetChanged = errors.New("offset has already been changed")
|
||||
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")
|
||||
ErrNoRemoveRoot = errors.New("refusing to remove root directory")
|
||||
)
|
||||
|
||||
// FSError represents an error returned by BLE FS
|
||||
|
@ -3,7 +3,6 @@ package blefs
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -291,7 +290,7 @@ func (fl *File) Write(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
close(fl.offsetCh)
|
||||
return int(fl.offset), nil
|
||||
return int(fl.amtTferd), nil
|
||||
}
|
||||
|
||||
// WriteString converts the string to []byte and calls Write()
|
||||
@ -335,23 +334,9 @@ func (fl *File) Close() error {
|
||||
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) {
|
||||
// Get directory in filepath
|
||||
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
|
||||
return fl.fs.Stat(fl.path)
|
||||
}
|
||||
|
||||
// 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
|
||||
// is never exceeded.
|
||||
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.
|
||||
|
@ -12,4 +12,4 @@ func (iofs goFS) Open(path string) (fs.File, error) {
|
||||
|
||||
func (blefs *FS) GoFS() fs.FS {
|
||||
return goFS{blefs}
|
||||
}
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/fxamacker/cbor/v2 v2.4.0
|
||||
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
|
||||
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/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/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA=
|
||||
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268 h1:kOnq7TfaAO2Vc/MHxPqFIXe00y1qBxJAvhctXdko6vo=
|
||||
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/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -42,18 +42,22 @@ const (
|
||||
)
|
||||
|
||||
var charNames = map[string]string{
|
||||
NewAlertChar: "New Alert",
|
||||
NotifEventChar: "Notification Event",
|
||||
StepCountChar: "Step Count",
|
||||
MotionValChar: "Motion Values",
|
||||
FirmwareVerChar: "Firmware Version",
|
||||
CurrentTimeChar: "Current Time",
|
||||
LocalTimeChar: "Local Time",
|
||||
BatteryLvlChar: "Battery Level",
|
||||
HeartRateChar: "Heart Rate",
|
||||
FSTransferChar: "Filesystem Transfer",
|
||||
FSVersionChar: "Filesystem Version",
|
||||
WeatherDataChar: "Weather Data",
|
||||
NewAlertChar: "New Alert",
|
||||
NotifEventChar: "Notification Event",
|
||||
StepCountChar: "Step Count",
|
||||
MotionValChar: "Motion Values",
|
||||
FirmwareVerChar: "Firmware Version",
|
||||
CurrentTimeChar: "Current Time",
|
||||
LocalTimeChar: "Local Time",
|
||||
BatteryLvlChar: "Battery Level",
|
||||
HeartRateChar: "Heart Rate",
|
||||
FSTransferChar: "Filesystem Transfer",
|
||||
FSVersionChar: "Filesystem Version",
|
||||
WeatherDataChar: "Weather Data",
|
||||
NavFlagsChar: "Navigation Icon",
|
||||
NavNarrativeChar: "Navigation Instruction",
|
||||
NavManDistChar: "Navigation Distance to next event",
|
||||
NavProgressChar: "Navigation Progress",
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
@ -70,9 +74,11 @@ type Device struct {
|
||||
fsVersionChar *gatt.GattCharacteristic1
|
||||
fsTransferChar *gatt.GattCharacteristic1
|
||||
weatherDataChar *gatt.GattCharacteristic1
|
||||
weatherdataChar *gatt.GattCharacteristic1
|
||||
notifEventCh chan uint8
|
||||
notifEventDone bool
|
||||
Music MusicCtrl
|
||||
Navigation NavigationService
|
||||
DFU DFU
|
||||
}
|
||||
|
||||
@ -133,6 +139,7 @@ func Connect(ctx context.Context, opts *Options) (*Device, error) {
|
||||
|
||||
// Create new device
|
||||
out := &Device{device: btDev}
|
||||
out.Navigation = NavigationService{dev: out}
|
||||
|
||||
// Resolve characteristics
|
||||
err = out.resolveChars()
|
||||
@ -397,6 +404,14 @@ func (i *Device) resolveChars() error {
|
||||
charResolved := true
|
||||
// Set correct characteristics
|
||||
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:
|
||||
i.newAlertChar = char
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
// and returns it
|
||||
func NewHeader(evtType EventType, expires time.Duration) TimelineHeader {
|
||||
now := time.Now()
|
||||
_, offset := now.Zone()
|
||||
now = now.Add(time.Duration(offset) * time.Second)
|
||||
|
||||
return TimelineHeader{
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Timestamp: uint64(now.Unix()),
|
||||
Expires: uint32(expires.Seconds()),
|
||||
EventType: evtType,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user