openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

146 lines
4.3 KiB

#!/usr/bin/env python3
import json
import os
import subprocess
import time
from dataclasses import dataclass
@dataclass
class Profile:
iccid: str
isdp_aid: str
nickname: str
enabled: bool
provider: str
class LPAError(Exception):
pass
class LPA:
def __init__(self):
self.env = os.environ.copy()
self.env['LPAC_APDU'] = 'qmi'
self.env['QMI_DEVICE'] = '/dev/cdc-wdm0'
self.timeout_sec = 30
def list_profiles(self):
"""
List all profiles on the eUICC.
"""
raw = self._invoke('profile', 'list')[-1] # only one message
profiles = []
for profile in raw['payload']['data']:
profiles.append(Profile(
iccid=profile['iccid'],
isdp_aid=profile['isdpAid'],
nickname=profile['profileNickname'],
enabled=profile['profileState'] == 'enabled',
provider=profile['serviceProviderName'],
))
return profiles
def get_active_profile(self):
"""
Get the active profile on the eUICC.
"""
profiles = self.list_profiles()
for profile in profiles:
if profile.enabled:
return profile
return None
def process_notifications(self) -> None:
"""
Process notifications from the LPA, typically to activate/deactivate the profile with the carrier.
"""
msgs = self._invoke('notification', 'process', '-a', '-r')
assert msgs[-1]['payload']['message'] == 'success', 'expected success notification'
def enable_profile(self, iccid: str) -> None:
"""
Enable the profile on the eUICC.
"""
latest = self.get_active_profile()
if latest is None:
raise LPAError('no profile enabled')
if latest.iccid == iccid:
raise LPAError(f'profile {iccid} is already enabled')
else:
self.disable_profile(latest.iccid)
msgs = self._invoke('profile', 'enable', iccid)
assert msgs[-1]['payload']['message'] == 'success', 'expected success notification'
self.process_notifications()
def disable_profile(self, iccid: str) -> None:
"""
Disable the profile on the eUICC.
"""
latest = self.get_active_profile()
if latest is None:
raise LPAError('no profile enabled')
if latest.iccid != iccid:
raise LPAError(f'profile {iccid} is not enabled')
msgs = self._invoke('profile', 'disable', iccid)
assert msgs[-1]['payload']['message'] == 'success', 'expected success notification'
self.process_notifications()
def _invoke(self, *cmd: str):
print(f"invoking lpac {' '.join(list(cmd))}")
ret = subprocess.Popen(['sudo', '-E', 'lpac'] + list(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env)
try:
out, err = ret.communicate(timeout=self.timeout_sec)
except subprocess.TimeoutExpired as e:
ret.kill()
raise LPAError(f"lpac {cmd} timed out after {self.timeout_sec} seconds") from e
messages = []
for line in out.decode().strip().splitlines():
if line.startswith('{'):
message = json.loads(line)
# lpac response format validations
assert 'type' in message, 'expected type in message'
assert message['type'] == 'lpa' or message['type'] == 'progress', 'expected lpa or progress message type'
assert 'payload' in message, 'expected payload in message'
assert 'code' in message['payload'], 'expected code in message payload'
if message['payload']['code'] != 0:
raise LPAError(f"lpac {' '.join(cmd)} failed with code {message['payload']['code']}: <{message['payload']['message']}> {message['payload']['data']}")
assert 'data' in message['payload'], 'expected data in message payload'
messages.append(message)
if len(messages) == 0:
raise LPAError(f"lpac {cmd} returned no messages")
return messages
if __name__ == "__main__":
import sys
lpa = LPA()
print(lpa.list_profiles())
if len(sys.argv) > 2:
if sys.argv[1] == 'enable':
lpa.enable_profile(sys.argv[2])
elif sys.argv[1] == 'disable':
lpa.disable_profile(sys.argv[2])
else:
raise Exception(f"invalid command: {sys.argv[1]}")
if "RESTART" in os.environ:
subprocess.check_call("sudo systemctl stop ModemManager", shell=True)
subprocess.check_call("/usr/comma/lte/lte.sh stop_blocking", shell=True)
subprocess.check_call("/usr/comma/lte/lte.sh start", shell=True)
while not os.path.exists('/dev/ttyUSB2'):
time.sleep(1)