forked from Elara6331/infinitime
		
	Compare commits
	
		
			20 Commits
		
	
	
		
			9ed74726c4
			...
			0c369dc5df
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 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= | ||||||
|   | |||||||
| @@ -52,6 +52,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 { | ||||||
| @@ -67,9 +71,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 | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -130,6 +136,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() | ||||||
| @@ -394,6 +401,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, | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user