1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
""" Command line interface for Verisure MyPages """
import inspect
import json
import re
import click
import logging
from verisure import VariableTypes, Session, ResponseError, LoginError
class DeviceLabel(click.ParamType):
"""Click param for device label"""
name = "DeviceLabel"
def convert(self, value, param, ctx):
if re.match(r"^([A-Z]|[0-9]){4} ([A-Z]|[0-9]){4}$", value):
return value
self.fail(f"{value!r} is not a device label", param, ctx)
class ArmFutureState(click.ParamType):
"""Click param for arm future state"""
name = "FutureState"
class LockFutureState(click.ParamType):
"""Click param for lock future state"""
name = "FutureState"
class TransactionId(click.ParamType):
"""Click param for transaction id"""
name = "TransactionId"
class RequestId(click.ParamType):
"""Click param for request id"""
name = "RequestId"
class Code(click.ParamType):
"""Click param for code"""
name = "Code"
def convert(self, value, param, ctx):
if re.match(r"^[0-9]{4,6}$", value):
return value
self.fail(f"{value!r} is not a code", param, ctx)
VariableTypeMap = {
VariableTypes.DeviceLabel: DeviceLabel(),
VariableTypes.ArmFutureState: ArmFutureState(),
VariableTypes.LockFutureState: LockFutureState(),
bool: click.BOOL,
VariableTypes.TransactionId: TransactionId(),
VariableTypes.RequestId: RequestId(),
VariableTypes.Code: Code(),
}
def options_from_operator_list():
"""Get all query operations and build query cli"""
def decorator(f):
ops = inspect.getmembers(Session, predicate=inspect.isfunction)
for name, operation in reversed(ops):
if not hasattr(operation, 'is_query'):
continue
variables = list(operation.__annotations__.values())
# Remove Giid type from variables, not supported by CLI
if VariableTypes.Giid in variables:
variables.remove(VariableTypes.Giid)
dashed_name = name.replace('_', '-')
if len(variables) == 0:
click.option(
'--'+dashed_name,
is_flag=True,
help=operation.__doc__)(f)
elif len(variables) == 1:
click.option(
'--'+dashed_name,
type=VariableTypeMap[variables[0]],
help=operation.__doc__)(f)
else:
types = [VariableTypeMap[variable] for variable in variables]
click.option(
'--'+dashed_name,
type=click.Tuple(types),
help=operation.__doc__)(f)
return f
return decorator
def make_query(session, name, arguments):
"""make query operation"""
if arguments is True:
return getattr(session, name)()
if isinstance(arguments, str):
return getattr(session, name)(arguments)
return getattr(session, name)(*arguments)
@click.command()
@click.argument('username')
@click.argument('password')
@click.option('-i', '--installation', 'installation', help='Installation number', type=int, default=0) # noqa: E501
@click.option('-c', '--cookie', 'cookie', help='File to store cookie in', default='~/.verisure-cookie') # noqa: E501
@click.option('--mfa', 'mfa', help='Login using MFA', default=False, is_flag=True) # noqa: E501
@click.option('--log-level', type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], case_sensitive=False)) # noqa: E501
@options_from_operator_list()
def cli(username, password, installation, cookie, mfa, log_level, **kwargs):
"""Read and change status of verisure devices through verisure app API"""
if log_level:
logging.basicConfig(level=logging.getLevelName(log_level))
session = Session(username, password, cookie)
try:
# try using the cookie first
installations = session.login_cookie()
except LoginError:
installations = None
try:
if mfa and not installations:
session.request_mfa()
code = input("Enter verification code: ")
session.validate_mfa(code)
installations = session.login_cookie()
elif not installations:
installations = session.login()
session.set_giid(
installations['data']['account']
['installations'][installation]['giid'])
queries = [
make_query(session, name, arguments)
for name, arguments in kwargs.items()
if arguments]
result = session.request(*queries)
click.echo(json.dumps(result, indent=4, separators=(',', ': ')))
except ResponseError as ex:
click.echo(ex,err=True)
if __name__ == "__main__":
# pylint: disable=no-value-for-parameter
cli()
|