-
Notifications
You must be signed in to change notification settings - Fork 19
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
base: devel
Are you sure you want to change the base?
Changes from 7 commits
8b385ae
e1a63fb
f43ea67
1994095
0413ddf
ff3d603
b5d29b0
67c2baa
0966d0e
12d27f9
41ff44c
e1f101b
f085e7b
2a084a6
08dc9e8
da2fb47
1650127
0cf96f2
8e50fd7
01e72d3
1b5c9e5
248a30a
54d548f
49a1ee1
becaef5
85e74ce
2a40a20
47350dc
917f7f6
44d6f44
b04f185
9887af1
85cf72c
377c146
1d29e7f
f92f021
378593e
c787dc3
ec17696
886a243
801fb09
3a3500b
65e450e
bb9e456
5af4c3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
KERNEL=="ttyAMA0", ENV{OFONO_DRIVER}="sim900" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
from helpers import setup_logger | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
||
|
@@ -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)] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import logging | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
from gi.repository import GLib | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It appears the gi module is easily installable once I do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Package created ( |
||
|
||
from helpers import Singleton, setup_logger | ||
|
||
logger = setup_logger(__name__, 'debug') | ||
logging.basicConfig(level=logging.DEBUG) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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__() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, it doesn't inherit from anything =D There was a problem hiding this comment. Choose a reason for hiding this commentThe 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': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And if full_name is not found, it silently returns None? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'll probably want |
||
|
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
logger.error("Error while starting ofono bridge ! Powering off...") | ||
ofono.power_off() | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pydbus | ||
luma.oled | ||
python-nmap | ||
smspdu | ||
|
There was a problem hiding this comment.
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