nodes: 0, module; 1, function_definition; 2, function_name:discover; 3, parameters; 4, block; 5, default_parameter; 6, default_parameter; 7, expression_statement; 8, expression_statement; 9, expression_statement; 10, expression_statement; 11, comment:# 'ssdp:all'; 12, expression_statement; 13, expression_statement; 14, for_statement; 15, expression_statement; 16, comment:# only return 'Virtual Media Player' and sort the list; 17, return_statement; 18, identifier:timeout; 19, integer:1; 20, identifier:retries; 21, integer:1; 22, comment:"""Discover Raumfeld devices in the network
  
      :param timeout: The timeout in seconds
      :param retries: How often the search should be retried
      :returns:       A list of raumfeld devices, sorted by name
      """; 23, assignment; 24, assignment; 25, assignment; 26, assignment; 27, call; 28, identifier:_; 29, call; 30, block; 31, assignment; 32, call; 33, identifier:locations; 34, list; 35, identifier:group; 36, tuple; 37, identifier:service; 38, string; 39, identifier:message; 40, call; 41, attribute; 42, argument_list; 43, identifier:range; 44, argument_list; 45, expression_statement; 46, comment:# socket options; 47, expression_statement; 48, expression_statement; 49, comment:# send group multicast; 50, expression_statement; 51, while_statement; 52, identifier:devices; 53, list_comprehension; 54, identifier:sorted; 55, argument_list; 56, string; 57, integer:1900; 58, string_content:ssdp:urn:schemas-upnp-org:device:MediaRenderer:1; 59, attribute; 60, argument_list; 61, identifier:socket; 62, identifier:setdefaulttimeout; 63, identifier:timeout; 64, identifier:retries; 65, assignment; 66, call; 67, call; 68, call; 69, True; 70, block; 71, call; 72, for_in_clause; 73, list_comprehension; 74, keyword_argument; 75, string_content:239.255.255.250; 76, call; 77, identifier:format; 78, keyword_argument; 79, keyword_argument; 80, identifier:sock; 81, call; 82, attribute; 83, argument_list; 84, attribute; 85, argument_list; 86, attribute; 87, argument_list; 88, try_statement; 89, identifier:RaumfeldDevice; 90, argument_list; 91, identifier:location; 92, identifier:locations; 93, identifier:device; 94, for_in_clause; 95, if_clause; 96, identifier:key; 97, lambda; 98, attribute; 99, argument_list; 100, identifier:group; 101, identifier:group; 102, identifier:st; 103, identifier:service; 104, attribute; 105, argument_list; 106, identifier:sock; 107, identifier:setsockopt; 108, attribute; 109, attribute; 110, integer:1; 111, identifier:sock; 112, identifier:setsockopt; 113, attribute; 114, attribute; 115, integer:2; 116, identifier:sock; 117, identifier:sendto; 118, call; 119, identifier:group; 120, block; 121, except_clause; 122, identifier:location; 123, identifier:device; 124, identifier:devices; 125, comparison_operator:device.model_description == 'Virtual Media Player'; 126, lambda_parameters; 127, attribute; 128, string; 129, identifier:join; 130, list; 131, identifier:socket; 132, identifier:socket; 133, attribute; 134, attribute; 135, attribute; 136, identifier:socket; 137, identifier:SOL_SOCKET; 138, identifier:socket; 139, identifier:SO_REUSEADDR; 140, identifier:socket; 141, identifier:IPPROTO_IP; 142, identifier:socket; 143, identifier:IP_MULTICAST_TTL; 144, attribute; 145, argument_list; 146, expression_statement; 147, for_statement; 148, attribute; 149, block; 150, attribute; 151, string; 152, identifier:device; 153, identifier:device; 154, identifier:friendly_name; 155, string_content; 156, string; 157, string; 158, string:'MAN: "ssdp:discover"'; 159, string; 160, string; 161, string; 162, string; 163, identifier:socket; 164, identifier:AF_INET; 165, identifier:socket; 166, identifier:SOCK_DGRAM; 167, identifier:socket; 168, identifier:IPPROTO_UDP; 169, identifier:message; 170, identifier:encode; 171, string; 172, assignment; 173, identifier:line; 174, call; 175, block; 176, identifier:socket; 177, identifier:timeout; 178, break_statement; 179, identifier:device; 180, identifier:model_description; 181, string_content:Virtual Media Player; 182, escape_sequence:\r; 183, escape_sequence:\n; 184, string_content:M-SEARCH * HTTP/1.1; 185, string_content:HOST: {group[0]}:{group[1]}; 186, string_content:ST: {st}; 187, string_content:MX: 1; 188, string_content:utf-8; 189, identifier:response; 190, call; 191, attribute; 192, argument_list; 193, if_statement; 194, attribute; 195, argument_list; 196, identifier:response; 197, identifier:split; 198, string; 199, call; 200, block; 201, call; 202, identifier:decode; 203, string; 204, string_content; 205, attribute; 206, argument_list; 207, expression_statement; 208, if_statement; 209, attribute; 210, argument_list; 211, string_content:utf-8; 212, escape_sequence:\r; 213, escape_sequence:\n; 214, identifier:line; 215, identifier:startswith; 216, string; 217, assignment; 218, not_operator; 219, block; 220, identifier:sock; 221, identifier:recv; 222, integer:2048; 223, string_content:Location:; 224, identifier:location; 225, call; 226, comparison_operator:location in locations; 227, expression_statement; 228, attribute; 229, argument_list; 230, identifier:location; 231, identifier:locations; 232, call; 233, subscript; 234, identifier:strip; 235, attribute; 236, argument_list; 237, call; 238, integer:1; 239, identifier:locations; 240, identifier:append; 241, identifier:location; 242, attribute; 243, argument_list; 244, identifier:line; 245, identifier:split; 246, string; 247, string_content:
code: def discover(timeout=1, retries=1):
      """Discover Raumfeld devices in the network
  
      :param timeout: The timeout in seconds
      :param retries: How often the search should be retried
      :returns:       A list of raumfeld devices, sorted by name
      """
      locations = []
  
      group = ('239.255.255.250', 1900)
      service = 'ssdp:urn:schemas-upnp-org:device:MediaRenderer:1'  # 'ssdp:all'
      message = '\r\n'.join(['M-SEARCH * HTTP/1.1',
                             'HOST: {group[0]}:{group[1]}',
                             'MAN: "ssdp:discover"',
                             'ST: {st}',
                             'MX: 1', '', '']).format(group=group, st=service)
  
      socket.setdefaulttimeout(timeout)
      for _ in range(retries):
          sock = socket.socket(socket.AF_INET,
                               socket.SOCK_DGRAM,
                               socket.IPPROTO_UDP)
          # socket options
          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
          sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
  
          # send group multicast
          sock.sendto(message.encode('utf-8'), group)
  
          while True:
              try:
                  response = sock.recv(2048).decode('utf-8')
                  for line in response.split('\r\n'):
                      if line.startswith('Location: '):
                          location = line.split(' ')[1].strip()
                          if not location in locations:
                              locations.append(location)
              except socket.timeout:
                  break
      devices = [RaumfeldDevice(location) for location in locations]
  
      # only return 'Virtual Media Player' and sort the list
      return sorted([device for device in devices
                     if device.model_description == 'Virtual Media Player'],
                    key=lambda device: device.friendly_name)