Skip to content

Commit 1f2f203

Browse files
authored
Merge 08ac346 into b5f82f8
2 parents b5f82f8 + 08ac346 commit 1f2f203

4 files changed

Lines changed: 73 additions & 32 deletions

File tree

source/bdDetect.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,8 @@ def driverSupportsAutoDetection(driver):
706706
addUsbDevices("seikantk", KEY_HID, {
707707
"VID_10C4&PID_EA80", # Seika Notetaker
708708
})
709+
710+
# Bluetooth name of the Seika devices is "TSM abcd", where the "abcd" is a four-digit
711+
# number, e.g. "TSM 3366", "TSM 0001", etc. There is a space between "TSM" and "abcd".
712+
seikaBluetoothNameRegex = re.compile(r"TSM \d\d\d\d")
713+
addBluetoothDevices("seikantk", lambda m: bool(seikaBluetoothNameRegex.match(m.deviceInfo["bluetoothName"])))

source/braille.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2493,7 +2493,7 @@ def getManualPorts(cls) -> typing.Iterator[typing.Tuple[str, str]]:
24932493
@classmethod
24942494
def _getTryPorts(
24952495
cls, port: Union[str, bdDetect.DeviceMatch]
2496-
) -> Iterable[bdDetect.DeviceMatch]:
2496+
) -> typing.Iterator[bdDetect.DeviceMatch]:
24972497
"""Returns the ports for this driver to which a connection attempt should be made.
24982498
This generator function is usually used in L{__init__} to connect to the desired display.
24992499
@param port: the port to connect to.

source/brailleDisplayDrivers/seika.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def __init__(self):
9292
# Read out the input buffer
9393
versionS = self._ser.read(12)
9494
log.debug(f"receive {versionS}")
95-
if versionS.startswith(prefix=(
95+
if versionS.startswith((
9696
b'\x00\x05(\x08v5.0\x01\x01\x01\x01',
9797
b'\x00\x05(\x08seika\x00'
9898
)):

source/brailleDisplayDrivers/seikantk.py

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
# This file represents the braille display driver for
77
# Seika Notetaker, a product from Nippon Telesoft
88
# see www.seika-braille.com for more details
9-
109
from io import BytesIO
1110
import typing
1211
from typing import Dict, List, Set
1312

13+
import serial
14+
1415
import braille
1516
import brailleInput
1617
import inputCore
17-
import hwPortUtils
1818
import bdDetect
1919
import hwIo
2020
from serial.win32 import INVALID_HANDLE_VALUE
@@ -57,66 +57,102 @@
5757
SEIKA_KEYS = b"\xff\xff\xa6"
5858
SEIKA_KEYS_ROU = b"\xff\xff\xa8"
5959

60-
SEIKA_CONFIG = b"\x50\x00\x00\x25\x80\x00\x00\x03\x00"
60+
BAUD = 9600
61+
SEIKA_HID_FEATURES = b"".join([
62+
b"\x50\x00\x00",
63+
int.to_bytes(BAUD, length=2, byteorder="big", signed=False), # b"\x25\x80"
64+
b"\x00\x00\x03\x00",
65+
])
6166
SEIKA_CMD_ON = b"\x41\x01"
6267

6368
vidpid = "VID_10C4&PID_EA80"
6469
hidvidpid = "HID\\VID_10C4&PID_EA80"
6570
SEIKA_NAME = "seikantk"
6671

67-
6872
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
6973
_dev: hwIo.IoBase
7074
name = SEIKA_NAME
7175
# Translators: Name of a braille display.
7276
description = _("Seika Notetaker")
73-
path = ""
7477
isThreadSafe = True
75-
for d in hwPortUtils.listHidDevices():
76-
if d["hardwareID"].startswith(hidvidpid):
77-
path = d["devicePath"]
7878

7979
@classmethod
8080
def getManualPorts(cls) -> typing.Iterator[typing.Tuple[str, str]]:
8181
"""@return: An iterator containing the name and description for each port.
8282
"""
8383
return braille.getSerialPorts()
8484

85-
def __init__(self, port="hid"):
85+
def __init__(self, port=bdDetect.KEY_HID):
8686
super().__init__()
8787
self.numCells = 0
8888
self.numBtns = 0
8989
self.numRoutingKeys = 0
9090
self.handle = None
91-
9291
self._hidBuffer = b""
9392
self._command: typing.Optional[bytes] = None
9493
self._argsLen: typing.Optional[int] = None
95-
log.info(f"Seika Notetaker braille driver path: {self.path}")
96-
97-
if self.path == "":
98-
raise RuntimeError("No MINI-SEIKA display found, no path found")
99-
self._dev = dev = hwIo.Hid(path=self.path, onReceive=self._onReceive)
100-
if dev._file == INVALID_HANDLE_VALUE:
101-
raise RuntimeError("No MINI-SEIKA display found, open error")
102-
dev.setFeature(SEIKA_CONFIG) # baud rate, stop bit usw
103-
dev.setFeature(SEIKA_CMD_ON) # device on
94+
95+
log.debug(f"Seika Notetaker braille driver: ({port!r})")
96+
dev: typing.Optional[typing.Union[hwIo.Hid, hwIo.Serial]] = None
97+
for match in self._getTryPorts(port):
98+
self.isHid = match.type == bdDetect.KEY_HID
99+
self.isSerial = match.type == bdDetect.KEY_SERIAL
100+
try:
101+
if self.isHid:
102+
log.info(f"Trying Seika notetaker on USB-HID")
103+
self._dev = dev = hwIo.Hid(
104+
path=match.port, # for a Hid match type 'port' is actually 'path'.
105+
onReceive=self._onReceive
106+
)
107+
dev.setFeature(SEIKA_HID_FEATURES) # baud rate, stop bit usw
108+
dev.setFeature(SEIKA_CMD_ON) # device on
109+
elif self.isSerial:
110+
log.info(f"Trying Seika notetaker on Bluetooth (serial) port:{port}")
111+
self._dev = dev = hwIo.Serial(
112+
port=match.port,
113+
onReceive=self._onReceive,
114+
baudrate=BAUD,
115+
parity=serial.PARITY_NONE,
116+
bytesize=serial.EIGHTBITS,
117+
stopbits=serial.STOPBITS_ONE,
118+
)
119+
# Does the device need to be sent SEIKA_CMD_ON as per USB-HID?
120+
else:
121+
log.debug(f"Port type not handled: {match.type}")
122+
continue
123+
except EnvironmentError:
124+
log.debugWarning("", exc_info=True)
125+
continue
126+
if self._getDeviceInfo(dev):
127+
break
128+
elif dev:
129+
dev.close()
130+
131+
if not dev:
132+
RuntimeError("No MINI-SEIKA display found")
133+
elif self.numCells == 0:
134+
dev.close()
135+
raise RuntimeError("No MINI-SEIKA display found, no response")
136+
else:
137+
log.info(
138+
f"Seika notetaker,"
139+
f" Cells {self.numCells}"
140+
f" Buttons {self.numBtns}"
141+
)
142+
143+
def _getDeviceInfo(self, dev: hwIo.IoBase) -> bool:
144+
if not dev or dev._file == INVALID_HANDLE_VALUE:
145+
log.debug("No MINI-SEIKA display found, open error")
146+
return False
147+
104148
dev.write(SEIKA_REQUEST_INFO) # Request the Info from the device
105149

106150
# wait and try to get info from the Braille display
107151
for i in range(MAX_READ_ATTEMPTS): # the info-block is about
108152
dev.waitForRead(READ_TIMEOUT_SECS)
109153
if self.numCells:
110-
log.info(
111-
f"Seika notetaker on USB-HID,"
112-
f" Cells {self.numCells}"
113-
f" Buttons {self.numBtns}"
114-
)
115-
break
116-
117-
if self.numCells == 0:
118-
dev.close()
119-
raise RuntimeError("No MINI-SEIKA display found, no response")
154+
return True
155+
return False
120156

121157
def terminate(self):
122158
try:
@@ -126,7 +162,7 @@ def terminate(self):
126162

127163
def display(self, cells: List[int]):
128164
# cells will already be padded up to numCells.
129-
cellBytes = SEIKA_SEND_TEXT + self.numCells.to_bytes(1, 'little') + bytes(cells)
165+
cellBytes = SEIKA_SEND_TEXT + bytes([self.numCells]) + bytes(cells)
130166
self._dev.write(cellBytes)
131167

132168
def _onReceive(self, data: bytes):

0 commit comments

Comments
 (0)