def Run(): # ++++++++++++++++++++++++++++++++++++++++ CLI Timeout ++++++++++++++++++++++++++++++++++++++++ # 3 sec option to enter cli after timeout normal boot print() print() CLI_Timeout() print() print() print("Testing here") import machine print(machine.reset_cause()) # ++++++++++++++++++++++++++++++++++++++++ Import / Run Dobby ++++++++++++++++++++++++++++++++++++++++ try: import dobby.main except MemoryError as e: print('\n\n\n Unable to load Dobby - Not enough free memory - Free memory: ' + str(gc.mem_free()) + ' - Starting CLI\n\n\n') CLI_Run() except SyntaxError as e: print('\n\n\n Unable to load Dobby - Syntax error: ' + str(e) + ' - Starting CLI\n\n\n') CLI_Run() # except: # print('\n\n\n Unable to load Dobby Lib - Unknown Error - Starting CLI\n\n\n') # CLI_Run() # No errors on import else: # Try to load the config try: Config = DobbyConfig.Load(Config_Name = 'device', Delete_On_Error = False) except DobbyConfig.Error as e: # On error run the cli print("\n\n\n Unable to load Dobby - Missing 'device' config - Starting CLI\n\n\n") CLI_Run() # Add defaults if missing Config.update(DobbyConfig.Defaults_Device(Config)) # Check if we got all the config se need Resoult = DobbyConfig.Check_Device(Config) # If not true Resoult contains a string with the missing settings if Resoult is not True: print('\n\n\n Unable to load Dobby - Missing config setting: ' + Resoult + ' - Starting CLI\n\n\n') CLI_Run() else: # Run dobby and pass loaded config dobby.main.Run(Config)
def Run(): # Dict to hold config Config = {} # Print entry message print("\n Dobby Command Line Interface\n") # Start eternal input loop while True: # Get_CLI_Input will break true when the user has requested we boot the system # After the needed settings is entered # This function is the CLI interface printing information and writing settings # Get user entry try: User_Entry = input("Dobby CLI: ") except (EOFError, KeyboardInterrupt): # If we got interrupted exit print("\n\n Leaving CLI.\n") sys.exit() # Check if the 200 pound gorilla pressed enter without entering a command if User_Entry == "": continue # Check what we got from the user # ---------------------------------------- Help ---------------------------------------- elif User_Entry.lower() == "help": print() print("Options marked with * is required configuration") print("Avalible commands:") print() print(" System:") print( " boot - Boots the system after neede settings have been entered" ) print(" cat <path> - Prints the content of a file from path") print( " config check - Lists all missing settings needed to start Dobby" ) print( " config delete <config name or path> - Deleted the config with the given name or path" ) print(" config dir - Lists all the config files in '/conf'") print( " config list - Lists all settings needed to start Dobby and the current value" ) print( " config load <path> - Default: /conf/device.json - Loads the given config" ) print(" config save - Saves the config to file system") print(" free memory - Prints the amount of free memory") print(" json - Paste a json config string") print(" ll <path> - Default: / - Lists content of path") print( " log level <1-5> - Default: 1 - 0: Debug - 1: Info - 2: Warning - 3: Error - 4: Critical - 5: Fatal" ) print() print(" Communication:") print(" wifi ssid <ssid> *") print(" wifi password <password> *") print() print(" mqtt broker <ip address> *") print(" mqtt port <port> - Default: 1883") print(" mqtt user <username> *") print(" mqtt password <password> *") print() print() continue # ---------------------------------------- Boot ---------------------------------------- elif User_Entry.lower() == "boot": # Add default settings if needed Config.update(DobbyConfig.Defaults_Device(Config)) # Check if we got all needed settings if DobbyConfig.Check_Device(Config) == True: # Save config to fs try: DobbyConfig.Save('device.json', Config) except DobbyConfig.Error as e: print(" " + str(e)) else: # Log event print(" Base config OK. Rebooting") # Reboot the device since there is not enough memory to load dobby machine.reset() else: print(" Base config requirements not met unable to boot") continue # ---------------------------------------- cat ---------------------------------------- elif User_Entry.lower().startswith("cat ") == True: # Check if we got a config store on local fs try: f = open(User_Entry[4:]) cat_String = f.read() f.close() # No config store on fs or other error except OSError as e: print(" File: " + str[User_Entry[4:]]) continue # ---------------------------------------- Config * ---------------------------------------- elif User_Entry.lower().startswith("config ") == True: # User_Entry[7:] = remove "config " or "CONFIG " or any other combination there of User_Entry = User_Entry[7:] # ---------------------------------------- Config check ---------------------------------------- if User_Entry.lower() == "check": # Check if we got all needed settings if DobbyConfig.Check_Device(Config) == True: print(" Check passed") else: print(" Check failed") # continue so we dont trigger unknown command continue # ---------------------------------------- Config delete ---------------------------------------- elif User_Entry.lower().startswith("delete ") == True: User_Entry = User_Entry[7:] # Delete file and get resoult Resoult = DobbyConfig.Delete(User_Entry) if Resoult == True: print(" Config file deleted: " + User_Entry) else: print(" Unable to delete config file: " + User_Entry) continue # ---------------------------------------- Config dir ---------------------------------------- elif User_Entry.lower() == "dir": # for loop to print files in config dir for Entry in uos.listdir('/conf'): # print each line stripping ".json" from file name print(" ", Entry.replace(".json", "")) continue # ---------------------------------------- Config list ---------------------------------------- elif User_Entry.lower() == "list": List_String = "" # Add default if missing Config.update(DobbyConfig.Defaults_Device(Config)) # Generate string Print_String = "" for Key, Value in Config.items(): Print_String = Print_String + " " + str( Key.replace('_', " ")) + ": " + str(Value) + "\n" print("\n" + Print_String) continue # ---------------------------------------- Config load ---------------------------------------- elif User_Entry.lower().startswith("load") == True: # Load with no config specified defaulting to /conf/device.json if User_Entry.lower() == "config load": # Load the config Config = DobbyConfig.Load(Config_Name='device', Delete_On_Error=False) else: # Load the config Config = DobbyConfig.Load(Config_Name=None, Delete_On_Error=False, Full_Path=User_Entry[12:]) # Check if we cloud load the config if Config == False: Config = {} print(" Unable to load config") # Add defaults Config.update(DobbyConfig.Defaults_Device(Config)) else: # Add defaults Config.update(DobbyConfig.Defaults_Device(Config)) print(" Gonfig loaded") continue # ---------------------------------------- Config Save ---------------------------------------- elif User_Entry.lower() == "save": # Try to save config try: DobbyConfig.Save('device.json', Config) except DobbyConfig.Error as e: print(" " + str(e)) else: # Log event print(" Config saved to: /conf/device.json") # Reboot the device since there is not enough memory to load dobby machine.reset() # ---------------------------------------- Unknown command ---------------------------------------- # Remember continue above so we dont trigger this else: print("Unknown config command: " + User_Entry) # since the 200 pound gorilla entered "config " and then someting incorrect # we need to continue because it was a config command and we dont need to check for other matchers continue # ---------------------------------------- Header ---------------------------------------- elif User_Entry.lower().startswith("header ") == True: Config["Header"] = User_Entry continue # Hostname elif User_Entry.lower().startswith("hostname ") == True: Config["Hostname"] = User_Entry[9:] continue # Exit elif User_Entry.lower() == "exit": print("Quitting Dobby...") sys.exit() # free memory elif User_Entry.lower() == "free memory": print(' Free memory: ' + str(gc.mem_free())) continue # json elif User_Entry.lower() == "json": # Get json string from user json_Config = input("Please paste json config string: ") # Try to parse json try: # parse json to config dict Config = ujson.loads(json_Config) except ValueError: print() print(" Invalid json string") print() continue # Add default settings if needed Config.update(DobbyConfig.Defaults_Device(Config)) # Log Event print(" json config ok") continue # log level elif User_Entry.lower().startswith("log level ") == True: # Check if the log level is valid ## Invalid level if int(User_Entry[10:]) not in range(0, 6): print() print(" Incorrect log level: " + User_Entry[10:]) print() ## Valid level else: Config["Log_Level"] = int(User_Entry[10:]) continue # ll elif User_Entry.lower().startswith("ll") == True: ll_Resoult = "" # ll / if User_Entry.lower() == "ll": ll_Resoult = uos.listdir() # ll <path> else: # Try to list path try: ll_Resoult = uos.listdir(User_Entry[3:]) # If failed report dir missing except OSError: print() print(" No sucth dir: " + User_Entry[3:]) print() continue # Print listdir print() print(ll_Resoult) print() # continue so we dont trigger unknown command continue # ---------------------------------------- wifi ---------------------------------------- elif User_Entry.lower().startswith("wifi ") == True: # User_Entry[5:] = remove "wifi " or "WIFI " or any other combination there of # wifi SSID if User_Entry[5:].lower().startswith("ssid ") == True: Config["WiFi_SSID"] = User_Entry[10:] continue # wifi Password elif User_Entry[5:].lower().startswith("password ") == True: Config["WiFi_Password"] = User_Entry[14:] continue # ---------------------------------------- mqtt ---------------------------------------- elif User_Entry.lower().startswith("mqtt ") == True: # User_Entry[5:] = remove "mqtt " or "MQTT " or any other combination there of # MQTT broker if User_Entry[5:].lower().startswith("broker ") == True: Config["MQTT_Broker"] = User_Entry[12:] continue # MQTT Port elif User_Entry[5:].lower().startswith("port ") == True: Config["MQTT_Port"] = User_Entry[10:] continue # MQTT Username elif User_Entry[5:].lower().startswith("user ") == True: Config["MQTT_Username"] = User_Entry[10:] continue # MQTT Password elif User_Entry[5:].lower().startswith("password ") == True: Config["MQTT_Password"] = User_Entry[14:] continue # ---------------------------------------- Unknown command ---------------------------------------- # Remember continue above so we dontr trigger this else: print("Unknown command: " + User_Entry) print() print(" Exitting Dobby Command Line Interface.") print()
def __init__(self, Config): # Needed Variables ## Version Version = 300000 ### First didget = Software type 1-Production 2-Beta 3-Alpha ### Secound and third didget = Major version number ### Fourth to sixth = Minor version number ## Dict holding all configs ### Fill with config from device.json if it failes self.Config will = False self.Config = Config ## Log Queue self.Log_Queue = list() ## Variable for Pin Monitor # Remember to pass Dobby aka self self.Pin_Monitor = DobbyPinMonitor.Init(self) ## Holds all loaded modules self.Modules = {} # Holds loaded System Modules like WirePusher if enabeled self.Sys_Modules = {} # Log relies on this to check if we need to blink on errors # So needs to be set before log is used the first time self.Indicator = None # If this is set, all subscribe and publishes will be mirrored and topic replaced as folles # <System Header>/<Hostname>/ = <self.gBridge> # We have to load this before mqtt so we know if we need to mirror topics self.gBridge_Topic = self.Config.get('gBridge_Topic', None) # I havent found a good way to get hardware info. # will use machine.EXT0_WAKE in a try statement # if we get AttibuteError then we know its a ESP8266 try: if machine.EXT0_WAKE == None: pass # ESP8266 except AttributeError: self.ESP_Type = 8266 # ESP32 else: self.ESP_Type = 32 # # import and Start webrepl if esp32 # import webrepl # webrepl.start() # self.Log(0, "System/webrepl", "Starting") # Import ntp time import ntptime # List of push messages that failed to send # we retry when online again self.Push_Queue = [] # MQTT Connection status self.MQTT_State = 'init' self.MQTT_Reconnect_At = 0 self.MQTT_Subscribe_To = [] # Init message self.Log(1, 'System', 'Initializing Dobby version: ' + str(Version)) # Subscribe to Commands topic self.MQTT_Subscribe(self.Peripherals_Topic("Commands")) # Change CPU frequancy if requested if Config.get('CPU_16', False) == True: machine.freq(160000000) self.Log(1, 'System', 'CPU frequancy set to: 16MHz') # ++++++++++++++++++++++++++++++++++++++++ WiFi setup ++++++++++++++++++++++++++++++++++++++++ # Setup WiFi # ## Log event self.Log(1, 'System', 'Connecting to WiFi SSID: ' + self.Config['WiFi_SSID']) ## Disable AP self.ap0 = network.WLAN(network.AP_IF) # Check if AP is active if self.ap0.active() == True: # Disable ap if active self.ap0.active(False) ## Setup wlan0 self.wlan0 = network.WLAN(network.STA_IF) # Activate wlan0 regardless if it is no not self.wlan0.active(True) # Check if the right SSID is configured if self.wlan0.config('essid') != self.Config['WiFi_SSID']: # Disconnect from incorrect ssid self.wlan0.disconnect() # Hostname only works with version 4 + # Set wifi hostname self.wlan0.config( dhcp_hostname=str.encode(self.Config['Hostname'])) # Connect to wifi self.wlan0.connect(self.Config['WiFi_SSID'], self.Config['WiFi_Password']) else: self.Log(0, 'WiFi', 'Config ok') # Var to indicate of we have published the ip we got when wifi connected self.wlan0_Published_IP = False # ++++++++++++++++++++++++++++++++++++++++ MQTT ++++++++++++++++++++++++++++++++++++++++ # Remember to add something raondom after the hostname so the borker see a new connecton # Check if we got a user and pass for mqtt # Generate Unique Post Hostname Post_Hostname = str(os.urandom(1)[0] % 1000) # Log event self.Log( 0, 'MQTT', 'Using hostname: ' + self.Config['Hostname'] + "-" + Post_Hostname) # Stores messages so we can act on them in MQTT Loop ## List containing Topic and payload ## [[<Topic>, <Payload>]] self.MQTT_Incomming_Queue = [] # Create MQTT Client self.MQTT_Client = MQTT.MQTTClient( self.Config['Hostname'] + "-" + Post_Hostname, self.Config['MQTT_Broker'], int(self.Config.get('MQTT_Port', 1883)), self.Config.get('MQTT_Username', None), self.Config.get('MQTT_Password', None)) # Set last will self.MQTT_Client.set_last_will( self.Config['System_Header'] + "/" + self.Config['Hostname'] + "/Log/Will", "Disconnected") # try to connect to mqtt self.MQTT_Connect() # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # Loop over config names in /conf and import matching modules ## Get config names Config_List = os.listdir('/conf') # Remove device.json since we dont want to use that again Config_List.remove('device.json') # Move relay to the front of the list if present ## try to remove for Entry in ['dimmer.json', 'relay.json']: try: Config_List.remove(Entry) ## if it fails do nothing except ValueError as e: pass ## If we removed relay.json add it back at the beginning of the list else: Config_List.insert(0, Entry) ## Loop over names in config for Name in Config_List: # Import the config ## False = Config not found or error during import ## If not false the imported Module will be returned Config = DobbyConfig.Load(Config_Name=Name, Delete_On_Error=False) Error = None # Load config is False if no config is found if Config is not False: # Try to import dobbydutton Module_Name = str('dobby.' + Name.replace('.json', '')) try: Module = __import__(Module_Name.replace(".", '/')) except (AttributeError, TypeError, SyntaxError, ImportError, KeyError) as e: Error = str(e) except MemoryError as e: Error = 'Not enough free memory. Free memory: ' + str( gc.mem_free()) finally: # Check if Module import ok if Error is None: # Log event self.Log(0, "System", "Module loaded: " + str(Module_Name)) # Pass config and get perifical object # Remember to pass Dobby aka self so we can log in Button and use dobby variables try: self.Modules[Name.replace('.json', '')] = Module.Init( self, Config) except self.Module_Error as e: Error = str(e) # Log event self.Log( 4, "System", "Unable to load module: " + str(Module_Name) + " - Error: " + Error) # Remove config file os.remove("/conf/" + Name) self.Log(1, "System", "Removed config file: /conf/" + Name) else: # Log event self.Log( 4, "System", "Unable to load module: " + str(Module_Name) + " - Error: " + Error) # Remove config file os.remove("/conf/" + Name) self.Log(1, "System", "Removed config file: /conf/" + Name) else: self.Log(0, 'System', "Invalid config: /conf/" + Name + "'") # Activate indicator if 'LED' owned by Indicator aka not used for something else # on esp32 its IO2 # on esp8266 its D4 if self.Pin_Monitor.Get_Owner(self.Pin_Monitor.LED) == 'Indicator': # check if indicator is already imported and init if self.Sys_Modules.get('indicator', None) == None: # Import Indicator import dobby.indicator # Init Indicator and store in Sys_Modules to enable loop self.Sys_Modules['indicator'] = dobby.indicator.Init( self, {"System": { "Pin": "LED" }}) # Create the indicator object self.Indicator = self.Sys_Modules['indicator'].Peripherals[ "System"] # indicator already imported to add an object in stead else: # Create the indicator object self.Indicator = self.Sys_Modules['indicator'].Add( "System", {"Pin": "LED"}) # On add the indicator will be blinked 3 times # will use this as boot blink # add wifi infication if disconnected if self.wlan0.isconnected() == False: self.Indicator.Add("WiFi", 4, "0.5s", "0.75s") else: # Log we could not enable indicator led self.Log(0, 'System/Indicator', "LED Pin in use, cannot enable system indicator") # Boot done message self.Log( 0, 'System', 'Dobby - Initialization compleate - Free memory: ' + str(gc.mem_free())) # Start loop() self.Loop()
def __init__(self, Config): # Needed Variables ## Version Version = 300000 ### First didget = Software type 1-Production 2-Beta 3-Alpha ### Secound and third didget = Major version number ### Fourth to sixth = Minor version number ## Dict holding all configs ### Fill with config from device.json if it failes self.Config will = False self.Config = Config ## Log Queue self.Log_Queue = list() ## Variable for Pin Monitor # Remember to pass Dobby aka self self.Pin_Monitor = DobbyPinMonitor.Init(self) ## Holds all loaded modules self.Modules = {} # Holds loaded System Modules like WirePusher if enabeled self.Sys_Modules = {} # Log relies on this to check if we need to blink on errors # So needs to be set before log is used the first time self.IndicatorLED = None # If this is set, all subscribe and publishes will be mirrored and topic replaced as folles # <System Header>/<Hostname>/ = <self.gBridge> # We have to load this before mqtt so we know if we need to mirror topics self.gBridge_Topic = self.Config.get('gBridge_Topic', None) # MQTT Connection status self.MQTT_State = 'init' self.MQTT_Reconnect_At = 0 self.MQTT_Subscribe_To = [] # Init message self.Log(1, 'System', 'Initializing Dobby version: ' + str(Version)) # Subscribe to Commands topic self.MQTT_Subscribe(self.Peripherals_Topic("Commands")) # Change CPU frequancy if requested if Config.get('CPU_16', False) == True: machine.freq(160000000) self.Log(1, 'System', 'CPU frequancy set to: 16MHz') # ++++++++++++++++++++++++++++++++++++++++ WiFi setup ++++++++++++++++++++++++++++++++++++++++ # Setup WiFi # ## Log event self.Log(1, 'System', 'Connecting to WiFi SSID: ' + self.Config['WiFi_SSID']) ## Disable AP self.ap0 = network.WLAN(network.AP_IF) # Check if AP is active if self.ap0.active() == True: # Disable ap if active self.ap0.active(False) ## Setup wlan0 self.wlan0 = network.WLAN(network.STA_IF) # Activate wlan0 self.wlan0.active(True) # Set wifi hostname self.wlan0.config(dhcp_hostname=str(self.Config['Hostname'])) # Connect to wifi self.wlan0.connect( self.Config['WiFi_SSID'], self.Config['WiFi_Password'], ) # Check if we connected if self.wlan0.isconnected() == True: # Log ip self.Log(0, 'WiFi', 'Got IP: ' + str(self.wlan0.ifconfig()[0])) else: # Log ip self.Log(0, 'WiFi', "Not connected") # ++++++++++++++++++++++++++++++++++++++++ MQTT ++++++++++++++++++++++++++++++++++++++++ # Remember to add something raondom after the hostname so the borker see a new connecton # Check if we got a user and pass for mqtt # Generate Unique Post Hostname Post_Hostname = str(os.urandom(1)[0] % 1000) # Log event self.Log( 0, 'MQTT', 'Using hostname: ' + self.Config['Hostname'] + "-" + Post_Hostname) # Stores messages so we can act on them in MQTT Loop ## List containing Topic and payload ## [[<Topic>, <Payload>]] self.MQTT_Incomming_Queue = [] # Create MQTT Client self.MQTT_Client = MQTT.MQTTClient( self.Config['Hostname'] + "-" + Post_Hostname, self.Config['MQTT_Broker'], int(self.Config.get('MQTT_Port', 1883)), self.Config.get('MQTT_Username', None), self.Config.get('MQTT_Password', None)) # Set last will self.MQTT_Client.set_last_will( self.Config['System_Header'] + "/" + self.Config['Hostname'] + "/Log/Will", "Disconnected") # try to connect to mqtt self.MQTT_Connect() # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++++++++++++++++++++++ Setup peripherals ++++++++++++++++++++++++++++++++++++++++ # Loop over config names in /conf and import matching modules ## Get config names Config_List = os.listdir('/conf') # Remove device.json since we dont want to use that again Config_List.remove('device.json') # Move relay to the front of the list if present ## try to remove for Entry in ['dimmer.json', 'relay.json']: try: Config_List.remove(Entry) ## if it fails do nothing except ValueError as e: pass ## If we removed relay.json add it back at the beginning of the list else: Config_List.insert(0, Entry) ## Loop over names in config for Name in Config_List: # Import the config ## False = Config not found or error during import ## If not false the imported Module will be returned Config = DobbyConfig.Load(Config_Name=Name, Delete_On_Error=False) Error = None # Load config is False if no config is found if Config is not False: # Try to import dobbydutton Module_Name = str('dobby.' + Name.replace('.json', '')) try: Module = __import__(Module_Name.replace(".", '/')) except (AttributeError, TypeError, SyntaxError, ImportError, KeyError) as e: Error = str(e) except MemoryError as e: Error = 'Not enough free memory. Free memory: ' + str( gc.mem_free()) finally: # Check if Module import ok if Error is None: # Log event self.Log(0, "System", "Module loaded: " + str(Module_Name)) # Pass config and get perifical object # Remember to pass Dobby aka self so we can log in Button and use dobby variables try: self.Modules[Name.replace('.json', '')] = Module.Init( self, Config) except self.Module_Error as e: Error = str(e) # Log event self.Log( 4, "System", "Unable to load module: " + str(Module_Name) + " - Error: " + Error) # Remove config file os.remove("/conf/" + Name) self.Log(1, "System", "Removed config file: /conf/" + Name) else: # Log event self.Log( 4, "System", "Unable to load module: " + str(Module_Name) + " - Error: " + Error) # Remove config file os.remove("/conf/" + Name) self.Log(1, "System", "Removed config file: /conf/" + Name) else: self.Log(0, 'System', "Invalid config: /conf/" + Name + "'") # Activate indicator led if D4 owned by IndicatorLED aka not used for something else if self.Pin_Monitor.Pins['D4']['Owner'] == 'IndicatorLED': # Import IndicatorLED import dobby.indicatorled # Init IndicatorLED and store in Sys_Modules to enable loop self.Sys_Modules['IndicatorLED'] = dobby.indicatorled.Init(self) # Do boot blink if IndicatorLED is configured self.Sys_Modules['IndicatorLED'].Enable('Booting') # Boot done message self.Log( 0, 'System', 'Dobby - Initialization compleate - Free memory: ' + str(gc.mem_free())) # Start loop() self.Loop()