Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "Cellular",
"publisher": "Pavel Machek",
"short_description": "Application for placing phone calls",
"long_description": "Simple application for monitoring network state and placing phone calls.",
"icon_url": "https://apps.micropythonos.com/apps/cz.ucw.pavel.cellular/icons/cz.ucw.pavel.cellular_0.0.1_64x64.png",
"download_url": "https://apps.micropythonos.com/apps/cz.ucw.pavel.cellular/mpks/cz.ucw.pavel.cellular_0.0.1.mpk",
"fullname": "cz.ucw.pavel.cellular",
"version": "0.0.1",
"category": "utilities",
"activities": [
{
"entrypoint": "assets/main.py",
"classname": "Main",
"intent_filters": [
{
"action": "main",
"category": "launcher"
}
]
}
]
}

151 changes: 151 additions & 0 deletions internal_filesystem/apps/cz.ucw.pavel.cellular/assets/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
from mpos import Activity

"""
Simple cellular-network example
"""

import time
import os
import json

try:
import lvgl as lv
except ImportError:
pass

from mpos import Activity, MposKeyboard

TMP = "/tmp/cmd.json"


def run_cmd_json(cmd):
rc = os.system(cmd + " > " + TMP)
if rc != 0:
raise RuntimeError("command failed")

with open(TMP, "r") as f:
data = f.read().strip()

return json.loads(data)

def dbus_json(cmd):
return run_cmd_json("sudo /home/mobian/g/MicroPythonOS/internal_filesystem/apps/cz.ucw.pavel.cellular/assets/phone.py " + cmd)

class CellularManager:
def init(self):
v = dbus_json("loc_on")

def poll(self):
v = dbus_json("signal")
print(v)
self.signal = v

def call(self, num):
v = dbus_json("call '%s'" % num)

def sms(self, num, text):
v = dbus_json("call '%s' '%s'" % (num, text))

cm = CellularManager()

# ------------------------------------------------------------
# User interface
# ------------------------------------------------------------

class Main(Activity):

def __init__(self):
super().__init__()

# --------------------

def onCreate(self):
self.screen = lv.obj()
#self.screen.remove_flag(lv.obj.FLAG.SCROLLABLE)

# Top labels
self.lbl_time = lv.label(self.screen)
self.lbl_time.set_style_text_font(lv.font_montserrat_34, 0)
self.lbl_time.set_text("Startup...")
self.lbl_time.align(lv.ALIGN.TOP_LEFT, 6, 22)

self.lbl_date = lv.label(self.screen)
self.lbl_date.set_style_text_font(lv.font_montserrat_20, 0)
self.lbl_date.align_to(self.lbl_time, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 5)
self.lbl_date.set_text("(details here?")

self.lbl_month = lv.label(self.screen)
self.lbl_month.set_style_text_font(lv.font_montserrat_20, 0)
self.lbl_month.align(lv.ALIGN.TOP_RIGHT, -6, 22)

self.number = lv.textarea(self.screen)
#self.number.set_accepted_chars("0123456789")
self.number.set_one_line(True)
self.number.set_style_text_font(lv.font_montserrat_34, 0)
self.number.align_to(self.lbl_date, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 12)

self.call = lv.button(self.screen)
self.call.align_to(self.number, lv.ALIGN.OUT_RIGHT_MID, 2, 0)
self.call.add_event_cb(lambda e: self.on_call(), lv.EVENT.CLICKED, None)

# Two text areas on single screen don't work well.
# Perhaps make it dialog?
#self.sms = lv.textarea(self.screen)
#self.sms.set_style_text_font(lv.font_montserrat_24, 0)
#self.sms.align_to(self.number, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 10)

l = lv.label(self.call)
l.set_text("Call")
l.center()

kb = lv.keyboard(self.screen)
kb.set_textarea(self.number)
kb.set_size(lv.pct(100), lv.pct(33))

self.setContentView(self.screen)
cm.init()

def onResume(self, screen):
self.timer = lv.timer_create(self.tick, 60000, None)
self.tick(0)

def onPause(self, screen):
if self.timer:
self.timer.delete()
self.timer = None

# --------------------

def on_call(self):
num = self.number.get_text()
cm.call(num)

def on_sms(self):
num = self.number.get_text()
text = self.sms.get_text()
cm.sms(num, text)

def tick(self, t):
now = time.localtime()
y, m, d = now[0], now[1], now[2]
hh, mm, ss = now[3], now[4], now[5]

self.lbl_month.set_text("busy")

cm.poll()
s = ""
s += cm.signal["OperatorName"] + "\n"
s += "RegistrationState %d\n" % cm.signal["RegistrationState"]
s += "State %d " % cm.signal["State"]
sq, re = cm.signal["SignalQuality"]
s += "Signal %d\n" % sq

self.lbl_month.set_text(s)
self.lbl_time.set_text("%02d:%02d" % (hh, mm))
s = ""
self.lbl_date.set_text("%04d-%02d-%02d %s" % (y, m, d, s))


# --------------------


167 changes: 167 additions & 0 deletions internal_filesystem/apps/cz.ucw.pavel.cellular/assets/phone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env python3
from pydbus import SystemBus, Variant
import pydbus
import time
import sys
import json

"""
Lets make it class Phone, one method would be reading battery information, one would be reading operator name / signal strength, one would be getting wifi enabled/disabled / AP name.

sudo apt install python3-pydbus

sudo mmcli --list-modems
sudo mmcli -m 6 --location-enable-gps-nmea --location-enable-gps-raw
"""



class Phone:
verbose = False

def __init__(self):
self.bus = pydbus.SystemBus()

def init_sess(self):
self.sess = pydbus.SessionBus()

def get_mobile_loc(self):
loc = None
mm = self.bus.get("org.freedesktop.ModemManager1")
for modem_path in mm.GetManagedObjects():
modem = self.bus.get(".ModemManager1", modem_path)
loc = modem.GetLocation()
return loc

def get_cell_signal(self):
loc = None
mm = self.bus.get("org.freedesktop.ModemManager1")
for modem_path in mm.GetManagedObjects():
modem = self.bus.get(".ModemManager1", modem_path)

loc = {}

def attr(v):
loc[v] = getattr(modem, v, None)

attr("OperatorName")
attr("OperatorCode") # 0..11 according to MMState
attr("State") # 0..11 according to MMState
attr("AccessTechnologies")
attr("Model")
attr("Manufacturer")
attr("Revision")
attr("EquipmentIdentifier")

attr("Gsm")
attr("Umts")
attr("Lte")

attr("SignalQuality")
attr("RegistrationState")

return loc

def start_call(self, num):
mm = self.bus.get("org.freedesktop.ModemManager1")

for modem_path in mm.GetManagedObjects():
modem = self.bus.get("org.freedesktop.ModemManager1", modem_path)
voice = modem["org.freedesktop.ModemManager1.Modem.Voice"]

call_properties = {
"number": Variant('s', num)
}

call_path = voice.CreateCall(call_properties)
#call = self.bus.get("org.freedesktop.ModemManager1", call_path)
#call_iface = call["org.freedesktop.ModemManager1.Call"]
#call_iface.Start()

return { "call": call_path }

def send_sms(self, num, text):
mm = self.bus.get("org.freedesktop.ModemManager1")

for modem_path in mm.GetManagedObjects():
modem = self.bus.get("org.freedesktop.ModemManager1", modem_path)
messaging = modem["org.freedesktop.ModemManager1.Modem.Messaging"]

sms_properties = {
"number": Variant('s', num),
"text": Variant('s', text)
}

sms_path = messaging.Create(sms_properties)
sms = self.bus.get("org.freedesktop.ModemManager1", sms_path)
sms_iface = sms["org.freedesktop.ModemManager1.Sms"]
sms_iface.Send()

return { "sms": sms_path }

# 0x01 = 3GPP LAC/CI
# 0x02 = GPS NMEA
# 0x04 = GPS RAW
# 0x08 = CDMA BS
# 0x10 = GPS Unmanaged
CELL_ID = 0x01
GPS_NMEA = 0x02
GPS_RAW = 0x04

def enable_mobile_loc(self, gps_on, cell_on):
"""
Enable GPS RAW + NMEA.
"""
mm = self.bus.get("org.freedesktop.ModemManager1")
for modem_path in mm.GetManagedObjects():
modem = self.bus.get(".ModemManager1", modem_path)

# Setup(uint32 sources, boolean signal_location)
# signal_location=True makes ModemManager emit LocationUpdated signals
if gps_on:
sources = self.GPS_NMEA | self.GPS_RAW
else:
sources = 0
if cell_on:
sources |= self.CELL_ID;
modem.Setup(sources, True)

continue
# Optional: explicitly enable (some modems require it)
try:
modem.SetEnable(True)
except Exception:
print("Cant setenable")
return { 'result' : 'setenable failed' }
return { 'result': 'ok' }

phone = Phone()

def handle_cmd(v, a):
if v == "bat":
print(json.dumps(phone.get_battery_info()))
sys.exit(0)
if v == "loc":
print(json.dumps(phone.get_mobile_loc()))
sys.exit(0)
if v == "loc_on":
print(json.dumps(phone.enable_mobile_loc(True, True)))
sys.exit(0)
if v == "loc_off":
print(json.dumps(phone.enable_mobile_loc(False, False)))
sys.exit(0)
if v == "signal":
print(json.dumps(phone.get_cell_signal()))
sys.exit(0)
if v == "call":
print(json.dumps(phone.start_call(a[2])))
sys.exit(0)
if v == "sms":
print(json.dumps(phone.send_sms(a[2], a[3])))
sys.exit(0)
print("Unknown command "+v)
sys.exit(1)

if len(sys.argv) > 1:
handle_cmd(sys.argv[1], sys.argv)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading