Пример #1
0
    def __init__(
        self,
        name: str,
        vendor_id: int,
        product_id: int,
        resolution: str,
        num_cameras: int = 1,
        simulate: bool = False,
        usb_mux_comms: Optional[Dict[str, Any]] = None,
        usb_mux_channel: Optional[int] = None,
        i2c_lock: Optional[threading.RLock] = None,
        mux_simulator: Optional[MuxSimulator] = None,
    ) -> None:
        """Initializes USB camera camera."""

        # Initialize parameters
        self.name = name
        self.vendor_id = vendor_id
        self.product_id = product_id
        self.resolution = resolution
        self.num_cameras = num_cameras
        self.simulate = simulate

        # Initialize logger
        logname = "Driver({})".format(name)
        self.logger = logger.Logger(logname, "peripherals")

        # Initialize pygame
        pygame.init()
        pygame.camera.init()

        # Check if simulating
        if self.simulate:
            self.logger.info("Simulating driver")
            self.directory = SIMULATE_IMAGE_DIR
        else:
            self.directory = IMAGE_DIR

        # Check directory exists else create it
        if not os.path.exists(self.directory):
            os.makedirs(self.directory)

        # Check if using usb mux
        if usb_mux_comms == None or usb_mux_channel == None:
            self.usb_mux_enabled = False
            return

        # Initialize usb mux properties
        self.bus = usb_mux_comms.get("bus")
        self.mux = usb_mux_comms.get("mux")
        self.channel = usb_mux_comms.get("channel")
        self.address = usb_mux_comms.get("address")

        # Check if using default bus
        if self.bus == "default":
            self.logger.debug("Using default i2c bus")
            self.bus = os.getenv("DEFAULT_I2C_BUS")

            # Convert exported value from non-pythonic none to pythonic None
            if self.bus == "none":
                self.bus = None

            if self.bus != None:
                self.bus = int(self.bus)

        # Check if using default mux
        if self.mux == "default":
            self.logger.debug("mux is default")
            self.mux = os.getenv("DEFAULT_MUX_ADDRESS")

            # Convert exported value from non-pythonic none to pythonic None
            if self.mux == "none":
                self.mux = None
            self.logger.debug("mux = {}".format(self.mux))

        # Convert i2c config params from hex to int if they exist
        if self.address != None:
            address = int(self.address, 16)
        if self.mux != None:
            self.mux = int(self.mux, 16)

        # Using usb mux, initialize driver
        try:
            self.dac5578 = DAC5578Driver(
                name=name,
                i2c_lock=i2c_lock,  # type: ignore
                bus=self.bus,
                mux=self.mux,
                channel=self.channel,
                address=self.address,
                simulate=self.simulate,
                mux_simulator=mux_simulator,
            )
            self.usb_mux_channel = usb_mux_channel
            self.usb_mux_enabled = True
        except I2CError as e:
            raise exceptions.InitError(logger=self.logger) from e
Пример #2
0
class IotViewSet(viewsets.ModelViewSet):
    """View set for iot interactions."""

    # Initialize logger
    logger = logger.Logger("IotViewSet", "app")

    @list_route(methods=["GET"], permission_classes=[IsAuthenticated, IsAdminUser])
    def info(self, request: Request) -> Response:
        """Gets iot info."""
        self.logger.debug("Getting iot info")

        # Get iot manager
        app_config = apps.get_app_config(APP_NAME)
        iot_manager = app_config.coordinator.iot

        # Build response
        response = {
            "message": "Successfully got iot info",
            "is_registered": iot_manager.is_registered,
            "is_connected": iot_manager.is_connected,
            "verification_code": iot_manager.verification_code,
        }

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, 200)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def register(self, request: Request) -> Response:
        """Registers device with google cloud platform."""
        self.logger.debug("Registering device with iot cloud")

        # Get iot manager
        app_config = apps.get_app_config(APP_NAME)
        iot_manager = app_config.coordinator.iot

        # Register device
        iot_manager.register()

        # Build response
        response = {"message": "Successfully registered"}

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, 200)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def reregister(self, request: Request) -> Response:
        """Unregisters device with google cloud platform."""
        self.logger.debug("Re-registering device with iot cloud")

        # Get iot manager
        app_config = apps.get_app_config(APP_NAME)
        iot_manager = app_config.coordinator.iot

        # Unregister device
        message, status = iot_manager.reregister()

        # Build response
        response = {"message": message}

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)
Пример #3
0
    def __init__(
        self,
        name: str,
        state: State,
        config: Dict,
        i2c_lock: threading.RLock,
        simulate: bool = False,
        mux_simulator: MuxSimulator = None,
    ) -> None:
        """Initializes manager."""

        # Initialize parent class
        super().__init__()

        # Initialize passed in parameters
        self.name = name
        self.state = state
        self.config = config
        self.i2c_lock = i2c_lock
        self.simulate = simulate
        self.mux_simulator = mux_simulator

        # Initialize logger
        logname = "Manager({})".format(self.name)
        self.logger = logger.Logger(logname, "peripherals")

        # Load config parameters
        self.parameters = self.config.get("parameters", {})
        self.variables = self.parameters.get("variables", {})
        self.communication = self.parameters.get("communication", {})

        # Enfore communication to be empty dict if none
        if self.communication == None:
            self.communication = {}

        # Get standard i2c config parameters if they exist
        self.bus = self.communication.get("bus")
        self.address = self.communication.get("address")
        self.mux = self.communication.get("mux")
        self.channel = self.communication.get("channel")

        # Check if using default bus
        if self.bus == "default":
            self.logger.debug("Using default i2c bus")
            self.bus = os.getenv("DEFAULT_I2C_BUS")

        # Convert exported value from non-pythonic none to pythonic None
        if self.bus == "none":
            self.bus = None

        if self.bus != None:
            self.bus = int(self.bus)  # type: ignore

        # Check if using default mux
        if self.mux == "default":
            self.logger.debug("mux is default")
            self.mux = os.getenv("DEFAULT_MUX_ADDRESS")

        # Convert exported value from non-pythonic none to pythonic None
        if self.mux == "none":
            self.mux = None
        self.logger.debug("mux = {}".format(self.mux))

        # Convert i2c config params from hex to int if they exist
        if self.address != None:
            self.address = int(self.address, 16)
        if self.mux != None:
            self.mux = int(self.mux, 16)

        # Load setup dict and uuid
        self.setup_dict = self.load_setup_dict_from_file()
        self.setup_uuid = self.setup_dict.get("uuid", None)

        # Pull out setup properties if they exist
        self.properties = self.setup_dict.get("properties", {})

        # Initialize state machine transitions
        self.transitions = {
            modes.INIT: [modes.SETUP, modes.ERROR, modes.SHUTDOWN],
            modes.SETUP: [modes.NORMAL, modes.ERROR, modes.SHUTDOWN],
            modes.NORMAL: [
                modes.CALIBRATE,
                modes.MANUAL,
                modes.RESET,
                modes.ERROR,
                modes.SHUTDOWN,
            ],
            modes.MANUAL: [
                modes.NORMAL,
                modes.CALIBRATE,
                modes.RESET,
                modes.ERROR,
                modes.SHUTDOWN,
            ],
            modes.CALIBRATE: [modes.RESET, modes.ERROR, modes.SHUTDOWN],
            modes.RESET: [modes.INIT, modes.ERROR, modes.SHUTDOWN],
            modes.ERROR: [modes.RESET, modes.SHUTDOWN],
        }

        # Initialize state machine mode
        self.mode = modes.INIT
Пример #4
0
class RecipeViewSet(viewsets.ModelViewSet):
    """View set for recipe interactions."""

    # Initialize view set parameters
    serializer_class = serializers.RecipeSerializer
    lookup_field = "uuid"
    permission_classes = [IsAuthenticated]

    # Initialize logger
    logger = logger.Logger("RecipeViewSet", "app")

    def get_queryset(self) -> QuerySet:
        """Gets recipe queryset."""
        queryset = models.RecipeModel.objects.all().order_by("name")
        return queryset

    def create(self, request: Request) -> Response:
        """Creates a recipe."""
        self.logger.debug("Creating recipe")

        # Initialize permission classes
        permission_classes = [IsAuthenticated, IsAdminUser]

        # Get recipe json
        try:
            request_dict = request.data.dict()
            recipe_json = request_dict["json"]
        except KeyError as e:
            message = "Unable to create recipe, {} is required".format(e)
            return Response({"message": message}, 400)

        # Get recipe manager
        app_config = apps.get_app_config(APP_NAME)
        recipe_manager = app_config.coordinator.recipe

        # Create recipe
        message, status = recipe_manager.create_recipe(recipe_json)

        # Build response
        response = {"message": message}

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)

    @detail_route(methods=["post"], permission_classes=[IsAuthenticated, IsAdminUser])
    def start(self, request: Request, uuid: str) -> Response:
        """Starts a recipe."""
        self.logger.debug("Starting recipe")
        self.logger.debug("request type = {}".format(type(request)))

        # Get optional timestamp parameter
        request_dict = request.data.dict()
        timestamp_ = request_dict.get("timestamp")

        # Ensure timestamp format
        if timestamp_ != None and timestamp_ != "":
            timestamp = float(timestamp_)
        else:
            timestamp = None  # type: ignore

        # Get recipe manager
        app_config = apps.get_app_config(APP_NAME)
        recipe_manager = app_config.coordinator.recipe

        # Start recipe
        message, status = recipe_manager.start_recipe(uuid, timestamp)

        # Build response
        response = {"message": message}

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        res = Response(response, status)
        self.logger.debug("res type = {}".format(type(res)))
        return Response(response, status)

    @list_route(methods=["post"], permission_classes=[IsAuthenticated, IsAdminUser])
    def stop(self, request: Request) -> Response:
        """Stops a recipe."""
        self.logger.debug("Stopping recipe")

        # Get recipe manager
        app_config = apps.get_app_config(APP_NAME)
        recipe_manager = app_config.coordinator.recipe

        # Stop recipe
        message, status = recipe_manager.stop_recipe()

        # Build response
        response = {"message": message}

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)
Пример #5
0
class NetworkViewSet(viewsets.ModelViewSet):
    """View set for network interactions."""

    # Initialize logger
    logger = logger.Logger("NetworkViewSet", "app")

    @list_route(methods=["GET"], permission_classes=[IsAuthenticated, IsAdminUser])
    def info(self, request: Request) -> Response:
        """Gets network info."""
        self.logger.debug("Getting network info")

        # Get network manager
        app_config = apps.get_app_config(APP_NAME)
        network_manager = app_config.coordinator.network

        # Build response
        response = {
            "message": "Successfully got network info",
            "is_connected": network_manager.is_connected,
            "ip_address": network_manager.ip_address,
            "wifi_ssids": network_manager.wifi_ssids,
        }

        # Return response
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, 200)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def joinwifi(self, request: Request) -> Response:
        """Joins wifi network."""
        self.logger.debug("Joining wifi")

        # Get network manager
        app_config = apps.get_app_config(APP_NAME)
        network_manager = app_config.coordinator.network

        # Join wifi
        try:
            message, status = network_manager.join_wifi(request.data.dict())
        except Exception as e:
            message = "Unable to join wifi, unhandled exception: `{}`".format(type(e))
            self.logger.exception(message)
            status = 500

        # Build and return response
        response = {"message": message}
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def joinwifiadvanced(self, request: Request) -> Response:
        """Joins wifi network with advanced config."""
        self.logger.debug("Joining wifi advanced")

        # Get network manager
        app_config = apps.get_app_config(APP_NAME)
        network_manager = app_config.coordinator.network

        # Join wifi advanced
        try:
            message, status = network_manager.join_wifi_advanced(request.data.dict())
        except Exception as e:
            message = "Unable to join wifi advanced, unhandled "
            "exception: `{}`".format(type(e))
            self.logger.exception(message)
            status = 500

        # Build and return response
        response = {"message": message}
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def deletewifis(self, request: Request) -> Response:
        """Deletes stored wifi access points."""
        self.logger.debug("Deleting wifi access points")

        # Get network manager
        app_config = apps.get_app_config(APP_NAME)
        network_manager = app_config.coordinator.network

        # Delete wifis
        try:
            message, status = network_manager.delete_wifis()
        except Exception as e:
            message = "Unable to delete wifis, unhandled "
            "exception: `{}`".format(type(e))
            status = 500

        # Build and return response
        response = {"message": message}
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)

    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def disableraspiaccesspoint(self, request: Request) -> Response:
        """Disables raspberry pi access point."""
        self.logger.debug("Disabling raspberry pi access point")

        # Get network manager
        app_config = apps.get_app_config(APP_NAME)
        network_manager = app_config.coordinator.network
        iot_manager = app_config.coordinator.iot

        # Disable raspi access point
        try:
            message, status = network_manager.disable_raspi_access_point()
        except Exception as e:
            message = "Unable to disable raspi access point, unhandled exception: `{}`".format(
                type(e)
            )
            self.logger.exception(message)
            status = 500

        # Build and return response
        response = {"message": message}
        self.logger.debug("Returning response: {}".format(response))
        return Response(response, status)
Пример #6
0
class Logs(views.APIView):
    """Logs view page."""

    # Initialize view parameters
    renderer_classes = [TemplateHTMLRenderer]
    template_name = "logs.html"

    # Initialize logger
    logger = logger.Logger("LogsView", "app")

    @method_decorator(login_required)
    def get(self, request: Request) -> Response:
        """Gets logs view."""
        self.logger.debug("Getting logs view")

        # TODO: Clean up the method / make more DRY

        # Load device config
        if os.path.exists(DEVICE_CONFIG_PATH):
            with open(DEVICE_CONFIG_PATH) as f:
                config_name = f.readline().strip()
        else:
            config_name = "unspecified"
        device_config = json.load(open("data/devices/{}.json".format(config_name)))

        # Build peripheral logs
        logs = []
        peripherals = device_config.get("peripherals", {})
        if peripherals == None:
            peripherals = {}
        for peripheral in peripherals:

            # Get log file path
            name = peripheral["name"]
            PERIPHERAL_PATH = LOG_DIR + "peripherals/{}.log".format(name)

            # Check path exists
            if not os.path.exists(PERIPHERAL_PATH):
                continue

            # Load in log file
            log_file = open(PERIPHERAL_PATH)
            lines = log_file.readlines()

            # Return up to 500 lines
            if len(lines) < 500:
                entries = lines
            else:
                entries = lines[-500:]

            # Append to log
            logs.append({"name": name, "entries": entries})

        # Build controller logs
        controllers = device_config.get("controllers", {})
        if controllers == None:
            controllers = {}
        for controller in controllers:

            # Get log file path
            name = controller["name"]
            CONTROLLER_PATH = LOG_DIR + "controllers/{}.log".format(name)

            # Check path exists
            if not os.path.exists(CONTROLLER_PATH):
                continue

            # Load in log file
            log_file = open(CONTROLLER_PATH)
            lines = log_file.readlines()

            # Return up to 500 lines
            if len(lines) < 500:
                entries = lines
            else:
                entries = lines[-500:]

            # Append to log
            logs.append({"name": name, "entries": entries})

        # Build device top-level logs
        log_filenames = [
            "app",
            "device",
            "coordinator",
            "recipe",
            "resource",
            "iot",
            "i2c",
            "connect",
            "upgrade",
        ]

        # Load in all log files
        for name in log_filenames:

            # Get log file path
            LOG_PATH = LOG_DIR + name + ".log"

            # Check path exists
            if not os.path.exists(LOG_PATH):
                continue

            # Load log filenames
            file = open(LOG_PATH)
            lines = file.readlines()

            # Return up to 500 lines
            if len(lines) < 500:
                entries = lines
            else:
                entries = lines[-500:]

            # Append to log
            logs.append({"name": name.capitalize(), "entries": entries})

        # Return response
        return Response({"serial_number": os.getenv("SERIAL_NUMBER"),
                         "logs": logs, 
                         "logs_json": json.dumps(logs)})
Пример #7
0
class LEDViewSet(viewsets.ModelViewSet):
    # Initialize logger
    logger = logger.Logger("LEDViewSet", "app")

    # --------------------------------------------------------------------------
    def send_event(self, etype: str, value: str = None) -> Response:
        v = ""
        if value is not None and value is not "":
            v = ',"value":"' + value + '"'

        edict = {
            "recipient": '{"type":"Peripheral","name":"LEDPanel-Top"}',
            "request": '{"type":"' + etype + '"' + v + "}",
        }

        event_viewer = viewers.EventViewer()
        message, status = event_viewer.create(edict)

        response = {"message": message}
        return Response(response, status=status)

    # --------------------------------------------------------------------------
    # Send LED peripheral the manual mode command
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def manual(self, request: Request) -> Response:
        self.logger.debug("Put LED in manual mode via REST API.")
        return self.send_event(etype="Enable Manual Mode")

    # --------------------------------------------------------------------------
    # Send LED peripheral the reset event
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def reset(self, request: Request) -> Response:
        self.logger.debug("Reset LED.")
        return self.send_event(etype="Reset")

    # --------------------------------------------------------------------------
    # Send LED peripheral the set channel event
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def set_channel(self, request: Request) -> Response:

        # Get parameters
        try:
            rdict = request.data
        except Exception as e:
            message = "Unable to create request dict: {}".format(e)
            return Response(message, 400)
        self.logger.debug("debubrob rdict={}".format(rdict))

        channel = rdict.get("channel", "B")
        percent = rdict.get("percent", "50")
        self.logger.debug(
            "Set LED channel via REST API, channel={}, percent={}".format(
                channel, percent
            )
        )
        # set channel event: B,50
        val = "{},{}".format(channel, percent)
        return self.send_event(etype="Set Channel", value=val)

    # --------------------------------------------------------------------------
    # Send LED peripheral the Turn On event
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def turn_on(self, request: Request) -> Response:
        self.logger.debug("Turn On LED.")
        return self.send_event(etype="Turn On")

    # --------------------------------------------------------------------------
    # Send LED peripheral the Turn Off event
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def turn_off(self, request: Request) -> Response:
        self.logger.debug("Turn Off LED.")
        return self.send_event(etype="Turn Off")

    # --------------------------------------------------------------------------
    # Send LED peripheral the fade event
    @list_route(methods=["POST"], permission_classes=[IsAuthenticated, IsAdminUser])
    def fade(self, request: Request) -> Response:
        self.logger.debug("Fade LED.")
        return self.send_event(etype="Fade")
class EnvironmentViewer:
    """Viewer for environment info."""

    # Initialize logger
    logger = logger.Logger("EnvironmentViewer", "app")

    def __init__(self) -> None:
        """Initialize viewer."""
        self.sensor_summary = self.get_environment_summary("sensor")
        self.actuator_summary = self.get_environment_summary("actuator")

    def get_environment_summary(self, peripheral_type: str) -> Dict[str, str]:
        """Gets environment summary of current reported --> desired value for each 
        variable in shared state."""
        self.logger.debug("Getting environment summary")

        # Get coordinator manager
        app_config = apps.get_app_config(APP_NAME)
        coordinator = app_config.coordinator

        # Get environment state dict
        environment_dict = coordinator.state.environment
        if environment_dict == None:
            return {}

        # Initialize summary dict
        summary = {}

        # Log all variables in reported values
        for variable in environment_dict[peripheral_type]["reported"]:
            # Get peripheral info
            if peripheral_type == "sensor":
                info = get_sensor_variable_info(variable)
            elif peripheral_type == "actuator":
                info = get_actuator_variable_info(variable)
            else:
                raise ValueError(
                    "`peripheral_type` must be either `sensor` or `actuator`"
                )

            if info is None or info == {}:
                continue

            # Get peripheral name and unit
            name = info["name"]["verbose"]
            unit = info["unit"]["brief"]

            # Get reported and desired values
            reported = str(environment_dict[peripheral_type]["reported"][variable])
            if variable in environment_dict[peripheral_type]["desired"]:
                desired = str(environment_dict[peripheral_type]["desired"][variable])
            else:
                desired = "None"

            name_string = name + " (" + unit + ")"
            summary[name_string] = reported + " --> " + desired

        # Log remaining variables in desired
        for variable in environment_dict[peripheral_type]["desired"]:

            # Skip over repeated variables
            if variable in environment_dict[peripheral_type]["reported"]:
                continue

            # Get peripheral info
            if peripheral_type == "sensor":
                info = get_sensor_variable_info(variable)
            elif peripheral_type == "actuator":
                info = get_actuator_variable_info(variable)
            else:
                raise ValueError(
                    "`peripheral_type` must be either `sensor` or `actuator`"
                )

            if info is None:
                continue

            # Get peripheral name and unit
            name = info["name"]["verbose"]
            unit = info["unit"]["brief"]

            # Get reported and desired values
            desired = str(environment_dict[peripheral_type]["desired"][variable])
            reported = "None"
            name_string = name + " (" + unit + ")"
            summary[name_string] = reported + " --> " + desired

        return summary
Пример #9
0
    def __init__(
        self,
        name: str,
        config: Dict[str, Any],
        i2c_lock: threading.RLock,
        simulate: bool = False,
        mux_simulator: Optional[MuxSimulator] = None
    ) -> None:
        """Initializes panel."""

        # Initialize panel parameters
        self.name = name
        self.bus = config.get("bus")
        self.mux = config.get("mux")
        self.active_high = config.get("is_active_high")
        self.address = config.get("address")
        self.port = config.get("port")
        # self.fan_pin = config.get("fan_pin")
        # self.fan_pin_roots = config.get("fan_pin_roots")
        # self.humidifier_pin = config.get("humidifier_pin")
        # self.heater_pin = config.get("heater_pin")
        self.peltier_pin = config.get("pin")
        self.i2c_lock = i2c_lock
        self.simulate = simulate
        self.mux_simulator = mux_simulator

        # Initialize logger
        logname = "Driver({})".format(name)
        self.logger = logger.Logger(logname, "peripherals")

        # Check if using default bus
        if self.bus == "default":
            self.logger.debug("Using default i2c bus")
            self.bus = os.getenv("DEFAULT_I2C_BUS")

        # Convert exported value from non-pythonic none to pythonic None
        if self.bus == "none":
            self.bus = None

        if self.bus != None:
            self.bus = int(self.bus)

        # Check if using default mux
        if self.mux == "default":
            self.logger.debug("mux is default")
            self.mux = os.getenv("DEFAULT_MUX_ADDRESS")

        # Convert exported value from non-pythonic none to pythonic None
        if self.mux == "none":
            self.mux = None
        self.logger.debug("mux = {}".format(self.mux))

        # Convert i2c config params from hex to int if they exist
        if self.address != None:
            self.address = int(self.address, 16)
        if self.mux != None:
            self.mux = int(self.mux, 16)

        # if self.fan_pin != None:
        #     self.fan_pin = int(self.fan_pin)

        # if self.fan_pin_roots != None:
        #     self.fan_pin_roots = int(self.fan_pin_roots)

        # if self.humidifier_pin != None:
        #     self.humidifier_pin = int(self.humidifier_pin)

        # if self.heater_pin != None:
        #     self.heater_pin = int(self.heater_pin)    

        if self.peltier_pin != None:
            self.peltier_pin = int(self.peltier_pin)
Пример #10
0
    def __init__(self, state: State, recipe: RecipeManager) -> None:
        """Initializes iot manager."""

        # Initialize parent class
        super().__init__()

        # Initialize parameters
        self.state = state
        self.recipe = recipe

        # Initialize logger
        self.logger = logger.Logger("IotManager", "iot")
        self.logger.debug("Initializing manager")

        # Initialize our state variables
        self.received_message_count = 0
        self.published_message_count = 0

        # Initialize device info
        self.device_id = registration.device_id()

        # Initialize topics
        self.config_topic = "/devices/{}/config".format(self.device_id)
        self.command_topic = "/devices/{}/commands".format(self.device_id)
        self.telemetry_topic = "/devices/{}/events".format(self.device_id)

        # Initialize pubsub handler
        self.pubsub = PubSub(
            ref_self=self,
            on_connect=on_connect,
            on_disconnect=on_disconnect,
            on_publish=on_publish,
            on_message=on_message,
            on_subscribe=on_subscribe,
            on_log=on_log,
        )

        # Initialize state machine transitions
        self.transitions: Dict[str, List[str]] = {
            modes.INIT: [
                modes.CONNECTED,
                modes.DISCONNECTED,
                modes.ERROR,
                modes.SHUTDOWN,
            ],
            modes.CONNECTED: [
                modes.INIT,
                modes.DISCONNECTED,
                modes.ERROR,
                modes.SHUTDOWN,
            ],
            modes.DISCONNECTED: [
                modes.INIT,
                modes.CONNECTED,
                modes.SHUTDOWN,
                modes.ERROR,
            ],
            modes.ERROR: [modes.SHUTDOWN],
        }

        # Initialize state machine mode
        self.mode = modes.INIT

        # Initialize recipe modes
        self.previous_recipe_mode = recipe_modes.NORECIPE