def run_init_mode(self) -> None: """Runs init mode.""" self.logger.debug("Entered INIT") # Connect to pubsub service self.pubsub = PubSub( self, on_connect, on_disconnect, on_publish, on_message, on_subscribe, on_log, ) # Initialize iot connection state self.is_connected = False # Initialize registration state self.is_registered = registration.is_registered() self.device_id = registration.device_id() self.verification_code = registration.verification_code() # Check if network is connected if not self.network_is_connected: self.logger.info("Waiting for network to come online") # Loop forever while True: # Check if network is connected if self.network_is_connected: self.logger.debug("Network came online") break # Update every 100ms time.sleep(0.1) # Give the network time to initialize self.logger.debug("Waiting 30 seconds for network to initialize") time.sleep(30) # Check if device is registered if not self.is_registered: self.logger.debug("Device not registered, registering device") registration.register() # Update registation state self.is_registered = registration.is_registered() self.device_id = registration.device_id() self.verification_code = registration.verification_code() 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 client self.pubsub.initialize() # Transition to disconnected mode on next state machine update self.mode = modes.DISCONNECTED
def reregister(self) -> Tuple[str, int]: """Unregisters device by deleting iot registration data. TODO: This needs to go into the event queue, deleting reg data in middle of a registration update creates an unstable state.""" self.logger.info("Re-registering device") # Check network connection if not self.network_is_connected: return "Unable to re-register, network disconnected", 400 # Re-register device and update state try: registration.delete() registration.register() self.device_id = registration.device_id() self.verification_code = registration.verification_code() except Exception as e: message = "Unable to re-register, unhandled exception: {}".format(type(e)) self.logger.exception(message) self.mode = modes.ERROR return message, 500 # Transition to init mode on next state machine update self.mode = modes.INIT # Successfully deleted registration data return "Successfully re-registered device", 200
def load_mqtt_config(self) -> None: """Loads mqtt config.""" self.logger.debug("Loading mqtt config") # Load settings from environment variables try: self.project_id = os.environ["GCLOUD_PROJECT"] self.cloud_region = os.environ["GCLOUD_REGION"] self.registry_id = os.environ["GCLOUD_DEV_REG"] self.device_id = registration.device_id() self.private_key_filepath = os.environ["IOT_PRIVATE_KEY"] self.ca_certs = os.environ["CA_CERTS"] except KeyError as e: message = "Unable to load pubsub config, key {} is required".format(e) self.logger.critical(message) raise # Initialize client id self.client_id = "projects/{}/locations/{}/registries/{}/devices/{}".format( self.project_id, self.cloud_region, self.registry_id, self.device_id ) # Initialize config topic self.config_topic = "/devices/{}/config".format(self.device_id) # Initialize commands topic self.command_topic = "/devices/{}/commands/#".format(self.device_id) # Initialize event topic test_telemetry_topic = os.environ.get("IOT_TEST_TOPIC") if test_telemetry_topic is not None: self.telemetry_topic = "/devices/{}/{}".format(self.device_id, test_telemetry_topic) self.logger.debug("Publishing to test topic: {}".format(self.telemetry_topic)) else: self.telemetry_topic = "/devices/{}/events".format(self.device_id)
def upload_image(self, file_name: str) -> None: self.logger.debug("Uploading binary image") if not self.is_initialized: self.logger.warning("Tried to publish before client initialized") return if file_name == None or len(file_name) == 0: error_message = "Unable to publish image, file name " "`{}` is invalid".format(file_name) self.logger.error(error_message) raise ValueError(error_message) # Get the camera name and image type from the file_name: # /Users/rob/yada/yada/2019-05-08-T23-18-31Z_Camera-Top.png base = '' try: base = os.path.basename(file_name) # get just the file from path fn1 = base.split("_") # delimiter between datetime & camera name fn2 = fn1[1] # 'Camera-Top.png' fn3 = fn2.split(".") # delimiter between file and extension camera_name = fn3[0] # 'Camera-Top' except: camera_name = base device_id = registration.device_id() upload_file_name = '{}_{}'.format(device_id, base) # commented URL below is for running the firebase cloud function # service locally for testing #URL = 'http://*****:*****@{};filename={}'.format(file_name, upload_file_name) try: # Use curl to do a multi part form post of the binary data (fast) to # our firebase cloud function that puts the image in the GCP # storage bucket. res = subprocess.run(['curl', '--silent', URL, '-F', DATA]) self.logger.debug("Uploaded file: {}".format(upload_file_name)) # Publish a message indicating that we uploaded the image to the # public bucket written by the firebase cloud function, and we need # to move the image to the usual images bucket we have been using. message = { "messageType": IMAGE_MESSAGE, "varName": camera_name, "fileName": upload_file_name, } message_json = json.dumps(message) self.client.publish(self.telemetry_topic, message_json, qos=1) except Exception as e: error_message = "Unable to publish binary image, unhandled " "exception: {}".format(type(e)) self.logger.exception(error_message)
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