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-
109from io import BytesIO
1110import typing
1211from typing import Dict , List , Set
1312
13+ import serial
14+
1415import braille
1516import brailleInput
1617import inputCore
17- import hwPortUtils
1818import bdDetect
1919import hwIo
2020from serial .win32 import INVALID_HANDLE_VALUE
5757SEIKA_KEYS = b"\xff \xff \xa6 "
5858SEIKA_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+ ])
6166SEIKA_CMD_ON = b"\x41 \x01 "
6267
6368vidpid = "VID_10C4&PID_EA80"
6469hidvidpid = "HID\\ VID_10C4&PID_EA80"
6570SEIKA_NAME = "seikantk"
6671
67-
6872class 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