summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorEdi Feschiyan <edi.feschiyan@konsulko.com>2020-06-12 22:44:25 +0300
committerEdi Feschiyan <edi.feschiyan@konsulko.com>2020-06-12 22:44:25 +0300
commit8a8b87e65c0b3d579f8ea420e23a9cd07528dfe1 (patch)
treed5ced56f53104781bf156b38dbad7aba48578ca1 /services
parente2bb2d3ff3f909b9417040de4dd8ea876777a6ee (diff)
Adding cookiecutter, preparing for setuptools, new services
Diffstat (limited to 'services')
-rw-r--r--services/audiomixer.py94
-rw-r--r--services/base.py258
-rw-r--r--services/bluetooth-map.py0
-rw-r--r--services/bluetooth-pbap.py0
-rw-r--r--services/bluetooth.py96
-rw-r--r--services/geoclue.py44
-rw-r--r--services/gps.py56
-rw-r--r--services/mediaplayer.py160
-rw-r--r--services/network.py0
-rw-r--r--services/nfc.py42
-rw-r--r--services/taskmanager.py0
-rw-r--r--services/weather.py48
12 files changed, 798 insertions, 0 deletions
diff --git a/services/audiomixer.py b/services/audiomixer.py
new file mode 100644
index 0000000..e594471
--- /dev/null
+++ b/services/audiomixer.py
@@ -0,0 +1,94 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+import asyncio
+import os
+
+verbs = ['subscribe', 'unsubscribe', 'list_controls', 'volume', 'mute']
+events = ['volume_changed', 'mute_changed', 'controls_changed']
+
+
+class AudioMixerService(AGLBaseService):
+ service = 'agl-service-audiomixer'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--list_controls', default=True, help='Request list of controls', action='store_true')
+ parser.add_argument('--getmute', help='Get mute state', action='store_true')
+ parser.add_argument('--setmute', help='Set mute state', type=int, choices=[0, 1])
+ parser.add_argument('--setvolume', help='Set volume level', type=float)
+ parser.add_argument('--getvolume', help='Get volume level', action='store_true')
+
+
+ def __init__(self, ip, port=None, service='agl-service-audiomixer'):
+ super().__init__(api='audiomixer', ip=ip, port=port, service=service)
+
+ async def subscribe(self, event='volume_changed'): # audio mixer uses 'event' instead 'value',
+ return await self.request('subscribe', {'event': event})
+
+ async def unsubscribe(self, event='volume_changed'):
+ return await self.request('unsubscribe', {'event': event})
+
+ async def list_controls(self):
+ return await self.request('list_controls')
+
+ async def volume(self, control='Master', value=None):
+ if value is not None:
+ return await self.request('volume', {'control': control, 'value': value})
+ else:
+ return await self.request('volume', {'control': control})
+
+ async def mute(self, value=None):
+ return await self.request('mute', {'control': 'Master', 'value': value})
+
+
+async def main():
+ args = AudioMixerService.parser.parse_args()
+ ams = await AudioMixerService(ip=args.ipaddr, port=args.port)
+
+ if args.list_controls:
+ resp = await ams.list_controls()
+ print(f'Requesting list_controls with id {resp}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.setvolume is not None:
+ resp = await ams.volume(args.setvolume)
+ print(f'Setting volume to {args.setvolume} with id {resp}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.getvolume:
+ resp = await ams.volume()
+ print(f'Requesting volume with id {resp}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.setmute is not None:
+ resp = await ams.mute(args.setmute)
+ print(f'Setting mute to {args.setmute} with id {resp}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.getmute:
+ resp = await ams.mute()
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await ams.subscribe(event)
+ print(f'Subscribing to {event} with id {id}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.unsubscribe:
+ for event in args.unsubscribe:
+ id = await ams.unsubscribe(event)
+ print(f'Unsubscribing from {event} with id {id}')
+ r = AFBResponse(await ams.response())
+ print(r)
+
+ if args.listener:
+ async for response in ams.listener():
+ print(response)
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main())
diff --git a/services/base.py b/services/base.py
new file mode 100644
index 0000000..03d4793
--- /dev/null
+++ b/services/base.py
@@ -0,0 +1,258 @@
+from json import JSONDecodeError
+from parse import Result, parse
+from websockets import connect
+from random import randint
+from enum import IntEnum
+from typing import Union
+import asyncssh
+import argparse
+import asyncio
+import binascii
+import logging
+import json
+import sys
+import os
+import re
+
+# logging.getLogger('AGLBaseService')
+# logging.basicConfig(level=logging.DEBUG)
+
+# AFB message type
+class AFBT(IntEnum):
+ REQUEST = 2,
+ RESPONSE = 3,
+ ERROR = 4,
+ EVENT = 5
+
+msgq = {}
+AFBLEN = 3
+
+
+def newrand():
+ while True:
+ bs = os.urandom(5)
+ result = bs[0] * bs[1] * bs[2] * bs[3] + bs[4]
+ yield result
+
+def addrequest(msgid, msg):
+ msgq[msgid] = {'request': msg, 'response': None}
+
+def addresponse(msgid, msg):
+ if msgid in msgq.keys():
+ msgq[msgid]['response'] = msg
+
+class AFBResponse:
+ type: AFBT
+ msgid: int
+ data: dict
+ api: str
+ status: str
+ info: str
+
+ def __init__(self, data: list):
+ if type(data[0]) is not int:
+ logging.debug(f'Received a response with non-integer message type {binascii.hexlify(data[0])}')
+ raise ValueError('Received a response with non-integer message type')
+ if data[0] not in AFBT._value2member_map_:
+ raise ValueError(f'Received a response with invalid message type {data[0]}')
+ self.type = AFBT(data[0])
+
+ if self.type == AFBT.RESPONSE:
+ if 'request' not in data[2]:
+ logging.error(f'Received malformed or invalid response without "request" dict - {data}')
+ if not str.isnumeric(data[1]):
+ raise ValueError(f'Received a response with non-numeric message id {data[1]}')
+ else:
+ self.msgid = int(data[1])
+ self.status = data[2]['request']['status']
+ if 'info' in data[2]['request']:
+ self.info = data[2]['request']['info']
+ if 'response' in data[2]:
+ self.data = data[2]['response']
+
+ elif self.type == AFBT.EVENT:
+ self.api = data[1]
+ if 'data' in data[2]:
+ self.data = data[2]['data']
+
+ elif self.type == AFBT.ERROR:
+ logging.debug(f'AFB returned erroneous response {data}')
+ self.msgid = int(data[1])
+ self.status = data[2]['request']['status']
+ self.info = data[2]['request']['info']
+ # raise ValueError(f'AFB returned erroneous response {data}')
+ # if 'request' not in data[2] or 'response' not in data[2]:
+
+ if 'response' in data[2]:
+ self.data = data[2]['response']
+
+ def __str__(self): # for debugging purposes
+ if self.type == AFBT.EVENT:
+ return f'[{self.type.name}][{self.api}][Data: {self.data if hasattr(self, "data") else None}]'
+ else:
+ return f'[{self.type.name}][Status: {self.status}][{self.msgid}]' \
+ f'[Info: {self.info if hasattr(self,"info") else None}]' \
+ f'[Data: {self.data if hasattr(self, "data") else None}]'
+
+
+class AGLBaseService:
+ api: str
+ url: str
+ ip : str
+ port = None
+ token: str
+ uuid: str
+ service = None
+ logger = None
+
+ @staticmethod
+ def getparser():
+ parser = argparse.ArgumentParser(description='Utility to interact with agl-service-* via it\'s websocket')
+ parser.add_argument('-l', '--loglevel', help='Level of logging verbosity', default='INFO',
+ choices=list(logging._nameToLevel.keys()))
+ parser.add_argument('ipaddr', default=os.environ.get('AGL_TGT_IP', 'localhost'), help='AGL host address')
+ parser.add_argument('--port', default=os.environ.get('AGL_TGT_PORT', None), help=f'AGL service websocket port')
+ parser.add_argument('--listener', default=False, help='Register a listener for incoming events', action='store_true')
+ parser.add_argument('--subscribe', type=str, help='Subscribe to event type', action='append', metavar='event')
+ parser.add_argument('--unsubscribe', type=str, help='Unsubscribe from event type', action='append', metavar='event')
+ parser.add_argument('--json', type=str, help='Send your own json string')
+ parser.add_argument('--verb', type=str, help='Send the json above to specific verb')
+ parser.add_argument('--api', type=str, help='Send the above two to a specific api')
+ return parser
+
+ def __init__(self, api: str, ip: str, port: str = None, url: str = None,
+ token: str = 'HELLO', uuid: str = 'magic', service: str = None):
+ self.api = api
+ self.url = url
+ self.ip = ip
+ self.port = port
+ self.token = token
+ self.uuid = uuid
+ self.service = service
+ self.logger = logging.getLogger(service)
+
+ def __await__(self):
+ return self._async_init().__await__()
+
+ async def __aenter__(self):
+ return self._async_init()
+
+ async def _async_init(self):
+ # setting ping_interval to None because AFB does not support websocket ping
+ # if set to !None, the library will close the socket after the default timeout
+ if self.port is None:
+ serviceport = await self.portfinder()
+ if serviceport is not None:
+ self.port = serviceport
+ else:
+ self.logger.error('Unable to find port')
+ exit(1)
+
+ URL = f'ws://{self.ip}:{self.port}/api?token={self.token}&uuid={self.uuid}'
+ self._conn = connect(close_timeout=0, uri=URL, subprotocols=['x-afb-ws-json1'], ping_timeout=None, compression=None)
+ self.websocket = await self._conn.__aenter__()
+ return self
+
+ async def __aexit__(self, *args, **kwargs):
+ await self._conn.__aexit__(*args, **kwargs)
+
+ async def close(self):
+ await self._conn.__aexit__(*sys.exc_info())
+
+ async def send(self, message):
+ await self.websocket.send(message)
+
+ async def receive(self):
+ return await self.websocket.recv()
+
+ async def portfinder(self):
+ # TODO:handle ssh timeouts, asyncssh does not support it apparently, and connect returns context_manager which
+ # cannot be used with asyncio.wait_for
+
+ async with asyncssh.connect(self.ip, username='root') as c:
+
+ servicename = await c.run(f"systemctl --all | grep {self.service}-- | awk '{{print $1}}'", check=False)
+ if self.service not in servicename.stdout:
+ logging.error(f"Service matching pattern - '{self.service}' - NOT FOUND")
+ exit(1)
+ pidres = await c.run(f'systemctl show --property MainPID --value {servicename.stdout.strip()}')
+ pid = int(pidres.stdout.strip(), 10)
+ if pid is 0:
+ logging.warning(f'Service {servicename.stdout.strip()} is stopped')
+ return None
+ else:
+ self.logger.debug(f'Service PID: {str(pid)}')
+
+ sockets = await c.run(f'find /proc/{pid}/fd/ | xargs readlink | grep socket')
+ inodes = frozenset(re.findall("socket:\\[(.*)\\]", sockets.stdout))
+ self.logger.debug(f"Socket inodes: {inodes}")
+
+ procnettcp = await c.run('cat /proc/net/tcp')
+ fieldsstr = '{sl}: {local_address} {rem_address} {st} {tx_queue}:{rx_queue} {tr}:{tmwhen} {retrnsmt} {uid}'\
+ ' {timeout} {inode} {sref_cnt} {memloc} {rto} {pred_sclk} {ackquick} {congest} {slowstart}'
+ tcpsockets = [' '.join(l.split()) for l in procnettcp.stdout.splitlines()[1:]]
+ # different lines with less stats appear sometimes, parse will return None, so ignore 'None' lines
+ parsedtcpsockets = []
+ for l in tcpsockets:
+ res = parse(fieldsstr, l)
+ if isinstance(res, Result):
+ parsedtcpsockets.append(res)
+
+ socketinodesbythisprocess = [l for l in parsedtcpsockets if
+ isinstance(l, Result) and
+ l.named['inode'] in inodes and
+ # 0A is listening state for the socket
+ l.named['st'] == '0A']
+
+ for s in socketinodesbythisprocess:
+ _, port = tuple(parse('{}:{}', s['local_address']))
+ port = int(port, 16)
+ if port >= 30000: # the port is above 30000 range, 8080 is some kind of proxy
+ self.logger.debug(f'Service running at port {port}')
+ return port
+
+ async def listener(self, stdout: bool = False):
+ while True:
+ raw = await self.response()
+ data = AFBResponse(raw)
+ if stdout: print(data)
+ yield data
+
+ async def response(self):
+ try:
+ msg = await self.websocket.recv()
+ try:
+ data = json.loads(msg)
+ self.logger.debug('[AGL] -> ' + msg)
+ if isinstance(data, list):
+ # check whether the received response is an answer to previous query and queue it for debugging
+ if len(data) == AFBLEN and data[0] == AFBT.RESPONSE and str.isnumeric(data[1]):
+ msgid = int(data[1])
+ if msgid in msgq:
+ addresponse(msgid, data)
+ return data
+ except JSONDecodeError:
+ self.logger.warning("Not decoding a non-json message")
+
+ except KeyboardInterrupt:
+ self.logger.debug("Received keyboard interrupt, exiting")
+ except asyncio.CancelledError:
+ self.logger.warning("Websocket listener coroutine stopped")
+ except Exception as e:
+ self.logger.error("Unhandled seal: " + str(e))
+
+ async def afbresponse(self):
+ return AFBResponse(await self.response())
+
+ async def request(self, verb: str, values: Union[str, dict] = "", msgid: int = None):
+ msgid = next(newrand()) if msgid is None else msgid
+ l = json.dumps([AFBT.REQUEST, str(msgid), f'{self.api}/{verb}', values])
+ self.logger.debug(f'[AGL] <- {l}')
+ await self.send(l)
+ return msgid
+
+ async def subscribe(self, event):
+ return await self.request('subscribe', {'value': f'{event}'}) # some services may use 'event' instead 'value'
+
+ async def unsubscribe(self, event):
+ return await self.request('unsubscribe', {'value': f'{event}'})
diff --git a/services/bluetooth-map.py b/services/bluetooth-map.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/bluetooth-map.py
diff --git a/services/bluetooth-pbap.py b/services/bluetooth-pbap.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/bluetooth-pbap.py
diff --git a/services/bluetooth.py b/services/bluetooth.py
new file mode 100644
index 0000000..b73f8a8
--- /dev/null
+++ b/services/bluetooth.py
@@ -0,0 +1,96 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+import asyncio
+import os
+
+Verbs = ['subscribe', 'unsubscribe', 'managed_objects', 'adapter_state', 'default_adapter', 'avrcp_controls',
+ 'connect', 'disconnect', 'pair', 'cancel_pairing', 'confirm_pairing', 'remove_device']
+AdapterStateParams = ['discovery', 'discoverable', 'powered', ]
+
+BTEventType = ['adapter_changes', 'device_changes', 'media', 'agent']
+
+
+class BluetoothService(AGLBaseService):
+ service = 'agl-service-bluetooth'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--default_adapter', help='Get default bluetooth adapter', action='store_true')
+ parser.add_argument('--managed_objects', help='Get managed objects', action='store_true')
+ parser.add_argument('--adapter', help='Select remote adapter', required=False, default='hci0')
+ parser.add_argument('--adapter_state')
+ parser.add_argument('--connect', help='Connect to device', metavar='dev_88_0F_10_96_D3_20')
+ parser.add_argument('--disconnect', help='Disconnect from device', metavar='dev_88_0F_10_96_D3_20')
+ parser.add_argument('--pair', help='Pair with a device', metavar='dev_88_0F_10_96_D3_20')
+ parser.add_argument('--cancel_pairing', help='Cancel ongoing pairing')
+ parser.add_argument('--confirm_pairing', metavar='pincode')
+ parser.add_argument('--remove_device', metavar='dev_88_0F_10_96_D3_20', help='Remove paired device')
+
+
+
+ def __init__(self, ip, port=None, service='agl-service-bluetooth'):
+ super().__init__(api='Bluetooth-Manager', ip=ip, port=port, service=service)
+
+ async def subscribe(self, event='device_changes'):
+ await super().subscribe(event=event)
+
+ async def unsubscribe(self, event='device_changes'):
+ await super().unsubscribe(event=event)
+
+ async def managed_objects(self):
+ return await self.request('managed_objects')
+
+ async def adapter_state(self, adapter=None, value=None):
+ p = {}
+ if adapter:
+ p = {'adapter': adapter}
+ if isinstance(value, dict):
+ p = {**p, **value}
+
+ return await self.request('adapter_state', p)
+
+ async def default_adapter(self):
+ return await self.request('default_adapter', "")
+
+ async def connect(self, device: str = 'hci0'):
+ return await self.request('connect', {'device': device})
+
+ async def disconnect(self, device: str = 'hci0'):
+ return await self.request('disconnect', {'device': device})
+
+ async def pair(self, device):
+ return await self.request('pair', {'device': device})
+
+ async def cancel_pairing(self):
+ return await self.request('cancel_pairing')
+
+ async def confirm_pairing(self, pincode):
+ return await self.request('confirm_pairing', {'pincode': pincode})
+
+ async def remove_device(self, device):
+ return await self.request('remove_device', {'device': device})
+
+ async def avrcp_controls(self):
+ pass
+
+async def main(loop):
+ args = BluetoothService.parser.parse_args()
+ bts = await BluetoothService(ip=args.ipaddr, port=args.port)
+
+ if args.default_adapter:
+ id = await bts.default_adapter()
+ print(f'Requesting default adapter with id {id}')
+ r = AFBResponse(await bts.response())
+ print(r)
+
+ if args.adapter_state:
+ pass
+
+ if args.listener:
+ for response in bts.listener():
+ print(response)
+
+ bts.logger.debug(await bts.adapter_state('hci0', {'uuids': ['0000110e-0000-1000-8000-00805f9b34fb']}))
+
+
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/services/geoclue.py b/services/geoclue.py
new file mode 100644
index 0000000..df1afd6
--- /dev/null
+++ b/services/geoclue.py
@@ -0,0 +1,44 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+import asyncio
+import os
+
+
+class GeoClueService(AGLBaseService):
+ service = 'agl-service-geoclue'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--location', help='Get current location', action='store_true')
+
+ def __init__(self, ip, port=None, api='geoclue'):
+ super().__init__(ip=ip, port=port, api=api, service='agl-service-geoclue')
+
+ async def location(self):
+ return await self.request('location')
+
+ async def subscribe(self, event='location'):
+ return await super().subscribe(event=event)
+
+ async def unsubscribe(self, event='location'):
+ return await super().unsubscribe(event=event)
+
+
+async def main(loop):
+ args = GeoClueService.parser.parse_args()
+ gcs = await GeoClueService(args.ipaddr)
+
+ if args.location:
+ id = await gcs.location()
+ print(f'Sent location request with messageid {id}')
+ print(AFBResponse(await gcs.response()))
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await gcs.subscribe(event)
+ print(f"Subscribed for {event} with messageid {id}")
+ print(AFBResponse(await gcs.response()))
+ if args.listener:
+ async for response in gcs.listener():
+ print(response)
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/services/gps.py b/services/gps.py
new file mode 100644
index 0000000..1905e0f
--- /dev/null
+++ b/services/gps.py
@@ -0,0 +1,56 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+import asyncio
+import os
+
+
+class GPSService(AGLBaseService):
+ service = 'agl-service-gps'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--record', help='Begin recording verb ')
+ parser.add_argument('--location', help='Get current location', action='store_true')
+
+ def __init__(self, ip, port=None):
+ super().__init__(api='gps', ip=ip, port=port, service='agl-service-gps')
+
+ async def location(self):
+ return await self.request('location')
+
+ async def record(self, state='on'):
+ return await self.request('record', {'state': state})
+
+ async def subscribe(self, event='location'):
+ return await super().subscribe(event=event)
+
+ async def unsubscribe(self, event='location'):
+ return await super().subscribe(event=event)
+
+
+async def main(loop):
+ args = GPSService.parser.parse_args()
+ gpss = await GPSService(ip=args.ipaddr, port=args.port)
+
+ if args.loglevel:
+ gpss.logger.setLevel(args.loglevel)
+
+ if args.record:
+ id = await gpss.record(args.record)
+ print(f'Sent gps record request with value {args.record} with messageid {id}')
+ print(AFBResponse(await gpss.response()))
+
+ if args.location:
+ msgid = await gpss.location()
+ print(AFBResponse(await gpss.response()))
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await gpss.subscribe(event)
+ print(f'Subscribed for event {event} with messageid {id}')
+ print(AFBResponse(await gpss.response()))
+
+ if args.listener:
+ async for response in gpss.listener():
+ print(response)
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/services/mediaplayer.py b/services/mediaplayer.py
new file mode 100644
index 0000000..9291981
--- /dev/null
+++ b/services/mediaplayer.py
@@ -0,0 +1,160 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+from typing import Union
+import logging
+import asyncio
+import os
+
+class AFBMediaPlayerResponse(AFBResponse):
+ status: str
+ info: str
+ data = None
+
+ def __init__(self, data: AFBResponse):
+ if isinstance(data, list):
+ super().__init__(data)
+ self.msgid = data.msgid
+ self.type = data.type
+ self.data = data.data
+
+
+class MediaPlayerService(AGLBaseService):
+ service = 'agl-service-mediaplayer'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--playlist', help='Get current playlist', action='store_true')
+ parser.add_argument('--control', help='Play/Pause/Previous/Next')
+ parser.add_argument('--seek', help='Seek time through audio track', metavar='msec', type=int)
+ parser.add_argument('--rewind', help='Rewind time', metavar='msec', type=int)
+ parser.add_argument('--fastforward', help='Fast forward time', metavar='msec', type=int)
+ parser.add_argument('--picktrack', help='Play specific track in the playlist', metavar='index', type=int)
+ parser.add_argument('--volume', help='Volume control - <1-100>', metavar='int')
+ parser.add_argument('--loop', help='Set loop state - <off/track/playlist>', metavar='string')
+ parser.add_argument('--avrcp', help='AVRCP Controls')
+ def __await__(self):
+ return super()._async_init().__await__()
+
+ def __init__(self, ip, port=None):
+ super().__init__(api='mediaplayer', ip=ip, port=port, service='agl-service-mediaplayer')
+
+ async def playlist(self):
+ return await self.request('playlist')
+
+ async def subscribe(self, event='metadata'):
+ return await super().subscribe(event=event)
+
+ async def unsubscribe(self, event='metadata'):
+ return await super().subscribe(event=event)
+
+ async def control(self, name, value=None):
+ loopstate = ['off', 'playlist', 'track']
+ avrcp_controls = ['next', 'previous', 'play', 'pause']
+ controls = {
+ 'play': None,
+ 'pause': None,
+ 'previous': None,
+ 'next': None,
+ 'seek': 'position',
+ 'fast-forward': 'position',
+ 'rewind': 'position',
+ 'pick-track': 'index',
+ 'volume': 'volume',
+ 'loop': 'state',
+ # 'avrcp_controls': 'value'
+ }
+ assert name in controls.keys(), f'Tried to use non-existent {name} as control for {self.api}'
+ msg = None
+ if name in ['play', 'pause', 'previous', 'next']:
+ msg = {'value': name}
+ elif name in ['seek', 'fast-forward', 'rewind']:
+ #assert value > 0, "Tried to seek with negative integer"
+ msg = {'value': name, controls[name]: str(value)}
+ elif name == 'pick-track':
+ assert type(value) is int, "Try picking a song with an integer"
+ assert value > 0, "Tried to pick a song with a negative integer"
+ msg = {'value': name, controls[name]: str(value)}
+ elif name == 'volume':
+ assert type(value) is int, "Try setting the volume with an integer"
+ assert value > 0, "Tried to set the volume with a negative integer, use values betwen 0-100"
+ assert value < 100, "Tried to set the volume over 100%, use values betwen 0-100"
+ msg = {'value': name, name: str(value)}
+ elif name == 'loop':
+ assert value in loopstate, f'Tried to set invalid loopstate - {value}, use "off", "playlist" or "track"'
+ msg = {'value': name, controls[name]: str(value)}
+ # elif name == 'avrcp_controls':
+ # msg = {'value': name, }
+ assert msg is not None, "Congratulations, somehow you made an invalid control request"
+
+ return await self.request('controls', msg)
+
+
+async def main(loop):
+ args = MediaPlayerService.parser.parse_args()
+ MPS = await MediaPlayerService(ip=args.ipaddr)
+
+ if args.playlist:
+ id = await MPS.playlist()
+ r = AFBResponse(await MPS.response())
+ for l in r.data['list']: print(l)
+
+ if args.control:
+ id = await MPS.control(args.control)
+ print(f'Sent {args.control} request with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.seek:
+ id = await MPS.control('seek', args.seek)
+ print(f'Sent seek request to {args.seek} msec with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.fastforward:
+ id = await MPS.control('fast-forward', args.fastforward)
+ print(f'Sent fast-forward request for {args.fastforward} msec with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.rewind:
+ id = await MPS.control('rewind', -args.rewind)
+ print(f'Sent rewind request for {args.rewind} msec with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.picktrack:
+ id = await MPS.control('pick-track', args.picktrack)
+ print(f'Sent pick-track request with index {args.rewind} with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.volume:
+ id = await MPS.control('volume', int(args.volume))
+ print(f'Sent volume request: {args.rewind} with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ if args.loop:
+ id = await MPS.control('loop', args.loop)
+ print(f'Sent loop-state request: {args.loop} with messageid {id}')
+ r = AFBResponse(await MPS.response())
+ print(r)
+
+ # if args.avrcp:
+ # id = await MPS.control('avrcp_controls', args.avrcp)
+ # print(f'Sent AVRCP control request: {args.loop} with messageid {id}')
+ # r = AFBResponse(await MPS.response())
+ # print(r)
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await MPS.subscribe(event)
+ print(f"Subscribed for event {event} with messageid {id}")
+ r = await MPS.response()
+ print(r)
+
+ if args.listener:
+ async for response in MPS.listener():
+ print(response)
+
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/services/network.py b/services/network.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/network.py
diff --git a/services/nfc.py b/services/nfc.py
new file mode 100644
index 0000000..c966c4a
--- /dev/null
+++ b/services/nfc.py
@@ -0,0 +1,42 @@
+from aglbaseservice import AGLBaseService, AFBResponse
+import asyncio
+
+class NFCService(AGLBaseService):
+ service = 'agl-service-nfc'
+ parser = AGLBaseService.getparser()
+
+ def __init__(self, ip, port=None, api='nfc'):
+ super().__init__(ip=ip, port=port, api=api, service='agl-service-nfc')
+
+ async def subscribe(self, event='presence'):
+ return await super().subscribe(event=event)
+
+ async def unsubscribe(self, event='presence'):
+ return await super().unsubscribe(event=event)
+
+
+async def main(loop):
+ args = NFCService.parser.parse_args()
+ nfcs = await NFCService(ip=args.ipaddr, port=args.port)
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await nfcs.subscribe(event)
+ print(f"Subscribing for event {event} with messageid {id}")
+ r = AFBResponse(await nfcs.response())
+ print(r)
+
+ if args.unsubscribe:
+ for event in args.unsubscribe:
+ id = await nfcs.unsubscribe(event)
+ print(f"Unsubscribing for event {event} with messageid {id}")
+ r = AFBResponse(await nfcs.response())
+ print(r)
+
+ if args.listener:
+ async for response in nfcs.listener():
+ print(response)
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main(loop))
diff --git a/services/taskmanager.py b/services/taskmanager.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/taskmanager.py
diff --git a/services/weather.py b/services/weather.py
new file mode 100644
index 0000000..f3c02d0
--- /dev/null
+++ b/services/weather.py
@@ -0,0 +1,48 @@
+import asyncio
+import json
+from aglbaseservice import AGLBaseService, AFBResponse
+
+
+class WeatherService(AGLBaseService):
+ service = 'agl-service-weather'
+ parser = AGLBaseService.getparser()
+ parser.add_argument('--current', default=True, help='Request current weather state', action='store_true')
+ parser.add_argument('--apikey', default=False, help='Request weather API Key', action='store_true')
+
+ def __init__(self, ip, port=None):
+ super().__init__(api='weather', ip=ip, port=port, service='agl-service-weather')
+
+ async def current_weather(self):
+ return await self.request('current_weather', "")
+
+ async def apikey(self):
+ return await self.request('api_key', "")
+
+
+async def main():
+ args = WeatherService.parser.parse_args()
+ aws = await WeatherService(ip=args.ipaddr, port=args.port)
+ if args.current:
+ id = await aws.current_weather()
+ resp = AFBResponse(await aws.response())
+ print(json.dumps(resp.data, indent=2))
+
+ if args.apikey:
+ id = await aws.apikey()
+ resp = AFBResponse(await aws.response())
+ print(resp.data['api_key'])
+
+ if args.subscribe:
+ for event in args.subscribe:
+ id = await aws.subscribe(event)
+ print(f'Subscribed for event {event} with messageid {id}')
+ resp = AFBResponse(await aws.response())
+ print(resp)
+
+ if args.listener:
+ async for response in aws.listener():
+ print(response)
+
+if __name__ == '__main__':
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(main())