@@ -19241,5 +19241,118 @@ def bm_st_sz(st):
1924119241
1924219242 def to_float (number ):
1924319243
19244+
1924419245 return struct .unpack (">f" , number .to_bytes (4 , "big" ))[0 ]
1924519246
19247+
19248+ # --- Unified Decoder (Re-injected & Renamed) ---
19249+ class UnifiedDecoder :
19250+ def __init__ (self ):
19251+ self .drivers = []
19252+ self .analysis = {}
19253+
19254+ # Load Analysis
19255+ try :
19256+ with open ('deveui_analysis.json' , 'r' ) as f :
19257+ self .analysis = json .load (f )
19258+ except : pass
19259+
19260+ # Load Drivers from globals.COMPATIBILITY
19261+ seen = set ()
19262+ for cls in globals .COMPATIBILITY :
19263+ if cls not in seen :
19264+ try :
19265+ inst = cls ()
19266+ self .drivers .append (inst )
19267+ seen .add (cls )
19268+ except Exception as e :
19269+ logging .debug (f"Failed to instantiate { cls } : { e } " )
19270+
19271+ def identify_manufacturer (self , deveui ):
19272+ if not deveui : return None
19273+ deveui = deveui .upper ().replace (':' , '' ).replace ('-' , '' )
19274+
19275+ # 1. Look in analysis specific DEVEUI
19276+ if "manufacturers" in self .analysis :
19277+ if deveui in self .analysis ["manufacturers" ]:
19278+ return self .analysis ["manufacturers" ][deveui ]
19279+
19280+ # 2. Look in analysis prefixes
19281+ best_match = None
19282+ best_len = 0
19283+ prefixes = self .analysis .get ("manufacturers_by_prefix" , {})
19284+ for prefix , manuf in prefixes .items ():
19285+ if deveui .startswith (prefix ):
19286+ if len (prefix ) > best_len :
19287+ best_len = len (prefix )
19288+ best_match = manuf
19289+
19290+ if best_match : return best_match
19291+
19292+ # 3. Hardcoded Fallbacks
19293+ if deveui .startswith ("A84041" ): return "dragino"
19294+ if deveui .startswith ("24E124" ): return "milesight"
19295+ if deveui .startswith ("70B3D5E7" ): return "watteco"
19296+ if deveui .startswith ("70B3D52D" ): return "mclimate"
19297+
19298+ return None
19299+
19300+ def decode_uplink (self , encoded_data , deveui = None ):
19301+ # Handle Hex or Base64 or List
19302+ raw_payload = ""
19303+ if isinstance (encoded_data , list ):
19304+ raw_payload = "" .join ([f"{ x :02X} " for x in encoded_data ])
19305+ elif isinstance (encoded_data , str ):
19306+ try :
19307+ # Try base64 first
19308+ b = base64 .b64decode (encoded_data )
19309+ raw_payload = b .hex ().upper ()
19310+ except :
19311+ if len (encoded_data ) % 2 == 0 :
19312+ raw_payload = encoded_data .upper ()
19313+ else :
19314+ return {"error" : "Invalid format" , "raw" : encoded_data }
19315+
19316+ data_wrapper = {"payload" : raw_payload }
19317+
19318+ manuf = self .identify_manufacturer (deveui )
19319+
19320+ potential_drivers = self .drivers
19321+ if manuf :
19322+ # Sort to prioritize manufacturer in driver name
19323+ potential_drivers = sorted (self .drivers , key = lambda d : 0 if manuf .lower () in d .name .lower () else 1 )
19324+
19325+ # Try finding a driver
19326+ for drv in potential_drivers :
19327+ try :
19328+ d_in = data_wrapper .copy ()
19329+ res = drv .parse (d_in , None )
19330+ if res and res .get ('parsed' ):
19331+ if len (res ['parsed' ]) > 0 :
19332+ res ['parsed' ]['_driver' ] = drv .name
19333+ return res ['parsed' ]
19334+ except Exception : pass
19335+
19336+ # Fallback for NKE / Nexelec A3... format (common if no devEUI match)
19337+ # Payload starting with A3/A4/A9 often NKE-like
19338+ if raw_payload .upper ().startswith ('A3' ) or raw_payload .upper ().startswith ('A9' ):
19339+ for d in self .drivers :
19340+ if 'nexelec' in d .name .lower () or 'atmo' in d .name .lower ():
19341+ try :
19342+ res = d .parse ({"payload" : raw_payload }, None )
19343+ if res and res .get ('parsed' ): return res ['parsed' ]
19344+ except : pass
19345+
19346+ return {"error" : "No driver found" , "raw" : raw_payload , "info" : f"Manuf: { manuf } " }
19347+
19348+ # Instantiate Global
19349+ try :
19350+ unified_decoder = UnifiedDecoder ()
19351+ except Exception as e :
19352+ logging .error (f"Failed to init UnifiedDecoder: { e } " )
19353+ # Mock
19354+ class MockUni :
19355+ def decode_uplink (self , * args ): return {"error" : "Decoder Init Failed" }
19356+ unified_decoder = MockUni ()
19357+
19358+
0 commit comments