-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
246 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
2.13 | ||
==== | ||
|
||
* Add support for asyncio in the gammu worker | ||
|
||
2.12 | ||
==== | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: UTF-8 -*- | ||
# vim: expandtab sw=4 ts=4 sts=4: | ||
# | ||
# Copyright © 2003 - 2018 Michal Čihař <michal@cihar.com> | ||
# | ||
# This file is part of python-gammu <https://wammu.eu/python-gammu/> | ||
# | ||
# This program is free software; you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation; either version 2 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, write to the Free Software Foundation, Inc., | ||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
''' | ||
python-gammu - Phone communication libary | ||
Gammu asynchronous wrapper example with asyncio. This allows your application to care | ||
only about handling received data and not about phone communication | ||
details. | ||
''' | ||
|
||
import sys | ||
import pprint | ||
pp = pprint.PrettyPrinter(indent=4) | ||
|
||
import gammu | ||
import gammu.asyncworker | ||
import asyncio | ||
|
||
|
||
def sms_callback(messages): | ||
pp.pprint(messages) | ||
|
||
async def send_message_async(state_machine, number, message): | ||
smsinfo = { | ||
'Class': -1, | ||
'Unicode': False, | ||
'Entries': [ | ||
{ | ||
'ID': 'ConcatenatedTextLong', | ||
'Buffer': message | ||
} | ||
]} | ||
# Encode messages | ||
encoded = gammu.EncodeSMS(smsinfo) | ||
# Send messages | ||
for message in encoded: | ||
# Fill in numbers | ||
message['SMSC'] = {'Location': 1} | ||
message['Number'] = number | ||
# Actually send the message | ||
await state_machine.send_sms_async(message) | ||
|
||
async def get_network_info(worker): | ||
info = await worker.GetNetworkInfoAsync() | ||
print('NetworkName:',info['NetworkName']) | ||
print(' State:',info['State']) | ||
print(' NetworkCode:',info['NetworkCode']) | ||
print(' CID:',info['CID']) | ||
print(' LAC:',info['LAC']) | ||
|
||
async def get_info(state_machine): | ||
print('Phone infomation:') | ||
manufacturer = await state_machine.GetManufacturer() | ||
print(('{0:<15}: {1}'.format('Manufacturer', manufacturer))) | ||
model = await state_machine.GetModel() | ||
print(('{0:<15}: {1} ({2})'.format('Model', model[0], model[1]))) | ||
imei = await state_machine.GetIMEI() | ||
print(('{0:<15}: {1}'.format('IMEI', imei))) | ||
firmware = await state_machine.GetFirmware() | ||
print(('{0:<15}: {1}'.format('Firmware', firmware[0]))) | ||
|
||
async def main(): | ||
|
||
gammu.SetDebugFile(sys.stderr) | ||
gammu.SetDebugLevel('textall') | ||
|
||
config = dict(Device="/dev/ttyS6", Connection="at") | ||
worker = gammu.asyncworker.GammuAsyncWorker() | ||
worker.configure(config) | ||
|
||
try: | ||
await worker.init_async() | ||
|
||
print(await worker.get_signal_quality_async()) | ||
|
||
await send_message_async(worker, '6700', 'BAL') | ||
|
||
# Just a busy waiting for event | ||
# We need to keep communication with phone to get notifications | ||
print('Press Ctrl+C to interrupt') | ||
while 1: | ||
try: | ||
signal = await worker.get_signal_quality_async() | ||
print('Signal is at {0:d}%'.format(signal['SignalPercent'])) | ||
except Exception as e: | ||
print('Exception reading signal: {0}'.format(e)) | ||
|
||
await asyncio.sleep(30) | ||
|
||
except Exception as e: | ||
print('Exception:') | ||
print(e) | ||
|
||
print("Terminate Start") | ||
await worker.terminate_async() | ||
print("Terminate Done") | ||
|
||
if __name__ == '__main__': | ||
event_loop = asyncio.get_event_loop() | ||
try: | ||
event_loop.run_until_complete(main()) | ||
finally: | ||
event_loop.close() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
__all__ = [ | ||
'data', | ||
'worker', | ||
'asyncworker', | ||
'smsd', | ||
'exception', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
"""Async extensions for gammu.""" | ||
import asyncio | ||
from asyncio import get_running_loop | ||
|
||
import gammu # pylint: disable=import-error, no-member | ||
import gammu.worker # pylint: disable=import-error, no-member | ||
|
||
class GammuAsyncThread(gammu.worker.GammuThread): | ||
"""Thread for phone communication.""" | ||
|
||
def __init__(self, queue, config, callback): | ||
"""Initialize thread.""" | ||
super().__init__(queue, config, callback) | ||
|
||
def _do_command(self, future, cmd, params, percentage=100): | ||
"""Execute single command on phone.""" | ||
func = getattr(self._sm, cmd) | ||
result = None | ||
try: | ||
if params is None: | ||
result = func() | ||
elif isinstance(params, dict): | ||
result = func(**params) | ||
else: | ||
result = func(*params) | ||
except gammu.GSMError as info: | ||
errcode = info.args[0]["Code"] | ||
error = gammu.ErrorNumbers[errcode] | ||
self._callback(future, result, error, percentage) | ||
except Exception as exception: # pylint: disable=broad-except | ||
self._callback(future, None, exception, percentage) | ||
else: | ||
self._callback(future, result, None, percentage) | ||
|
||
|
||
class GammuAsyncWorker(gammu.worker.GammuWorker): | ||
"""Extend gammu worker class for async operations.""" | ||
|
||
def worker_callback(self, name, result, error, percents): | ||
"""Execute command from the thread worker.""" | ||
future = None | ||
if name == "Init" and self._init_future is not None: | ||
future = self._init_future | ||
elif name == "Terminate" and self._terminate_future is not None: | ||
# Set _kill to true on the base class to avoid waiting for termination | ||
self._thread._kill = True # pylint: disable=protected-access | ||
future = self._terminate_future | ||
elif hasattr(name, "set_result"): | ||
future = name | ||
|
||
if future is not None: | ||
if error is None: | ||
self._loop.call_soon_threadsafe(future.set_result, result) | ||
else: | ||
exception = error | ||
if not isinstance(error, Exception): | ||
exception = gammu.GSMError(error) | ||
self._loop.call_soon_threadsafe(future.set_exception, exception) | ||
|
||
def __init__(self): | ||
"""Initialize the worker class. | ||
@param callback: See L{GammuThread.__init__} for description. | ||
""" | ||
super().__init__(self.worker_callback) | ||
self._loop = get_running_loop() | ||
self._init_future = None | ||
self._terminate_future = None | ||
self._thread = None | ||
|
||
async def init_async(self): | ||
"""Connect to phone.""" | ||
self._init_future = self._loop.create_future() | ||
|
||
self._thread = GammuAsyncThread(self._queue, self._config, self._callback) | ||
self._thread.start() | ||
|
||
await self._init_future | ||
self._init_future = None | ||
|
||
async def get_signal_quality_async(self): | ||
"""Get signal quality from phone.""" | ||
future = self._loop.create_future() | ||
self.enqueue(future, commands=[("GetSignalQuality", ())]) | ||
result = await future | ||
return result | ||
|
||
async def send_sms_async(self, message): | ||
"""Send sms message via the phone.""" | ||
future = self._loop.create_future() | ||
self.enqueue(future, commands=[("SendSMS", [message])]) | ||
result = await future | ||
return result | ||
|
||
async def set_incoming_callback_async(self, callback): | ||
"""Set the callback to call from phone.""" | ||
future = self._loop.create_future() | ||
self.enqueue(future, commands=[("SetIncomingCallback", [callback])]) | ||
result = await future | ||
return result | ||
|
||
async def set_incoming_sms_async(self): | ||
"""Activate SMS notifications from phone.""" | ||
future = self._loop.create_future() | ||
self.enqueue(future, commands=[("SetIncomingSMS", ())]) | ||
result = await future | ||
return result | ||
|
||
async def terminate_async(self): | ||
"""Terminate phone communication.""" | ||
self._terminate_future = self._loop.create_future() | ||
self.enqueue("Terminate") | ||
await self._terminate_future | ||
|
||
while self._thread.is_alive(): | ||
await asyncio.sleep(5) | ||
self._thread = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters