27
27
MESSAGE_TYPE_ALL_LINK_CLEANUP ,
28
28
MESSAGE_FLAG_DIRECT_MESSAGE_NAK_0XA0 ,
29
29
MESSAGE_STANDARD_MESSAGE_RECEIVED_0X50 ,
30
- MESSAGE_EXTENDED_MESSAGE_RECEIVED_0X51
31
- )
30
+ MESSAGE_EXTENDED_MESSAGE_RECEIVED_0X51 ,
31
+ MESSAGE_TYPE_ALL_LINK_BROADCAST )
32
32
from insteonplm .messagecallback import MessageCallback
33
33
from insteonplm .messages .allLinkComplete import AllLinkComplete
34
34
from insteonplm .messages .extendedReceive import ExtendedReceive
43
43
DIRECT_ACK_WAIT_TIMEOUT = 3
44
44
ALDB_RECORD_TIMEOUT = 10
45
45
ALDB_RECORD_RETRIES = 20
46
- ALDB_ALL_RECORD_TIMEOUT = 30
46
+ ALDB_ALL_RECORD_TIMEOUT = 120
47
47
ALDB_ALL_RECORD_RETRIES = 5
48
48
49
49
@@ -215,9 +215,9 @@ def ALL_Link_cleanup(self, group, cmd_tuple):
215
215
self .address .human , group )
216
216
flags = MessageFlags .template (MESSAGE_TYPE_ALL_LINK_CLEANUP , 0 , 3 , 3 )
217
217
msg = StandardSend (self ._address , cmd_tuple , cmd2 = group , flags = flags )
218
- self ._send_msg (msg , self ._handle_ALL_Link_cleanup_ack )
218
+ self ._send_msg (msg , self ._handle_ALL_Link_cleanup )
219
219
220
- def _handle_ALL_Link_cleanup_ack (self , msg ):
220
+ def _handle_ALL_Link_cleanup (self , msg ):
221
221
_LOGGER .debug ('Received ALL-Link Cleanup ACK from %s; ALL-Link group '
222
222
'0x%x; looking for responder states' ,
223
223
msg .address .human , msg .cmd2 )
@@ -226,16 +226,10 @@ def _handle_ALL_Link_cleanup_ack(self, msg):
226
226
responders = self ._find_group_responder_states (msg .target , msg .cmd2 )
227
227
228
228
for responder in responders :
229
- _LOGGER .debug ('Calling %s:0x%x:handle_ALL_Link_cleanup_ack ' ,
229
+ _LOGGER .debug ('Calling %s:0x%02x:handle_ALL_Link_cleanup ' ,
230
230
self ._address .human , responder )
231
- if hasattr (self .states [responder ], 'handle_ALL_Link_cleanup_ack' ):
232
- self .states [responder ].handle_ALL_Link_cleanup_ack (
233
- msg , responders [responder ])
234
- else :
235
- _LOGGER .warning ('Device %s:0x%x has no '
236
- 'handle_ALL_Link_cleanup_ack() method. '
237
- 'Cannot perform ALL-Link cleanup' ,
238
- self ._address .human , responder )
231
+ self .states [responder ].handle_ALL_Link_cleanup (
232
+ msg , responders [responder ])
239
233
240
234
def _find_group_responder_states (self , ctl , ctl_group ):
241
235
_LOGGER .debug ('Looking for responder to controller %s:0x%x' ,
@@ -729,11 +723,21 @@ def _register_messages(self):
729
723
self ._message_callbacks .add (template_all_link_complete ,
730
724
self ._handle_all_link_complete )
731
725
726
+ template_All_Link_broadcast = StandardReceive .template (
727
+ flags = MessageFlags .template (MESSAGE_TYPE_ALL_LINK_CLEANUP , None ))
728
+ self ._message_callbacks .add (template_All_Link_broadcast ,
729
+ self ._handle_All_Link_broadcast )
730
+
731
+ template_All_Link_cleanup = StandardReceive .template (
732
+ flags = MessageFlags .template (MESSAGE_TYPE_ALL_LINK_BROADCAST , None ))
733
+ self ._message_callbacks .add (template_All_Link_cleanup ,
734
+ self ._handle_All_Link_broadcast )
735
+
732
736
# Send / Receive message processing
733
737
def receive_message (self , msg ):
734
738
"""Receive a messages sent to this device."""
735
739
_LOGGER .debug ('Starting Device.receive_message for %s' ,
736
- msg .address .human )
740
+ self .address .human )
737
741
if hasattr (msg , 'isack' ) and msg .isack :
738
742
_LOGGER .debug ('Got Message ACK %s' , id (msg ))
739
743
if self ._sent_msg_wait_for_directACK .get ('callback' ) is not None :
@@ -800,8 +804,24 @@ def _is_duplicate(self, msg):
800
804
if msg .matches_pattern (prev_msg ):
801
805
ret_val = True
802
806
807
+ # Add current message to recent list
803
808
self ._recent_messages .put_nowait (
804
809
{"msg" : msg , "received" : datetime .datetime .now ()})
810
+
811
+ # If an ALL-Link Broadcast was successfully received the ALL-Link
812
+ # cleanup should be considered a duplicate if not a 0x06 status report
813
+ if (msg .flags .isAllLinkBroadcast and msg .cmd1 != MESSAGE_ACK ):
814
+ # Fabricate duplicate all-link cleanup
815
+ dup_target = self ._plm .address
816
+ dup_group = msg .targetHi
817
+ dup_flags = MessageFlags .template (MESSAGE_TYPE_ALL_LINK_CLEANUP ,
818
+ 0 , 3 , 3 )
819
+ dup_msg = StandardReceive (msg .address , dup_target ,
820
+ {'cmd1' : msg .cmd1 , 'cmd2' : dup_group },
821
+ flags = dup_flags )
822
+ self ._recent_messages .put_nowait (
823
+ {"msg" : dup_msg , "received" : datetime .datetime .now ()})
824
+
805
825
return ret_val
806
826
807
827
def _send_msg (self , msg , callback = None , on_timeout = False ):
@@ -870,6 +890,88 @@ async def _wait_for_direct_ACK(self):
870
890
def _aldb_loaded_callback (self ):
871
891
self ._plm .devices .save_device_info ()
872
892
893
+ def _handle_All_Link_broadcast (self , msg ):
894
+ """Process ALL-Link broadcast or cleanup received from devices.
895
+
896
+ Parameters:
897
+ msg.address: controller's address
898
+ msg.targetHi: group triggered on controller for broadcast msg
899
+ msg.cmd2: group triggers on controller for clean up msg
900
+
901
+ Searches for controller's address and group triggered in this devices
902
+ ALDB to find this devices responding group.
903
+
904
+ """
905
+ if msg .cmd1 != MESSAGE_ACK : # Ignore 0x06 status reports
906
+
907
+ # Don't bother searching the ALDB if there isn't at least one
908
+ # state that can respond to controllers
909
+ responder = False
910
+ for state in self ._stateList :
911
+ if self ._stateList [state ].is_responder :
912
+ responder = True
913
+ break
914
+
915
+ if responder :
916
+ ctl_group = msg .targetHi
917
+ _LOGGER .debug ('_handle_All_Link_broadcast msg: %s for '
918
+ 'device %s' , id (msg ), self .address .human )
919
+ if not ctl_group :
920
+ # Not a broadcast message so must be a cleanup
921
+ ctl_group = msg .cmd2
922
+
923
+ resp_groups = self ._find_responder_groups (msg .address ,
924
+ ctl_group )
925
+ for resp_group in resp_groups :
926
+ if self ._stateList [resp_group ]:
927
+ self ._stateList [resp_group ].handle_ALL_Link_cleanup (
928
+ msg , resp_groups [resp_group ])
929
+ else :
930
+ _LOGGER .warning ('ALDB Responder record maps to '
931
+ 'nonexistent state' )
932
+
933
+ def _find_responder_groups (self , ctl_address , ctl_group ):
934
+ """Identify which PLM group responds to ctl / ctl_group.
935
+
936
+ Parameters:
937
+ ctl_address: address object of controller that sent ALL-Link command
938
+ ctl_group: group that was triggered on the controller
939
+
940
+ Returns:
941
+ dictionary of all responding groups on this device
942
+ key: responding group on this device from data3
943
+ value: recall_level from data1
944
+
945
+ NOTE: The IM class overrides this method for the PLM's underlying
946
+ device object.
947
+
948
+ """
949
+ _LOGGER .debug ('_find_responder_groups: looking for ctl: %s, '
950
+ 'ctl_group: 0x%02x on device %s' , ctl_address .human ,
951
+ ctl_group , self .address .human )
952
+ groups = {}
953
+ if self ._aldb :
954
+ for rec_num in self ._aldb :
955
+ rec = self ._aldb [rec_num ]
956
+ if (rec .control_flags .is_responder and
957
+ rec .group == ctl_group and
958
+ rec .address == ctl_address ):
959
+ # Recall level is stored in data1
960
+ # This device's group is stored in data3
961
+ data3 = rec .data3 if rec .data3 != 0 else 1
962
+ groups [data3 ] = rec .data1
963
+ _LOGGER .debug ('Found %d responders on device %s' , len (groups ),
964
+ self .address .human )
965
+ else :
966
+ if self ._aldb .status == ALDBStatus .LOADED :
967
+ _LOGGER .warning ('No responders found; ALDB is empty %s' ,
968
+ self .address .human )
969
+ else :
970
+ _LOGGER .warning ('No responders found; ALDB is not loaded %s' ,
971
+ self .address .human )
972
+
973
+ return groups
974
+
873
975
874
976
# pylint: disable=too-many-instance-attributes
875
977
class X10Device ():
0 commit comments