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
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)
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
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)
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)
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)})
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
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)
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