@@ -110,6 +110,8 @@ def windowProc(self, hwnd: int, msg: int, wParam: int, lParam: int):
110110MODEL_MODULAR_80 = b"\x88 "
111111MODEL_MODULAR_40 = b"\x89 "
112112MODEL_ACTIVATOR = b"\xA4 "
113+ MODEL_ACTIVATOR_PRO_64 = b"\xA6 "
114+ MODEL_ACTIVATOR_PRO_80 = b"\xA8 "
113115
114116# Key constants
115117KEY_B1 = 0x03
@@ -373,6 +375,15 @@ def display(self, cells: List[int]):
373375 super (StatusCellMixin , self ).display (cells )
374376
375377
378+ class ActiveSplitMixin (object ):
379+ """Mixin for displays supporting ActiveSplit, i.e. dynamic adjustment of number of cells"""
380+
381+ def postInit (self ):
382+ super (ActiveSplitMixin , self ).postInit ()
383+ log .debug ("Prevent disconnect/reconnect activity for dynamic length adjustment" )
384+ self ._display .sendExtendedPacket (HT_EXTPKT_NO_RECONNECT )
385+
386+
376387class ModularConnect88 (TripleActionKeysMixin , Model ):
377388 deviceId = MODEL_MODULAR_CONNECT_88
378389 genericName = "Modular Connect"
@@ -531,7 +542,7 @@ class Modular80(Modular):
531542 numCells = 80
532543
533544
534- class Activator (TimeSyncFirmnessMixin , AtcMixin , JoystickMixin , TripleActionKeysMixin , Model ):
545+ class Activator (ActiveSplitMixin , TimeSyncFirmnessMixin , AtcMixin , JoystickMixin , TripleActionKeysMixin , Model ):
535546 deviceId = MODEL_ACTIVATOR
536547 numCells = 40
537548 genericName = name = 'Activator'
@@ -545,6 +556,34 @@ def _get_keys(self) -> Dict[int, str]:
545556 return keys
546557
547558
559+ class ActivatorPro64 (ActiveSplitMixin , TimeSyncFirmnessMixin , AtcMixin , TripleActionKeysMixin , Model ):
560+ deviceId = MODEL_ACTIVATOR_PRO_64
561+ numCells = 64
562+ genericName = name = 'Activator Pro 64'
563+
564+ def _get_keys (self ) -> Dict [int , str ]:
565+ keys = super ().keys
566+ keys .update ({
567+ 0x7A : "escape" ,
568+ 0x7B : "return" ,
569+ })
570+ return keys
571+
572+
573+ class ActivatorPro80 (ActiveSplitMixin , TimeSyncFirmnessMixin , AtcMixin , TripleActionKeysMixin , Model ):
574+ deviceId = MODEL_ACTIVATOR_PRO_80
575+ numCells = 80
576+ genericName = name = 'Activator Pro 80'
577+
578+ def _get_keys (self ) -> Dict [int , str ]:
579+ keys = super ().keys
580+ keys .update ({
581+ 0x7A : "escape" ,
582+ 0x7B : "return" ,
583+ })
584+ return keys
585+
586+
548587def _allSubclasses (cls ):
549588 """List all direct and indirect subclasses of cls
550589
@@ -569,6 +608,7 @@ def _allSubclasses(cls):
569608HT_PKT_NAK = b"\x7D "
570609HT_PKT_ACK = b"\x7E "
571610HT_PKT_OK = b"\xFE "
611+ HT_PKT_OK_WITH_LENGTH = b"\xFD "
572612HT_PKT_RESET = b"\xFF "
573613HT_EXTPKT_BRAILLE = HT_PKT_BRAILLE
574614HT_EXTPKT_KEY = b"\x04 "
@@ -589,6 +629,7 @@ def _allSubclasses(cls):
589629HT_EXTPKT_GET_FIRMNESS = b"\x61 "
590630HT_EXTPKT_GET_PROTOCOL_PROPERTIES = b"\xC1 "
591631HT_EXTPKT_GET_FIRMWARE_VERSION = b"\xC2 "
632+ HT_EXTPKT_NO_RECONNECT = b"\xAE "
592633
593634# HID specific constants
594635HT_HID_RPT_OutData = b"\x01 " # receive data from device
@@ -637,6 +678,8 @@ def registerAutomaticDetection(cls, driverRegistrar: bdDetect.DriverRegistrar):
637678 "VID_1FE4&PID_0093" , # Basic Braille Plus 32
638679 "VID_1FE4&PID_0094" , # Basic Braille Plus 40
639680 "VID_1FE4&PID_00A4" , # Activator
681+ "VID_1FE4&PID_00A6" , # Activator Pro 64
682+ "VID_1FE4&PID_00A8" , # Activator Pro 80
640683 })
641684
642685 # Some older HT displays use a HID converter and an internal serial interface
@@ -656,7 +699,7 @@ def registerAutomaticDetection(cls, driverRegistrar: bdDetect.DriverRegistrar):
656699 "Braillino BL" ,
657700 "Braille Wave BW" ,
658701 "Easy Braille EBR" ,
659- "Activator AC " ,
702+ "Activator" ,
660703 )))
661704
662705 @classmethod
@@ -712,6 +755,9 @@ def __init__(self, port="auto"):
712755
713756 if self .numCells :
714757 # A display responded.
758+ if not isinstance (self ._model , OldProtocolMixin ):
759+ self .sendExtendedPacket (HT_EXTPKT_GET_PROTOCOL_PROPERTIES )
760+ self ._dev .waitForRead (self .timeout )
715761 self ._model .postInit ()
716762 log .info ("Found {device} connected via {type} ({port})" .format (
717763 device = self ._model .name , type = portType , port = port ))
@@ -923,15 +969,18 @@ def _serialOnReceive(self, data: bytes):
923969 self ._handleInputStream (data , self ._dev )
924970
925971 def _handleInputStream (self , htPacketType : bytes , stream ):
926- if htPacketType in (HT_PKT_OK , HT_PKT_EXTENDED ):
972+ if htPacketType in (HT_PKT_OK , HT_PKT_EXTENDED , HT_PKT_OK_WITH_LENGTH ):
927973 modelId : bytes = stream .read (1 )
928974 if not self ._model :
929975 if modelId not in MODELS :
930976 log .debugWarning ("Unknown model: %r" % modelId )
931977 raise RuntimeError (
932978 "The model with ID %r is not supported by this driver" % modelId )
933979 self ._model = MODELS .get (modelId )(self )
934- self .numCells = self ._model .numCells
980+ if htPacketType == HT_PKT_OK_WITH_LENGTH :
981+ self .numCells = ord (stream .read (1 ))
982+ else :
983+ self .numCells = self ._model .numCells
935984 elif self ._model .deviceId != modelId :
936985 # Somehow the model ID of this display changed, probably another display
937986 # plugged in the same (already open) serial port.
@@ -962,7 +1011,7 @@ def _handleInputStream(self, htPacketType: bytes, stream):
9621011 # Ignore ATC packets for now
9631012 pass
9641013 elif extPacketType == HT_EXTPKT_GET_PROTOCOL_PROPERTIES :
965- pass
1014+ self . numCells = packet [ 3 ]
9661015 elif isinstance (self ._model , TimeSyncFirmnessMixin ):
9671016 if extPacketType == HT_EXTPKT_GET_RTC :
9681017 self ._model .handleTime (packet [1 :])
0 commit comments