Skip to content

Commit

Permalink
Add custom fixed BLE binaries
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexxIT committed Mar 8, 2021
1 parent 94b6241 commit 8dbbed4
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 15 deletions.
60 changes: 52 additions & 8 deletions custom_components/xiaomi_gateway3/core/gateway3.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def process_zb_stats(self, payload: dict):
self.get_gateway_info()

def process_ble_stats(self, payload: dict):
did = payload['dev']['did']
did = payload['dev']['did'] if 'dev' in payload else payload['did']
if did in self.stats:
self.stats[did](payload)

Expand Down Expand Up @@ -471,11 +471,30 @@ def _prepare_gateway(self, with_devices: bool = False):
# run NTPd for sync time
shell.run_ntpd()

# all data or only necessary events
pattern = '\\{"' if 'miio' in self._debug \
else "ble_event|properties_changed|heartbeat"

if f"awk /{pattern} {{" not in ps:
bt_fix = shell.check_bt()
if bt_fix is None:
self.debug("Fixed BT don't supported")

elif bt_fix is False:
self.debug("Download fixed BT")
shell.download_bt()

# check after download
if shell.check_bt():
self.debug("Run fixed BT")
shell.run_bt()

elif "log/ble" not in ps:
self.debug("Run fixed BT")
shell.run_bt()

if "log/miio" not in ps:
# all data or only necessary events
pattern = (
'\\{"' if 'miio' in self._debug else
"ot_agent_recv_handler_one.+"
"ble_event|properties_changed|heartbeat"
)
self.debug(f"Redirect miio to MQTT")
shell.redirect_miio2mqtt(pattern)

Expand Down Expand Up @@ -744,6 +763,10 @@ def on_message(self, client: Client, userdata, msg: MQTTMessage):
# time offset may changed right after gw.heartbeat
self.update_time_offset()

elif topic == 'log/ble':
payload = json.loads(msg.payload)
self.process_ble_event_fix(payload)

elif topic == 'log/z3':
self.process_z3(msg.payload.decode())

Expand Down Expand Up @@ -963,6 +986,10 @@ def process_ble_event(self, data: dict):
else:
device = self.devices[did]

if device.get('seq') == data['frmCnt']:
return
device['seq'] = data['frmCnt']

if isinstance(data['evt'], list):
# check if only one
assert len(data['evt']) == 1, data
Expand All @@ -972,10 +999,27 @@ def process_ble_event(self, data: dict):
else:
payload = None

if payload is None:
self.debug(f"Unsupported BLE {data}")
self._process_ble_event(device, payload)

def process_ble_event_fix(self, data: dict):
self.debug(f"Process BLE Fix {data}")

did = data['did']
if did not in self.devices:
self.debug(f"Unregistered BLE device {did}")
return

device = self.devices[did]
if device.get('seq') == data['seq']:
return
device['seq'] = data['seq']

payload = bluetooth.parse_xiaomi_ble(data, data['pdid'])
self._process_ble_event(device, payload)

def _process_ble_event(self, device: dict, payload: dict):
did = device['did']

# init entities if needed
init = device['init']
for k in payload.keys():
Expand Down
42 changes: 35 additions & 7 deletions custom_components/xiaomi_gateway3/core/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
# We should use HTTP-link because wget don't support HTTPS and curl removed in
# lastest fw. But it's not a problem because we check md5

CHECK_SOCAT = "(md5sum /data/socat | grep 92b77e1a93c4f4377b4b751a5390d979)"
# original link http://pkg.musl.cc/socat/mipsel-linux-musln32/bin/socat
DOWNLOAD_SOCAT = "(wget -O /data/socat http://pkg.simple-ha.ru/mipsel/socat && chmod +x /data/socat)"
# original link https://busybox.net/downloads/binaries/1.21.1/busybox-mipsel
DOWNLOAD = "(wget -O /data/{0} http://master.dl.sourceforge.net/project/mgl03/{1}/{0}?viasf=1 && chmod +x /data/{0})"

CHECK_SOCAT = "(md5sum /data/socat | grep 92b77e1a93c4f4377b4b751a5390d979)"
RUN_SOCAT = "/data/socat tcp-l:8888,reuseaddr,fork /dev/ttyS2"

CHECK_BUSYBOX = "(md5sum /data/busybox | grep 099137899ece96f311ac5ab554ea6fec)"
# original link https://busybox.net/downloads/binaries/1.21.1/busybox-mipsel
DOWNLOAD_BUSYBOX = "(wget -O /data/busybox http://pkg.simple-ha.ru/mipsel/busybox && chmod +x /data/busybox)"
LOCK_FIRMWARE = "/data/busybox chattr +i"
UNLOCK_FIRMWARE = "/data/busybox chattr -i"
RUN_FTP = "(/data/busybox tcpsvd -vE 0.0.0.0 21 /data/busybox ftpd -w &)"
Expand All @@ -32,6 +32,13 @@

FIRMWARE_PATHS = ('/data/firmware.bin', '/data/firmware/firmware_ota.bin')

BT_MD5 = {
'1.4.6_0012': '367bf0045d00c28f6bff8d4132b883de',
'1.4.6_0043': 'c4fa99797438f21d0ae4a6c855b720d2',
'1.4.7_0115': 'be4724fbc5223fcde60aff7f58ffea28',
'1.4.7_0160': '9290241cd9f1892d2ba84074f07391d4',
}


class TelnetShell(Telnet):
def __init__(self, host: str):
Expand All @@ -49,7 +56,8 @@ def exec(self, command: str, as_bytes=False) -> Union[str, bytes]:

def check_or_download_socat(self):
"""Download socat if needed."""
return self.exec(f"{CHECK_SOCAT} || {DOWNLOAD_SOCAT}")
download = DOWNLOAD.format('socat', 'bin')
return self.exec(f"{CHECK_SOCAT} || {download}")

def run_socat(self):
self.exec(f"{CHECK_SOCAT} && {RUN_SOCAT} &")
Expand All @@ -64,7 +72,27 @@ def stop_lumi_zigbee(self):
self.exec("killall daemon_app.sh Lumi_Z3GatewayHost_MQTT")

def check_or_download_busybox(self):
return self.exec(f"{CHECK_BUSYBOX} || {DOWNLOAD_BUSYBOX}")
download = DOWNLOAD.format('busybox', 'bin')
return self.exec(f"{CHECK_BUSYBOX} || {download}")

def check_bt(self):
md5 = BT_MD5.get(self.ver)
if not md5:
return None
return md5 in self.exec("md5sum /data/silabs_ncp_bt")

def download_bt(self):
self.exec("rm /data/silabs_ncp_bt")
md5 = BT_MD5.get(self.ver)
# we use same name for bt utis so gw can kill it in case of update etc.
self.exec(DOWNLOAD.format('silabs_ncp_bt', md5))

def run_bt(self):
self.exec(
"killall silabs_ncp_bt; pkill -f log/ble; "
"/data/silabs_ncp_bt /dev/ttyS1 1 2>&1 >/dev/null | "
"mosquitto_pub -t log/ble -l &"
)

def check_firmware_lock(self) -> bool:
"""Check if firmware update locked. And create empty file if needed."""
Expand Down Expand Up @@ -99,7 +127,7 @@ def run_ntpd(self):
self.exec("ntpd -l")

def get_running_ps(self) -> str:
return self.exec("ps")
return self.exec("ps -w")

def redirect_miio2mqtt(self, pattern: str):
self.exec("killall daemon_miio.sh miio_client; pkill -f log/miio")
Expand Down

0 comments on commit 8dbbed4

Please sign in to comment.