@@ -94,7 +94,7 @@ def __init__(
9494        self ._bridge_info : Optional [GenericJSONDict ] =  None 
9595        self ._pending_requests : dict [str , dict [str , Any ]] =  {}
9696        self ._update_ports_from_device_info_task : Optional [asyncio .Task ] =  None 
97-         self ._update_ports_from_device_info_scheduled : set [Union [DevicePort , None ]] =  set ()
97+         self ._update_ports_from_device_info_scheduled : set [Union [str , None ]] =  set ()
9898
9999        super ().__init__ (** kwargs )
100100
@@ -513,31 +513,32 @@ async def make_port_args(self) -> list[dict[str, Any]]:
513513            }
514514        ]
515515
516-     def  update_ports_from_device_info_asap (self , changed_port : Optional [DevicePort ] =  None ) ->  None :
517-         if  changed_port :
518-             self .debug ('will update ports from device info asap (changed port = %s )' , changed_port )
516+     def  update_ports_from_device_info_asap (self , changed_friendly_name : Optional [str ] =  None ) ->  None :
517+         if  changed_friendly_name :
518+             self .debug ('will update ports from device info asap (changed friendly name = "%s" )' , changed_friendly_name )
519519        else :
520520            self .debug ('will update ports from device info asap' )
521-         self ._update_ports_from_device_info_scheduled .add (changed_port )
521+         self ._update_ports_from_device_info_scheduled .add (changed_friendly_name )
522522
523523    async  def  _update_ports_from_device_info_loop (self ) ->  None :
524524        try :
525525            while  True :
526526                try :
527527                    if  self ._update_ports_from_device_info_scheduled :
528+                         changed_friendly_names  =  set (n  for  n  in  self ._update_ports_from_device_info_scheduled  if  n )
528529                        self ._update_ports_from_device_info_scheduled .clear ()
529-                         await  self ._update_ports_from_device_info ()
530+                         await  self ._update_ports_from_device_info (changed_friendly_names )
530531                except  Exception :
531532                    self .error ('error while updating ports from device info' , exc_info = True )
532533
533534                await  asyncio .sleep (1 )
534535        except  asyncio .CancelledError :
535536            self .debug ('updating ports from device info task cancelled' , exc_info = True )
536537
537-     async  def  _update_ports_from_device_info (self ) ->  None :
538+     async  def  _update_ports_from_device_info (self ,  changed_friendly_names :  set [ str ] ) ->  None :
538539        self .debug ('updating ports from device info' )
539540        port_args_list  =  self ._port_args_from_device_info (self ._device_info_by_friendly_name )
540-         ports_by_id  =  {p .get_initial_id (): p  for  p  in  self .get_device_ports ()}
541+         ports_by_id  =  {p .get_initial_id (): p  for  p  in  self .get_device_ports ()  +   self . get_control_ports () }
541542        port_args_by_id  =  {
542543            pa ['id' ]: pa 
543544            for  pa  in  port_args_list 
@@ -554,11 +555,7 @@ async def _update_ports_from_device_info(self) -> None:
554555                await  self .remove_port (existing_id )
555556
556557        # Add all ports that don't yet exist on the server 
557-         changed_friendly_names  =  set (
558-             p .get_device_friendly_name ()
559-             for  p  in  self ._update_ports_from_device_info_scheduled 
560-             if  p 
561-         )
558+         changed_friendly_names  =  set (changed_friendly_names )
562559        for  new_id , port_args  in  port_args_by_id .items ():
563560            if  new_id  not  in ports_by_id :
564561                self .debug ('new port %s detected' , new_id )
@@ -661,8 +658,6 @@ def _port_args_from_device_definition(self, friendly_name: str, definition: dict
661658        control_port_args  =  {
662659            'driver' : DeviceControlPort ,
663660            'id' : safe_friendly_name ,
664-             'type' : core_ports .TYPE_BOOLEAN ,
665-             'writable' : True ,
666661            'device_friendly_name' : friendly_name ,
667662            'additional_attrdefs' : {},
668663        }
@@ -674,9 +669,9 @@ def _port_args_from_device_definition(self, friendly_name: str, definition: dict
674669        for  exposed_item  in  exposed_items :
675670            path_str  =  '.' .join (exposed_item ['path' ])
676671            is_attrdef  =  (
677-                 exposed_item .get ('category' ) in  ('config' , 'diagnostic' )
678-                 or   exposed_item .get ('type' ) ==  'text' 
679-                 or   exposed_item .get ('storage' ) ==  'config' 
672+                 exposed_item .get ('category' ) in  ('config' , 'diagnostic' )  or 
673+                 exposed_item .get ('type' ) ==  'text'   or 
674+                 exposed_item .get ('storage' ) ==  'config' 
680675            )
681676
682677            if  is_attrdef  and  any (fnmatch (path_str , pat ) for  pat  in  force_port_properties ):
@@ -744,7 +739,6 @@ def _port_args_from_device_definition(self, friendly_name: str, definition: dict
744739                    'unit' : exposed_item .get ('unit' ),
745740                    'min' : exposed_item .get ('value_min' ),
746741                    'max' : exposed_item .get ('value_max' ),
747-                     'additional_attrdefs' : {},
748742                    'device_friendly_name' : friendly_name ,
749743                    'property_path' : exposed_item ['path' ],
750744                    'storage' : exposed_item ['storage' ],
@@ -761,46 +755,55 @@ def _port_args_from_device_definition(self, friendly_name: str, definition: dict
761755        return  [control_port_args ] +  list (port_args_by_id .values ())
762756
763757    def  get_device_ports (self , friendly_name : Optional [str ] =  None ) ->  list [DevicePort ]:
758+         device_ports  =  [p  for  p  in  self .get_ports () if  isinstance (p , DevicePort )]
764759        if  friendly_name :
765-             return  [
766-                 p  for  p  in  self .get_ports ()
767-                 if  isinstance (p , DevicePort ) and  (
768-                     p .get_initial_id ().startswith (f'{ friendly_name }  ) or 
769-                     p .get_initial_id () ==  friendly_name 
770-                 )
760+             device_ports  =  [
761+                 p 
762+                 for  p  in  device_ports 
763+                 if  p .get_initial_id ().startswith (f'{ friendly_name }  ) or  p .get_initial_id () ==  friendly_name 
771764            ]
772-         else :
773-             return  [p  for  p  in  self .get_ports () if  isinstance (p , DevicePort )]
765+ 
766+         return  device_ports 
767+ 
768+     def  get_control_ports (self ) ->  list [DeviceControlPort ]:
769+         return  [p  for  p  in  self .get_ports () if  isinstance (p , DeviceControlPort )]
770+ 
771+     def  get_control_port (self , friendly_name : str ) ->  Optional [DeviceControlPort ]:
772+         safe_friendly_name  =  self .get_device_safe_friendly_name (friendly_name )
773+         return  self .get_port (safe_friendly_name )
774774
775775    async  def  _maybe_trigger_port_update (self , friendly_name : str , old_properties : dict , new_properties : dict ) ->  None :
776+         control_port  =  self .get_control_port (friendly_name )
777+         if  not  control_port :
778+             return 
779+ 
776780        # Gather all properties that have just changed 
777781        changed_properties  =  (
778-             set (k  for  k , v  in  new_properties .items () if  v  !=  old_properties .get (k ))
779-             |   set (k  for  k , v  in  old_properties .items () if  v  !=  new_properties .get (k ))
782+             set (k  for  k , v  in  new_properties .items () if  v  !=  old_properties .get (k ))  | 
783+             set (k  for  k , v  in  old_properties .items () if  v  !=  new_properties .get (k ))
780784        )
781785
782786        # Gather all properties that represent port values 
783787        device_ports  =  self .get_device_ports (friendly_name )
784788        value_properties  =  set ()
785789        for  port  in  device_ports :
786-             if  not  isinstance (port , DeviceControlPort ):
787-                 value_properties .add (port .get_property_path ()[0 ])
790+             value_properties .add (port .get_property_path ()[0 ])
788791
789792        # If only port value properties have changed, don't trigger unnecessary `port-update` events 
790793        only_value_properties_changed  =  not  (changed_properties  -  value_properties )
791794        if  only_value_properties_changed :
792795            return 
793796
794-         # Trigger `port-update` on all affected ports 
795-         for  port  in  device_ports : 
796-              attrdefs   =   await   port . get_additional_attrdefs () 
797-             attr_properties   =   set ( a [ 'property_path' ][ 0 ]  for   a   in   attrdefs . values ()  if   a . get ( 'property_path' )) 
798-              if   not  ( attr_properties   &   changed_properties ): 
799-                  continue 
800-              port .invalidate_attrs ()
801-              if  port .is_enabled ():
802-                  await  port .trigger_update ()
803-                  port .save_asap ()
797+         attrdefs   =   await   control_port . get_additional_attrdefs () 
798+         attr_properties   =   set ( a [ 'property_path' ][ 0 ]  for  a  in  attrdefs . values ()  if   a . get ( 'property_path' )) 
799+         if   not  ( attr_properties   &   changed_properties ): 
800+             return 
801+ 
802+         # Trigger `port-update` on affected control port 
803+         control_port .invalidate_attrs ()
804+         if  control_port .is_enabled ():
805+             await  control_port .trigger_update ()
806+             control_port .save_asap ()
804807
805808
806809from  .ports  import  PermitJoinPort , DeviceControlPort , DevicePort   # noqa: E402 
0 commit comments