# Path: pyaiot/common/auth.py
# def auth_token(keys):
#     """Generate a token from the given private and secret keys."""
#     return Fernet(keys.private.encode()).encrypt(keys.secret.encode())
# 
# Path: pyaiot/common/messaging.py
# def check_broker_data(data):
#     """"Utility function that checks the data object.
# 
#     :param data: a dict with only 'uid', 'endpoint' and 'payload' keys.
# 
#     :return True of the data is correct, False otherwise
# 
#     >>> check_broker_data({'uid':1, 'endpoint':'/test', 'payload': 'ok'})
#     True
#     >>> check_broker_data({'endpoint':'/test', 'payload': 'ok'})
#     False
#     >>> check_broker_data({'uid':1, 'payload': 'ok'})
#     False
#     >>> check_broker_data({'uid':1, 'endpoint':'/test'})
#     False
#     >>> check_broker_data({'uid':1, 'endpoint':'/test', 'payload': 'ok',
#     ...                    'extra': 'too many'})
#     False
#     """
# 
#     if 'uid' not in data:
#         logger.debug("Invalid broker data: missing uid")
#     elif 'endpoint' not in data:
#         logger.debug("Invalid broker data: missing endpoint")
#     elif 'payload' not in data:
#         logger.debug("Invalid broker data: missing payload")
#     elif len(data.keys()) > 3:
#         logger.debug("Invalid broker data: too many keys")
#     else:
#         return True
#     return False
# 
# class Message():
#     """Utility class for generating and parsing service messages."""
# 
#     @staticmethod
#     def serialize(message):
#         return json.dumps(message, ensure_ascii=False)
# 
#     @staticmethod
#     def new_node(uid, dst="all"):
#         """Generate a text message indicating a new node."""
#         return Message.serialize({'type': 'new', 'uid': uid, 'dst': dst})
# 
#     @staticmethod
#     def out_node(uid):
#         """Generate a text message indicating a node to remove."""
#         return Message.serialize({'type': 'out', 'uid': uid})
# 
#     @staticmethod
#     def reset_node(uid):
#         """Generate a text message indicating a node reset."""
#         return Message.serialize({'type': 'reset', 'uid': uid})
# 
#     @staticmethod
#     def update_node(uid, endpoint, data, dst="all"):
#         """Generate a text message indicating a node update."""
#         return Message.serialize({'type': 'update',
#                                   'uid': uid,
#                                   'endpoint': endpoint,
#                                   'data': data,
#                                   'dst': dst})
# 
#     @staticmethod
#     def gateway_alive():
#         """Generate a text message indicating that a gateway is alive."""
#         return Message.serialize({'type': 'update', 'uid': 'alive'})
# 
#     @staticmethod
#     def discover_node():
#         """Generate a text message for websocket node discovery."""
#         return Message.serialize({'request': 'discover'})
# 
#     @staticmethod
#     def check_message(raw):
#         """Verify a received message is correctly formatted."""
#         reason = None
#         try:
#             message = json.loads(raw)
#         except TypeError as exc:
#             logger.warning(exc)
#             reason = "Invalid message '{}'.".format(raw)
#             message = None
#         except json.JSONDecodeError:
#             reason = ("Invalid message received "
#                       "'{}'. Only JSON format is supported.".format(raw))
#             message = None
# 
#         if message is not None:
#             if not hasattr(message, '__iter__'):
#                 reason = "Invalid message '{}'.".format(message)
#             elif 'type' not in message and 'data' not in message:
#                 reason = "Invalid message '{}'.".format(message)
#             elif (message['type'] != 'new' and message['type'] != 'update' and
#                   message['type'] != 'out' and message['type'] != 'reset'):
#                 reason = "Invalid message type '{}'.".format(message['type'])
# 
#         if reason is not None:
#             logger.warning(reason)
#             message = None
# 
#         return message, reason


import json
import logging
import asyncio
from abc import ABCMeta, abstractmethod
from tornado import web, gen
from tornado.ioloop import PeriodicCallback
from tornado.websocket import websocket_connect
from pyaiot.common.auth import auth_token
from pyaiot.common.messaging import check_broker_data, Message
    communication protocols used by subclasses (CoAP, MQTT, websocket, etc).
    """

    def __init__(self, keys, options, handlers=[]):
        if options.debug:
            logger.setLevel(logging.DEBUG)

        self.options = options
        self.nodes = {}
        self.broker = None
        self.keys = keys
        settings = {'debug': True}

        # Create connection to broker
        asyncio.ensure_future(self.create_broker_connection(
            "ws://{}:{}/gw".format(options.broker_host, options.broker_port)))

        super().__init__(handlers, **settings)
        logger.debug('Base Gateway application started')

    @abstractmethod
    def update_node_resource(self, node, resource, value):
        """Send an update to a node to change its resource with given value.

        This is dependent on the protocol used to communicate with nodes (CoAP,
        MQTT, etc) and has to be implemented in the protocol specific nodes
        controller.

        Should be a coroutine."""


# Path: pyaiot/common/auth.py
# def auth_token(keys):
#     """Generate a token from the given private and secret keys."""
#     return Fernet(keys.private.encode()).encrypt(keys.secret.encode())
# 
# Path: pyaiot/common/messaging.py
# def check_broker_data(data):
#     """"Utility function that checks the data object.
# 
#     :param data: a dict with only 'uid', 'endpoint' and 'payload' keys.
# 
#     :return True of the data is correct, False otherwise
# 
#     >>> check_broker_data({'uid':1, 'endpoint':'/test', 'payload': 'ok'})
#     True
#     >>> check_broker_data({'endpoint':'/test', 'payload': 'ok'})
#     False
#     >>> check_broker_data({'uid':1, 'payload': 'ok'})
#     False
#     >>> check_broker_data({'uid':1, 'endpoint':'/test'})
#     False
#     >>> check_broker_data({'uid':1, 'endpoint':'/test', 'payload': 'ok',
#     ...                    'extra': 'too many'})
#     False
#     """
# 
#     if 'uid' not in data:
#         logger.debug("Invalid broker data: missing uid")
#     elif 'endpoint' not in data:
#         logger.debug("Invalid broker data: missing endpoint")
#     elif 'payload' not in data:
#         logger.debug("Invalid broker data: missing payload")
#     elif len(data.keys()) > 3:
#         logger.debug("Invalid broker data: too many keys")
#     else:
#         return True
#     return False
# 
# class Message():
#     """Utility class for generating and parsing service messages."""
# 
#     @staticmethod
#     def serialize(message):
#         return json.dumps(message, ensure_ascii=False)
# 
#     @staticmethod
#     def new_node(uid, dst="all"):
#         """Generate a text message indicating a new node."""
#         return Message.serialize({'type': 'new', 'uid': uid, 'dst': dst})
# 
#     @staticmethod
#     def out_node(uid):
#         """Generate a text message indicating a node to remove."""
#         return Message.serialize({'type': 'out', 'uid': uid})
# 
#     @staticmethod
#     def reset_node(uid):
#         """Generate a text message indicating a node reset."""
#         return Message.serialize({'type': 'reset', 'uid': uid})
# 
#     @staticmethod
#     def update_node(uid, endpoint, data, dst="all"):
#         """Generate a text message indicating a node update."""
#         return Message.serialize({'type': 'update',
#                                   'uid': uid,
#                                   'endpoint': endpoint,
#                                   'data': data,
#                                   'dst': dst})
# 
#     @staticmethod
#     def gateway_alive():
#         """Generate a text message indicating that a gateway is alive."""
#         return Message.serialize({'type': 'update', 'uid': 'alive'})
# 
#     @staticmethod
#     def discover_node():
#         """Generate a text message for websocket node discovery."""
#         return Message.serialize({'request': 'discover'})
# 
#     @staticmethod
#     def check_message(raw):
#         """Verify a received message is correctly formatted."""
#         reason = None
#         try:
#             message = json.loads(raw)
#         except TypeError as exc:
#             logger.warning(exc)
#             reason = "Invalid message '{}'.".format(raw)
#             message = None
#         except json.JSONDecodeError:
#             reason = ("Invalid message received "
#                       "'{}'. Only JSON format is supported.".format(raw))
#             message = None
# 
#         if message is not None:
#             if not hasattr(message, '__iter__'):
#                 reason = "Invalid message '{}'.".format(message)
#             elif 'type' not in message and 'data' not in message:
#                 reason = "Invalid message '{}'.".format(message)
#             elif (message['type'] != 'new' and message['type'] != 'update' and
#                   message['type'] != 'out' and message['type'] != 'reset'):
#                 reason = "Invalid message type '{}'.".format(message['type'])
# 
#         if reason is not None:
#             logger.warning(reason)
#             message = None
# 
#         return message, reason


# Path: pyaiot/gateway/common/gateway.py
import json
import logging
import asyncio
from abc import ABCMeta, abstractmethod
from tornado import web, gen
from tornado.ioloop import PeriodicCallback
from tornado.websocket import websocket_connect
from pyaiot.common.auth import auth_token
from pyaiot.common.messaging import check_broker_data, Message

    communication protocols used by subclasses (CoAP, MQTT, websocket, etc).
    """

    def __init__(self, keys, options, handlers=[]):
        if options.debug:
            logger.setLevel(logging.DEBUG)

        self.options = options
        self.nodes = {}
        self.broker = None
        self.keys = keys
        settings = {'debug': True}

        # Create connection to broker
        asyncio.ensure_future(self.create_broker_connection(
            "ws://{}:{}/gw".format(options.broker_host, options.broker_port)))

        super().__init__(handlers, **settings)
        logger.debug('Base Gateway application started')

    @abstractmethod
    def update_node_resource(self, node, resource, value):
        """Send an update to a node to change its resource with given value.

        This is dependent on the protocol used to communicate with nodes (CoAP,
        MQTT, etc) and has to be implemented in the protocol specific nodes
        controller.

        Should be a coroutine."""


    @abstractmethod