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+ # Driver information can be found in .\devDocs\brailleDrivers\SeikaNotetaker.md
910
1011from io import BytesIO
1112import typing
12- from typing import List
13+ from typing import List , Set
1314
1415import braille
1516import brailleInput
4748 0x000400 : "RJ_LEFT" ,
4849 0x000800 : "RJ_RIGHT" ,
4950 0x001000 : "RJ_UP" ,
50- 0x002000 : "RJ_DOWN"
51+ 0x002000 : "RJ_DOWN" ,
5152}
5253
5354SEIKA_REQUEST_INFO = b"\x03 \xff \xff \xa1 "
@@ -100,6 +101,7 @@ def __init__(self, port="hid"):
100101 super ().__init__ ()
101102 self .numCells = 0
102103 self .numBtns = 0
104+ self .numRoutingKeys = 0
103105 self .handle = None
104106
105107 self ._hidBuffer = b""
@@ -153,7 +155,7 @@ def _onReceive(self, data: bytes):
153155 The buffer is accumulated until the buffer has the required number of bytes for the field being collected.
154156 There are 3 fields to be collected before a command can be processed:
155157 1: first 3 bytes: command
156- 2: 1 byte: specify total length in bytes?
158+ 2: 1 byte: specify length of subsequent arguments in bytes
157159 3: variable length: arguments for command type
158160
159161 After accumulating enough bytes for each phase, the buffer is cleared and the next stage is entered.
@@ -175,16 +177,12 @@ def _onReceive(self, data: bytes):
175177 hasCommandBeenCollected
176178 and not hasArgLenBeenCollected # argsLen has not
177179 ):
178- # Unknown why we must wait for 4 extra bytes. Without a device to inspect actual data
179- # it has to be assumed that the prior approach is correct, and infer what we can from
180- # it.
181- # Best guess: the data is sent with the following structure
180+ # the data is sent with the following structure
182181 # - command name (3 bytes)
183- # - total bytes Command + Args size (1 byte)
182+ # - number of subsequent bytes to read (1 byte)
184183 # - Args (variable bytes)
185- # - Constant 4 bytes containing unknown
186- self ._argsLen = ord (newByte ) - COMMAND_LEN + 4
187- # don't reset _hidBuffer the value for total length
184+ self ._argsLen = ord (newByte )
185+ self ._hidBuffer = b""
188186 elif ( # now collect the args,
189187 hasCommandBeenCollected
190188 and hasArgLenBeenCollected
@@ -212,39 +210,37 @@ def _processCommand(self, command: bytes, arg: bytes) -> None:
212210 log .warning (f"Seika device has received an unknown command { command } " )
213211
214212 def _handInfo (self , arg : bytes ):
215- self .numCells = arg [2 ]
216- self .numBtns = arg [1 ]
213+ self .numBtns = arg [0 ]
214+ self .numCells = arg [1 ]
215+ self .numRoutingKeys = arg [2 ]
216+ self ._description = arg [3 :].decode ("ascii" )
217217
218218 def _handRouting (self , arg : bytes ):
219- for i in range (arg [0 ]):
220- for j in range (8 ):
221- if arg [i + 1 ] & (1 << j ):
222- routingIndex = i * 8 + j
223- gesture = InputGestureRouting (routingIndex )
224- try :
225- inputCore .manager .executeGesture (gesture )
226- except inputCore .NoInputGestureAction :
227- log .debug ("No action for Seika Notetaker routing command" )
219+ routingIndexes = _getRoutingIndexes (arg )
220+ for routingIndex in routingIndexes :
221+ gesture = InputGestureRouting (routingIndex )
222+ try :
223+ inputCore .manager .executeGesture (gesture )
224+ except inputCore .NoInputGestureAction :
225+ log .debug ("No action for Seika Notetaker routing command" )
228226
229227 def _handKeys (self , arg : bytes ):
230- brailleDots = arg [1 ]
231- key = arg [2 ] | (arg [3 ] << 8 )
232- gesture = None
233- if key : # Mini Seika has 2 Top and 4 Front
234- gesture = InputGesture (keys = key )
228+ brailleDots = arg [0 ]
229+ key = arg [1 ] | (arg [2 ] << 8 )
230+ gestures = []
231+ if key :
232+ gestures . append ( InputGesture (keys = key ) )
235233 if brailleDots :
236- gesture = InputGesture (dots = brailleDots )
237- if gesture is not None :
234+ gestures . append ( InputGesture (dots = brailleDots ) )
235+ for gesture in gestures :
238236 try :
239237 inputCore .manager .executeGesture (gesture )
240238 except inputCore .NoInputGestureAction :
241- log .debug ("No action for Seika Notetaker keys." )
239+ log .debug ("No action for Seika Notetaker keys." )
242240
243241 def _handKeysRouting (self , arg : bytes ):
244- argk = b"\x03 " + arg [1 :]
245- argr = (arg [0 ] - 3 ).to_bytes (1 , 'little' ) + arg [4 :]
246- self ._handRouting (argr )
247- self ._handKeys (argk )
242+ self ._handRouting (arg [3 :])
243+ self ._handKeys (arg [:3 ])
248244
249245 gestureMap = inputCore .GlobalGestureMap ({
250246 "globalCommands.GlobalCommands" : {
@@ -290,6 +286,18 @@ def __init__(self, index):
290286 self .routingIndex = index
291287
292288
289+ def _getKeyNames (keys : int ) -> Set [int ]:
290+ return {_keyNames [1 << i ] for i in range (16 ) if (1 << i ) & keys }
291+
292+
293+ def _getDotNames (dots : int ) -> Set [int ]:
294+ return {_dotNames [1 << i ] for i in range (8 ) if (1 << i ) & dots }
295+
296+
297+ def _getRoutingIndexes (routingKeys : bytes ) -> Set [int ]:
298+ return {i * 8 + j for i in range (len (routingKeys )) for j in range (8 ) if routingKeys [i ] & (1 << j )}
299+
300+
293301class InputGesture (braille .BrailleDisplayGesture , brailleInput .BrailleInputGesture ):
294302 source = BrailleDisplayDriver .name
295303
@@ -298,13 +306,13 @@ def __init__(self, keys=None, dots=None, space=False, routing=None):
298306 # see what thumb keys are pressed:
299307 names = set ()
300308 if keys is not None :
301- names .update (_keyNames [ 1 << i ] for i in range ( 22 ) if ( 1 << i ) & keys )
309+ names .update (_getKeyNames ( keys ) )
302310 elif dots is not None :
303311 self .dots = dots
304312 if space :
305313 self .space = space
306314 names .add (_keyNames [1 ])
307- names .update (_dotNames [ 1 << i ] for i in range ( 8 ) if ( 1 << i ) & dots )
315+ names .update (_getDotNames ( dots ) )
308316 elif routing is not None :
309317 self .routingIndex = routing
310318 names .add ('routing' )
0 commit comments