Skip to content

Commit fcf772a

Browse files
committed
Fix Decoder
1 parent 2de3151 commit fcf772a

2 files changed

Lines changed: 114 additions & 1 deletion

File tree

Decoder.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+

app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json, os, csv, uuid, time
44
from github_backup_push import push_to_github
55
import requests
6-
from Decoder import jeedom_decoder
6+
from Decoder import unified_decoder
77
from pytz import timezone
88

99
app = Flask(__name__, static_folder='assets')

0 commit comments

Comments
 (0)