Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Ofono bridge #83

Open
wants to merge 45 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8b385ae
adds a basic `OfonoBridge` class to interact with Ofono through D-Bus
monsieurh Dec 20, 2017
e1a63fb
#50 restart ofono-bridge development
monsieurh Jan 7, 2018
f43ea67
#50 adds udev rule for SIM800L
monsieurh Jan 13, 2018
1994095
#50 WIP ofono-bridge
monsieurh Jan 13, 2018
0413ddf
Merge remote-tracking branch 'origin/devel' into ofono_bridge
monsieurh Jan 13, 2018
ff3d603
fixes duplicate entry in requirements.txt
monsieurh Jan 13, 2018
b5d29b0
adds some safeguards
monsieurh Jan 13, 2018
67c2baa
fixes logger import
monsieurh Jan 15, 2018
0966d0e
refactors '/sim900_0' as a member variable
monsieurh Jan 15, 2018
12d27f9
refactors the modem check as a method `_check_default_modem`
monsieurh Jan 15, 2018
41ff44c
updates `self._bus` hack explanation
monsieurh Jan 15, 2018
e1f101b
remove development leftover
monsieurh Jan 15, 2018
f085e7b
better exception handling
monsieurh Jan 15, 2018
2a084a6
replaces `print` by `logger.info`
monsieurh Jan 15, 2018
08dc9e8
now raises an exception when an interface is not found
monsieurh Jan 15, 2018
da2fb47
`ConversationManager` now recursively create log folder
monsieurh Jan 15, 2018
1650127
Merge branch 'devel' into ofono_bridge
CRImier Jun 15, 2018
0cf96f2
Adding a default log level - fixing tests
CRImier Jun 15, 2018
8e50fd7
Movong the ofono libraries into a separate library
CRImier Jun 15, 2018
01e72d3
WIP, debugging ofono
CRImier Jun 16, 2018
1b5c9e5
Latest screenshot are now shown first in screenshot listing app
CRImier Jun 17, 2018
248a30a
Merge branch 'devel' into ofono_bridge
CRImier Dec 19, 2018
54d548f
Thinking about architecture - initial changes
CRImier Dec 21, 2018
49a1ee1
Merge branch 'devel' into ofono_bridge
CRImier Jan 5, 2019
becaef5
Adding the BaseViewMixin, porting UI elements to use it
CRImier Jan 6, 2019
85e74ce
Fixing method's return type in declaration
CRImier Jan 6, 2019
2a40a20
Number input, overlays and value pagination work
CRImier Jan 6, 2019
47350dc
Adding BaseView class and porting everything to it, porting CharArrow…
CRImier Jan 6, 2019
917f7f6
Ported InputScreenView to use BaseView
CRImier Jan 6, 2019
44d6f44
Forgot a small thing
CRImier Jan 6, 2019
b04f185
Broke the launch process, fixing
CRImier Jan 6, 2019
9887af1
moving BaseViewMixin.__init__ - making it possible for the view to pr…
CRImier Jan 6, 2019
85cf72c
More testing and optimizations in the dialer
CRImier Jan 6, 2019
377c146
Adding BaseView functions for the usual character refresh, porting In…
CRImier Jan 6, 2019
1d29e7f
Moving overlays to use update_keymap
CRImier Jan 6, 2019
f92f021
Updating the overlay options for better button usage
CRImier Jan 6, 2019
378593e
Adding a bunch of functions to work with coordinates, adding tests, m…
CRImier Jan 6, 2019
c787dc3
Calculate FunctionOverlay labels depending on len of labels and not n…
CRImier Jan 6, 2019
ec17696
Numpad*Input now backgroundable
CRImier Jan 6, 2019
886a243
More phone app work - status screen ready
CRImier Jan 6, 2019
801fb09
Forgot to remove outdated test, adding
CRImier Jan 6, 2019
3a3500b
Making the Numpad*Input use the keymap for action buttons (as it shou…
CRImier Jan 6, 2019
65e450e
Accidentally changed context names, fixing the hardcoded exclusive co…
CRImier Jan 7, 2019
bb9e456
Adding "start context" function - for custom apps
CRImier Jan 7, 2019
5af4c3c
Merge branch 'devel' into ofono_bridge
CRImier May 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 00-ofono.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KERNEL=="ttyAMA0", ENV{OFONO_DRIVER}="sim900"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll be shipping this file in our own Debian package, most likely


11 changes: 8 additions & 3 deletions apps/personal/contacts/vcard_converter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from helpers import setup_logger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, why? I think you need to revert this particular change, which reverses our "setup_logger" integration work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(as in, revert the logger-> logging replacements, not the from_string addition)

logger = setup_logger(__name__, "warning")
import logging

import vobject

Expand Down Expand Up @@ -50,5 +49,11 @@ def from_vcards(contact_card_files):
contacts = []
for file_path in contact_card_files:
contacts += VCardContactConverter.parse_vcard_file(file_path)
logger.info("finished : {} contacts loaded", len(contacts))
logging.info("finished : {} contacts loaded", len(contacts))
return [VCardContactConverter.to_zpui_contact(c) for c in contacts]

@classmethod
def from_string(cls, vcard_string):
# type: (str) -> list
# Returns a list of ZPUI contacts from a string in vcard format
return [c for c in vobject.readComponents(vcard_string, ignoreUnreadable=True)]
Empty file added ofono/__init__.py
Empty file.
135 changes: 135 additions & 0 deletions ofono/bridge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import logging
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary import

import os
from time import sleep

import pydbus
from dbus.mainloop.glib import DBusGMainLoop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dbus module is not available on a ZeroPhone, even after pydbus is installed

from gi.repository import GLib
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for gi module

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears the gi module is easily installable once I do apt install python-gi. So, it appears, we really do need the "zerophone-essentials" virtual package =)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package created (zerophone-requirements)


from helpers import Singleton, setup_logger

logger = setup_logger(__name__, 'debug')
logging.basicConfig(level=logging.DEBUG)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused statement - we already have setup_logger



class OfonoBridge(object):
"""
Generic util class to bridge between ZPUI and ofono backend through D-Bus
"""

def __init__(self):
super(OfonoBridge, self).__init__()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, it doesn't inherit from anything =D

Copy link
Collaborator Author

@monsieurh monsieurh Jan 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... yet :), i'll remove it anyway, thanks


bus = pydbus.SystemBus()
manager = bus.get('org.ofono', '/')
modem_path = manager.GetModems()[0][0]

if modem_path != '/sim900_0':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to have the sim900 modem as the default modem? As in, what's wrong if it's not?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a check because if you run ofono with some plugins, you might have a /stk_test virtual modem (standing for SimToolKit test). I wanted to be able to check the modem was correctly initialized at this point, but it might be an issue when we switch to SIM52XX later on. Shall I remove it ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"check if it's correctly initialized" - you mean, check if it's available? If so, I'm guessing it might still be there in some cases, just not the 0th element in the list of the modems that you'll get. I suggest using if "/sim900_0" in manager.getModems()[0] or something along those lines.

raise ValueError("Default modem should be '/sim900_0', was '{}'".format(modem_path))

# self.start() #todo: check if it's the suitable place to do it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - it makes sense not to start() the loop right when the OfonoBridge is initialized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, explicit is better than implicit =)


def start(self):
self.power_on()
self._init_messages()
self._listen_messages()

@property
def _bus(self):
# lil hack so we always have an up-to-date bus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening with the bus? Why can it get out-of-date?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you get the bus, you actually get a snapshot of the currently available methods.

So if you get the bus before the modem is initialized you don't have any method. Using it that way ensures you always have the latest exposed methods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood! Would be great to put this explanation in the docstring/comment inside that function =)

return pydbus.SystemBus().get('org.ofono', '/sim900_0')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That "/sim900_0" string looks like it could benefit from being an attribute, it's repeated 3 times in this class

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed


@property
def message_manager(self):
return self._get_dbus_interface('MessageManager')

def _get_dbus_interface(self, name):
''.startswith('org.ofono')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this particular line of code do here? It seems like a leftover line of code.

full_name = name if name.startswith('org.ofono') else 'org.ofono.{}'.format(name)
if full_name in self._bus.GetProperties()['Interfaces']:
return self._bus[full_name]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if full_name is not found, it silently returns None?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right we should raise an error


def power_on(self):
if self._bus.GetProperties()["Powered"]:
logger.info("Modem already powered up !")
else:
logger.info("Powering up modem...")
try:
self._bus.SetProperty("Powered", pydbus.Variant('b', True))
sleep(2) # Let the modem some time to initialize
except Exception:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Just except: is enough if you want a catch-all
  2. I think that, if the modem can't power on, we'll want some more information - as much as the exception can provide us, and maybe more, so don't skimp on logging here =)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

logger.error("Couldn't power up the modem !")

def power_off(self):
self._bus.SetProperty("Powered", pydbus.Variant('b', False))

def send_sms(self, to, content): # todo : untested
self.message_manager.SendMessage(to, content)
print("Sending", to, content)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a logger.debug call instead of print?

ConversationManager().on_new_message_sent(to, content)

@staticmethod
def on_message_received(message, details, path=None, interface=None): # todo : untested
logger.info("Got message with path {}".format(path))
ConversationManager().on_new_message_received(message, details)

def _listen_messages(self):
logger.info("Connecting to dbus callbacks")
self.message_manager.IncomingMessage.connect(self.on_message_received)
self.message_manager.ImmediateMessage.connect(self.on_message_received)

def _init_messages(self):
self.message_manager.SetProperty("UseDeliveryReports", pydbus.Variant('b', True))


class ConversationManager(Singleton):
"""
Singleton dedicated to conversations. Logs every message sent and received in a flat file
for any given phone number
"""

def __init__(self):
super(ConversationManager, self).__init__()
self.folder = os.path.expanduser("~/.phone/sms/") # todo: store as a constant somewhere
if not os.path.exists(self.folder):
os.mkdir(self.folder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably want os.mkdirs - it works like mkdir -p


def on_new_message_sent(self, to, content):
logger.info("Sent message to '{}'".format(to))
self._write_log(to, self._format_log(content, from_me=True))

def on_new_message_received(self, content, details):
origin = details['Sender']
logger.info("Received message from'{}'".format(origin))
self._write_log(origin, self._format_log(content, from_me=True))

def _write_log(self, phone_number, log):
with open(self._get_log_path(phone_number), 'a+') as log_file:
log_file.write(log)

def _get_log_path(self, phone_number):
file_name = "{}.txt".format(phone_number)
return os.path.join(self.folder, file_name)

@staticmethod
def _format_log(content, from_me=True):
start_char = '>' if from_me else '<'
return "{prefix}\t{msg}\n".format(prefix=start_char, msg=content)


def main():
DBusGMainLoop(set_as_default=True) # has to be called first
ofono = OfonoBridge()
try:
ofono.start()
mainloop = GLib.MainLoop() # todo : own thread
mainloop.run()
except KeyboardInterrupt:
logger.info("Caught CTRL-C:exiting without powering off...")
except AttributeError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logger.exception instead of just error, and make it a catch-all (as in, except: instead of catching AttributeError)? Would be great to have exception information in the logs, as well as protect from future errors.

logger.error("Error while starting ofono bridge ! Powering off...")
ofono.power_off()


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pydbus
luma.oled
python-nmap
smspdu
Expand Down