|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
phomemofilter "brainbaking.com/phomemoprinter/filter"
|
|
|
|
"flag"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"tinygo.org/x/bluetooth"
|
|
|
|
)
|
|
|
|
|
|
|
|
var adapter = bluetooth.DefaultAdapter
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var home, _ = os.UserHomeDir()
|
|
|
|
var defaultDir = filepath.Join(home, "Downloads", "phomemo")
|
|
|
|
|
|
|
|
dir := flag.String("dir", defaultDir, "Directory to pick up any images from")
|
|
|
|
file := flag.String("file", "", "Print only a specific image. If provided, -dir will be ignored.")
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if *file != "" {
|
|
|
|
log.Printf("Attempting to print file %s...\n", *file)
|
|
|
|
|
|
|
|
if !isPossibleToPrint(*file) {
|
|
|
|
log.Fatalln("Cannot print, this is not a photo (.jpg/.png only!)")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
*dir = filepath.Join(os.TempDir(), "phomemo")
|
|
|
|
os.Mkdir(*dir, os.ModePerm) // ignore error in case still existing
|
|
|
|
err := copyFileToFolder(*file, *dir)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Cannot copy %s to %s: %w. Quitting. \n", *file, *dir, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Printf("Attempting to scan dir %s for files to print...\n", *dir)
|
|
|
|
}
|
|
|
|
|
|
|
|
files, err := os.ReadDir(*dir)
|
|
|
|
must("Open directory target", err)
|
|
|
|
if len(files) == 0 {
|
|
|
|
log.Fatalf("No files to print found in %s, nothing to do. Quitting.\n", *dir)
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
must("Enable Bluetooth stack", adapter.Enable())
|
|
|
|
pyfilterloc := dumpFilter(*dir)
|
|
|
|
defer func() {
|
|
|
|
os.RemoveAll(pyfilterloc)
|
|
|
|
}()
|
|
|
|
|
|
|
|
log.Println("Scanning for Bluetooth devices...")
|
|
|
|
ch := make(chan bluetooth.ScanResult, 1)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err = adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
|
|
|
|
if device.LocalName() == "Mr.in_M02" {
|
|
|
|
ch <- device
|
|
|
|
adapter.StopScan()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
must("start scan", err)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case device := <-ch:
|
|
|
|
log.Println("Found device: ", device.Address.String(), device.RSSI, device.LocalName())
|
|
|
|
for _, file := range files {
|
|
|
|
filePath := filepath.Join(*dir, file.Name())
|
|
|
|
if isPossibleToPrint(filePath) {
|
|
|
|
log.Printf("Trying to print %s\n", filePath)
|
|
|
|
tryToPrint(device, pyfilterloc, filePath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
log.Fatal("Timeout trying to locate Phomemo M02, is it on? Quitting.")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyFileToFolder(file string, folder string) error {
|
|
|
|
contents, err := os.ReadFile(file)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = os.WriteFile(filepath.Join(folder, "toprint.jpg"), contents, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func tryToPrint(phomemoAddress bluetooth.ScanResult, pyfilterloc string, filePath string) {
|
|
|
|
var phomemo *bluetooth.Device
|
|
|
|
phomemo, err := adapter.Connect(phomemoAddress.Address, bluetooth.ConnectionParams{})
|
|
|
|
must("Failed to connect to adapter", err)
|
|
|
|
log.Println("Connected to ", phomemoAddress.Address.String())
|
|
|
|
defer func() {
|
|
|
|
if err := phomemo.Disconnect(); err != nil {
|
|
|
|
log.Fatalf("Cannot disconnect: %w\n", err)
|
|
|
|
} else {
|
|
|
|
log.Println("disconnected")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
srvcs, err := phomemo.DiscoverServices(nil)
|
|
|
|
must("failed to discover service", err)
|
|
|
|
log.Println("Discovering all services on device...")
|
|
|
|
|
|
|
|
srvc := srvcs[0] // there's only one (0000ff00-...) anyway
|
|
|
|
log.Println("- service", srvc.UUID().String())
|
|
|
|
|
|
|
|
chars, err := srvc.DiscoverCharacteristics(nil)
|
|
|
|
must("Failed to discover characteristics of service", err)
|
|
|
|
|
|
|
|
// 3 characteristics to discover 0000ff01, 0000ff02, and 0000ff03
|
|
|
|
writeData(pyfilterloc, filePath, chars[1]) // 0000ff02-...
|
|
|
|
readResult(chars[0]) // 0000ff01-...
|
|
|
|
}
|
|
|
|
|
|
|
|
func isPossibleToPrint(filename string) bool {
|
|
|
|
return strings.HasSuffix(filename, ".jpg") ||
|
|
|
|
strings.HasSuffix(filename, ".JPG") ||
|
|
|
|
strings.HasSuffix(filename, ".png") ||
|
|
|
|
strings.HasSuffix(filename, ".PNG")
|
|
|
|
}
|
|
|
|
|
|
|
|
func dumpFilter(dir string) string {
|
|
|
|
filename := filepath.Join(dir, "phomemo-filter.py")
|
|
|
|
err := os.WriteFile(filename, phomemofilter.Pyfilter, 0666)
|
|
|
|
must("unable to dump python filter for use", err)
|
|
|
|
return filename
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeData(pyfilterloc string, path string, char bluetooth.DeviceCharacteristic) {
|
|
|
|
// first, it needs to be prepared for the printer
|
|
|
|
pathpho := path + ".pho"
|
|
|
|
argstr := []string{"-c", "/usr/local/bin/python " + pyfilterloc + " " + path + " > " + pathpho}
|
|
|
|
log.Println("args", argstr)
|
|
|
|
_, err := exec.Command("/bin/zsh", argstr...).Output()
|
|
|
|
must("something went wrong while filtering phomemo data py", err)
|
|
|
|
|
|
|
|
data, _ := os.ReadFile(pathpho)
|
|
|
|
|
|
|
|
log.Printf("-- writing file %s to characteristic %s\n", path, char.UUID().String())
|
|
|
|
_, err = char.WriteWithoutResponse(data)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(" ", err.Error())
|
|
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
os.RemoveAll(pathpho)
|
|
|
|
}
|
|
|
|
|
|
|
|
func readResult(char bluetooth.DeviceCharacteristic) {
|
|
|
|
log.Println("-- reading from to characteristic", char.UUID().String())
|
|
|
|
|
|
|
|
buf := make([]byte, 28)
|
|
|
|
n, err := char.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(" ", err.Error())
|
|
|
|
} else {
|
|
|
|
log.Println(" data bytes", strconv.Itoa(n))
|
|
|
|
log.Println(" value =", buf[:n])
|
|
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
func must(action string, err error) {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Fatal error: Failed to "+action+": %w \n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|