@@ -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,14 @@ class Modular80(Modular):
531542 numCells = 80
532543
533544
534- class Activator (TimeSyncFirmnessMixin , AtcMixin , JoystickMixin , TripleActionKeysMixin , Model ):
545+ class Activator (
546+ ActiveSplitMixin ,
547+ TimeSyncFirmnessMixin ,
548+ AtcMixin ,
549+ JoystickMixin ,
550+ TripleActionKeysMixin ,
551+ Model
552+ ):
535553 deviceId = MODEL_ACTIVATOR
536554 numCells = 40
537555 genericName = name = 'Activator'
@@ -545,6 +563,40 @@ def _get_keys(self) -> Dict[int, str]:
545563 return keys
546564
547565
566+ class ActivatorPro64 (
567+ ActiveSplitMixin ,
568+ TimeSyncFirmnessMixin ,
569+ AtcMixin ,
570+ TripleActionKeysMixin ,
571+ Model
572+ ):
573+ deviceId = MODEL_ACTIVATOR_PRO_64
574+ numCells = 64
575+ genericName = name = 'Activator Pro 64'
576+
577+ def _get_keys (self ) -> Dict [int , str ]:
578+ keys = super ().keys
579+ keys .update ({
580+ 0x7A : "escape" ,
581+ 0x7B : "return" ,
582+ })
583+ return keys
584+
585+
586+ class ActivatorPro80 (ActiveSplitMixin , TimeSyncFirmnessMixin , AtcMixin , TripleActionKeysMixin , Model ):
587+ deviceId = MODEL_ACTIVATOR_PRO_80
588+ numCells = 80
589+ genericName = name = 'Activator Pro 80'
590+
591+ def _get_keys (self ) -> Dict [int , str ]:
592+ keys = super ().keys
593+ keys .update ({
594+ 0x7A : "escape" ,
595+ 0x7B : "return" ,
596+ })
597+ return keys
598+
599+
548600def _allSubclasses (cls ):
549601 """List all direct and indirect subclasses of cls
550602
@@ -569,6 +621,7 @@ def _allSubclasses(cls):
569621HT_PKT_NAK = b"\x7D "
570622HT_PKT_ACK = b"\x7E "
571623HT_PKT_OK = b"\xFE "
624+ HT_PKT_OK_WITH_LENGTH = b"\xFD "
572625HT_PKT_RESET = b"\xFF "
573626HT_EXTPKT_BRAILLE = HT_PKT_BRAILLE
574627HT_EXTPKT_KEY = b"\x04 "
@@ -589,6 +642,7 @@ def _allSubclasses(cls):
589642HT_EXTPKT_GET_FIRMNESS = b"\x61 "
590643HT_EXTPKT_GET_PROTOCOL_PROPERTIES = b"\xC1 "
591644HT_EXTPKT_GET_FIRMWARE_VERSION = b"\xC2 "
645+ HT_EXTPKT_NO_RECONNECT = b"\xAE "
592646
593647# HID specific constants
594648HT_HID_RPT_OutData = b"\x01 " # receive data from device
@@ -637,6 +691,8 @@ def registerAutomaticDetection(cls, driverRegistrar: bdDetect.DriverRegistrar):
637691 "VID_1FE4&PID_0093" , # Basic Braille Plus 32
638692 "VID_1FE4&PID_0094" , # Basic Braille Plus 40
639693 "VID_1FE4&PID_00A4" , # Activator
694+ "VID_1FE4&PID_00A6" , # Activator Pro 64
695+ "VID_1FE4&PID_00A8" , # Activator Pro 80
640696 })
641697
642698 # Some older HT displays use a HID converter and an internal serial interface
@@ -656,7 +712,7 @@ def registerAutomaticDetection(cls, driverRegistrar: bdDetect.DriverRegistrar):
656712 "Braillino BL" ,
657713 "Braille Wave BW" ,
658714 "Easy Braille EBR" ,
659- "Activator AC " ,
715+ "Activator" ,
660716 )))
661717
662718 @classmethod
@@ -712,6 +768,9 @@ def __init__(self, port="auto"):
712768
713769 if self .numCells :
714770 # A display responded.
771+ if not isinstance (self ._model , OldProtocolMixin ):
772+ self .sendExtendedPacket (HT_EXTPKT_GET_PROTOCOL_PROPERTIES )
773+ self ._dev .waitForRead (self .timeout )
715774 self ._model .postInit ()
716775 log .info ("Found {device} connected via {type} ({port})" .format (
717776 device = self ._model .name , type = portType , port = port ))
@@ -923,15 +982,18 @@ def _serialOnReceive(self, data: bytes):
923982 self ._handleInputStream (data , self ._dev )
924983
925984 def _handleInputStream (self , htPacketType : bytes , stream ):
926- if htPacketType in (HT_PKT_OK , HT_PKT_EXTENDED ):
985+ if htPacketType in (HT_PKT_OK , HT_PKT_EXTENDED , HT_PKT_OK_WITH_LENGTH ):
927986 modelId : bytes = stream .read (1 )
928987 if not self ._model :
929988 if modelId not in MODELS :
930989 log .debugWarning ("Unknown model: %r" % modelId )
931990 raise RuntimeError (
932991 "The model with ID %r is not supported by this driver" % modelId )
933992 self ._model = MODELS .get (modelId )(self )
934- self .numCells = self ._model .numCells
993+ if htPacketType == HT_PKT_OK_WITH_LENGTH :
994+ self .numCells = ord (stream .read (1 ))
995+ else :
996+ self .numCells = self ._model .numCells
935997 elif self ._model .deviceId != modelId :
936998 # Somehow the model ID of this display changed, probably another display
937999 # plugged in the same (already open) serial port.
@@ -962,7 +1024,7 @@ def _handleInputStream(self, htPacketType: bytes, stream):
9621024 # Ignore ATC packets for now
9631025 pass
9641026 elif extPacketType == HT_EXTPKT_GET_PROTOCOL_PROPERTIES :
965- pass
1027+ self . numCells = packet [ 3 ]
9661028 elif isinstance (self ._model , TimeSyncFirmnessMixin ):
9671029 if extPacketType == HT_EXTPKT_GET_RTC :
9681030 self ._model .handleTime (packet [1 :])
0 commit comments