def pack_pybytes_message(self, command, pin, value): print_debug( 5, "This is pack_pybytes_message({}, {}, {})".format( command, pin, value)) body = struct.pack(constants.__PYBYTES_INTERNAL_PROTOCOL, command, pin, value) return self.__pack_message(constants.__TYPE_PYBYTES, body)
def __process_cli_activation(self, filename, activation_token): try: if not self.__pybytes_cli_activation.status_code == 200: print_debug( 3, 'Activation request returned {} with text: "{}".'.format( self.__pybytes_cli_activation.status_code, self.__pybytes_cli_activation.text)) else: print_debug( 99, 'Activation response:\n{}'.format( self.__pybytes_cli_activation.json())) if self.__process_sigfox_registration(activation_token): if self.__process_config(filename, self.__generate_cli_config()): try: self.__pybytes_cli_activation.close() except: pass return self.__pybytes_config else: try: self.__pybytes_cli_activation.close() except: pass print('Unable to provision Sigfox! Please try again.') return None except Exception as e: print('Exception during WiFi cli activation!') print('{}'.format(e)) return None
def update_network_config(self, letResp, fcota, config): try: if 'networkConfig' in letResp: netConf = letResp['networkConfig'] config['network_preferences'] = netConf['networkPreferences'] if 'wifi' in netConf: config['wifi'] = netConf['wifi'] elif 'wifi' in config: del config['wifi'] if 'lte' in netConf: config['lte'] = netConf['lte'] elif 'lte' in config: del config['lte'] if 'lora' in netConf: config['lora'] = { 'otaa': netConf['lora']['otaa'], 'abp': netConf['lora']['abp'] } elif 'lora' in config: del config['lora'] json_string = ujson.dumps(config) print_debug(1, "update_network_config : {}".format(json_string)) fcota.update_file_content('/flash/pybytes_config.json', json_string) except Exception as e: print_debug( 1, "error while updating network config pybytes_config.json! {}". format(e))
def __process_activation(self, filename): try: if not self.__pybytes_activation.status_code == 200: print_debug( 3, 'Activation request returned {}. Trying again in 10 seconds...' .format(self.__pybytes_activation.status_code)) self.__pybytes_activation.close() return False else: self.__activation2config() self.__pybytes_activation.close() print_debug( 2, 'Checking and writing configuration in __process_activation' ) if self.__check_config() and self.__write_config(filename): return True return False except Exception as e: print( 'Exception during WiFi esp-touch activation!\nPlease wait, retrying to activate your device...' ) print('{}'.format(e)) return False
def __convert_legacy_config(self, filename): try: from config import config as pybytes_legacy_config if pybytes_legacy_config.get( 'username') is None or pybytes_legacy_config.get( 'device_id') is None or pybytes_legacy_config.get( 'server') is None or pybytes_legacy_config.get( 'network_preferences') is None: print( "This config.py does not look like a Pybytes configuration... skipping..." ) del pybytes_legacy_config raise ValueError() else: self.__pybytes_config.update(pybytes_legacy_config) del pybytes_legacy_config print_debug( 2, 'Checking and writing configuration in __convert_legacy_config' ) if self.__write_config(filename): self.__pybytes_config[ 'cfg_msg'] = 'Configuration successfully converted from config.py to {}'.format( filename) self.__force_update = False else: print("Error saving configuration in new json format!") except: self.__force_update = True
def __activation2config(self): try: self.__pybytes_config = { 'username': self.__pybytes_activation.json().get( 'username'), # Pybytes username 'device_id': self.__pybytes_activation.json().get( 'deviceToken'), # device token 'server': 'mqtt.{}'.format(constants.__DEFAULT_DOMAIN), 'network_preferences': ['wifi'], # ordered list, first working network used 'wifi': { 'ssid': pycom.wifi_ssid_sta(), 'password': pycom.wifi_pwd_sta() }, 'ota_server': { 'domain': constants.__DEFAULT_SW_HOST, 'port': 443 }, 'pybytes_autostart': True, 'wlan_antenna': 0, 'ssl': True, 'dump_ca': True } except Exception as e: print_debug(2, 'Exception in __activation2config\n{}'.format(e))
def get_file_size(self, path): print_debug(2, 'FCOTA getting file infos [{}]'.format(path)) if '.' in path: fileInfo = os.stat(path) print_debug(2, 'printing fileInfo tupple: ' + str(fileInfo)) return self.convert_bytes(fileInfo[6]) return 'Unknown'
def pack_info_message(self, releaseVersion=None): print_debug(5, "This is pack_info_message()") body = bytearray() sysname = os.uname().sysname if (sysname == 'WiPy'): body.append(constants.__DEVICE_TYPE_WIPY_2) elif (sysname == 'LoPy'): body.append(constants.__DEVICE_TYPE_LOPY) elif (sysname == 'SiPy'): body.append(constants.__DEVICE_TYPE_SIPY) elif (sysname == 'LoPy4'): body.append(constants.__DEVICE_TYPE_LOPY_4) else: body.append(constants.__DEVICE_TYPE_UNKNOWN) release = self.__calc_int_version(os.uname().release) body.append((release >> 24) & 0xFF) body.append((release >> 16) & 0xFF) body.append((release >> 8) & 0xFF) body.append(release & 0xFF) if releaseVersion is not None: body.append((releaseVersion >> 8) & 0xFF) body.append(releaseVersion & 0xFF) return self.__pack_message(constants.__TYPE_INFO, body)
def enable_lte(self, carrier=None, cid=None, band=None, apn=None, type=None, reset=None, fallback=False): nwpref = None self.__check_init() self.set_config('lte', { "carrier": carrier, "cid": cid, "band": band, "apn": apn, "type": type, "reset": reset }, permanent=False) if fallback: nwpref = self.__conf.get('network_preferences', []) nwpref.extend(['lte']) else: nwpref = ['lte'] nwpref.extend(self.__conf.get('network_preferences', [])) print_debug(1, 'nwpref: {}'.format(nwpref)) if nwpref is not None: self.set_config('network_preferences', nwpref, reconnect=True)
def pack_pybytes_message_variable(self, command, pin, parameters): print_debug( 5, "This is pack_pybytes_message_variable({}, {}, {})".format( command, pin, parameters)) body = struct.pack( constants.__PYBYTES_INTERNAL_PROTOCOL_VARIABLE % len(parameters), command, pin, parameters) return self.__pack_message(constants.__TYPE_PYBYTES, body)
def __start_recv_mqtt(self): print_debug(5, "This is PybytesProtocol.__start_recv_mqtt()") self.__pybytes_connection.__connection.set_callback(self.__recv_mqtt) self.__pybytes_connection.__connection.subscribe(self.__mqtt_download_topic) print_debug(2, 'Using {} bytes as stack size'.format(self.__thread_stack_size)) _thread.stack_size(self.__thread_stack_size) _thread.start_new_thread(self.__check_mqtt_message, ()) self.__connectionAlarm = Timer.Alarm(self.__keep_connection, constants.__KEEP_ALIVE_PING_INTERVAL, periodic=True)
def __check_cb_config(self, config): try: print_debug(99, self.__pybytes_config) return (len(config['userId']) > 4 and len(config['device_id']) >= 36 and len(config['server']) > 4) except Exception as e: print_debug(4, 'Exception in __check_cb_config!\n{}'.format(e)) return False
def __check_dump_ca(self): ssl_params = self.__conf.get('ssl_params', {'ca_certs': '/flash/cert/pycom-ca.pem'}) print_debug(4, ' ssl_params={} '.format(ssl_params)) if self.__conf.get('dump_ca', False): try: stat = os.stat(ssl_params.get('ca_certs')) # noqa except: self.dump_ca(ssl_params.get('ca_certs'))
def __check_config(self): try: print_debug(99, self.__conf) return (len(self.__conf.get('username', '')) > 4 and len(self.__conf.get('device_id', '')) >= 36 and len(self.__conf.get('server', '')) > 4) except Exception as e: print_debug(4, 'Exception in __check_config!\n{}'.format(e)) return False
def __check_mqtt_message(self): print_debug(5, "This is PybytesProtocol.__check_mqtt_message()") while self.__wifi_or_lte_connection(): try: self.__pybytes_connection.__connection.check_msg() time.sleep(self.__mqtt_check_interval) except Exception as ex: print("Error receiving MQTT. Ignore this message if you disconnected") print_debug(2, "Exception: {}".format(ex)) sys.print_exception(ex)
def connect(self): try: lora_joining_timeout = 120 # seconds to wait for LoRa joining if self.__config_updated: if self.__check_config(): self.__create_pybytes_connection(self.__conf) self.__config_updated = False self.__check_init() if not self.__conf.get('network_preferences'): print( "network_preferences are empty, set it up in /flash/pybytes_config.json first" ) # noqa for net in self.__conf['network_preferences']: print_debug( 3, 'Attempting to connect with network {}'.format(net)) if net == 'lte' or net == 'nbiot': if self.connect_lte(): break elif net == 'wifi': if self.connect_wifi(): break elif net == 'lora_abp': if self.connect_lora_abp(lora_joining_timeout): break elif net == 'lora_otaa': if self.connect_lora_otaa(lora_joining_timeout): break elif net == 'sigfox': if self.connect_sigfox(): break import time time.sleep(.1) if self.is_connected(): if self.__frozen: print( 'Pybytes connected successfully (using the built-in pybytes library)' ) # noqa else: print( 'Pybytes connected successfully (using a local pybytes library)' ) # noqa # SEND DEVICE'S INFORMATION self.send_info_message() # ENABLE TERMINAL self.enable_terminal() else: print('ERROR! Could not connect to Pybytes!') except Exception as ex: print("Unable to connect to Pybytes: {}".format(ex))
def __write_config(self, filename='/flash/pybytes_config.json'): print_debug(2, 'Writing configuration to {}'.format(filename)) try: cf = open(filename, 'w') cf.write(json.dumps(self.__pybytes_config)) cf.close() return True except Exception as e: print("Error saving configuration in json format!") print("Exception: {}".format(e)) return False
def __check_lora_messages(self): print_debug(5, "This is PybytesProtocol.__check_lora_messages()") while(True): message = None with self.__pybytes_connection.lora_lock: try: self.__pybytes_connection.__lora_socket.setblocking(False) message = self.__pybytes_connection.__lora_socket.recv(256) except Exception as ex: print_debug(5, "Exception in PybytesProtocol.__check_lora_messages: {}".format(ex)) if (message): self.__process_recv_message(message) time.sleep(0.5)
def __cli_activation_over_wifi(self, activation_info): print('Please wait while we try to connect to {}'.format( activation_info.get('s'))) from network import WLAN wlan = WLAN(mode=WLAN.STA) attempt = 0 known_nets = [((activation_info['s'], activation_info['p']))] # noqa print_debug(3, 'WLAN connected? {}'.format(wlan.isconnected())) while not wlan.isconnected() and attempt < 10: attempt += 1 print_debug(3, "Wifi connection attempt: {}".format(attempt)) print_debug(3, 'WLAN connected? {}'.format(wlan.isconnected())) available_nets = None while available_nets is None: try: available_nets = wlan.scan() for x in available_nets: print_debug(5, x) time.sleep(3) except: pass nets = frozenset([e.ssid for e in available_nets]) known_nets_names = frozenset([e[0] for e in known_nets]) net_to_use = list(nets & known_nets_names) try: net_to_use = net_to_use[0] pwd = dict(known_nets)[net_to_use] sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] # noqa print_debug( 99, "Connecting with {} and {}".format(net_to_use, pwd)) if sec == 0: wlan.connect(net_to_use, timeout=10000) else: wlan.connect(net_to_use, (sec, pwd), timeout=10000) start_time = time.time() while not wlan.isconnected(): if time.time() - start_time > timeout: raise TimeoutError( 'Timeout trying to connect via WiFi') time.sleep(0.1) except Exception as e: if str(e) == "list index out of range" and attempt == 3: print("Please review Wifi SSID and password!") wlan.deinit() return None elif attempt == 3: print("Error connecting using WIFI: %s" % e) return None
def _send_packet(self, packet): written = -1 try: if self._sock: written = self._sock.write(packet) if (written is None): written = -1 else: print_debug(2, 'Packet sent. (Length: %d)' % written) except socket.error as err: print_debug(2, 'Socket send error {0}'.format(err)) return False return True if len(packet) == written else False
def get_file_content(self, path): print_debug(2, 'FCOTA reading file [{}]'.format(path)) if '.' in path: f = open(path, 'r') content = f.read() f.close() else: content = 'folder: {}'.format(path) # print_debug(2, 'encoding content') # print_debug(2, hexlify(content)) # content = hexlify(content) return content
def _recv_callback(self, cmd, payload): msg_type = cmd & 0xF0 if msg_type == mqttConst.MSG_CONNACK: return self._parse_connack(payload) elif msg_type == mqttConst.MSG_SUBACK: return self._parse_suback(payload) elif msg_type == mqttConst.MSG_PUBACK: return self._parse_puback(payload) elif msg_type == mqttConst.MSG_PUBLISH: return self._parse_publish(cmd, payload) elif msg_type == mqttConst.MSG_UNSUBACK: return self._parse_unsuback(payload) elif msg_type == mqttConst.MSG_PINGRESP: return self._parse_pingresp() else: print_debug(2, 'Unknown message type: %d' % msg_type) return False
def __pack_message(self, message_type, body): if self.__network_type is None: print_debug(0, "Error packing message without connection") return header = 0 header = header | ( (self.__network_type << 4) & constants.__NETWORK_TYPE_MASK) header = header | (message_type & constants.__TYPE_MASK) if body is not None: print_debug( 3, '__pack_message: %s' % binascii.hexlify( struct.pack(constants.__PYBYTES_PROTOCOL % len(body), header, body))) return struct.pack(constants.__PYBYTES_PROTOCOL % len(body), header, body) return struct.pack(constants.__PYBYTES_PROTOCOL_PING, header)
def __generate_cli_config(self): pybytes_config = self.__pybytes_cli_activation.json() cli_config = { 'userId': pybytes_config.get('userId'), 'device_token': pybytes_config.get('deviceToken'), 'mqttServiceAddress': pybytes_config.get('mqttServiceAddress'), 'network_preferences': pybytes_config.get('network_preferences'), 'wifi_ssid': '', 'wifi_pwd': '', } try: cli_config.update( {'wifi_ssid': pybytes_config.get('wifi').get('ssid')}) except: print_debug( 3, '__generate_cli_config: config does not contain wifi_ssid') try: cli_config.update( {'wifi_pwd': pybytes_config.get('wifi').get('password')}) except: print_debug( 3, '__generate_cli_config: config does not contain wifi_password') try: cli_config.update({ 'carrier': pybytes_config.get('lte').get('carrier').lower(), 'apn': pybytes_config.get('lte').get('apn'), 'cid': pybytes_config.get('lte').get('cid'), 'band': pybytes_config.get('lte').get('band'), 'reset': pybytes_config.get('lte').get('reset'), 'protocol': pybytes_config.get('lte').get('protocol') }) except: print_debug( 3, '__generate_cli_config: config does not contain LTE configuration' ) try: cli_config.update({ 'extra_preferences': pybytes_config.get('extra_preferences', '') }) except: print_debug( 3, '__generate_cli_config: config does not contain extra_preferences' ) return cli_config
def publish(self, topic, msg, retain=False, qos=0, priority=False): while 1: if not self.__reconnecting: try: # Disable retain for publish by now return self.__mqtt.publish(topic, msg, qos, False, priority=priority) except OSError as e: print_debug(2, "Error publish", e) if (not self.__reconnect): raise Exception('Reconnection Disabled.') self.reconnect() raise Exception('Error publish.') else: time.sleep(10)
def unpack_message(self, message): print_debug( 5, 'This is PybytesLibrary.unpack_message(message={})'.format( message)) header, body = struct.unpack( constants.__PYBYTES_PROTOCOL % (len(message) - 1), message) print_debug(6, 'header: {} body: {}'.format(header, body)) network_type = (header & constants.__NETWORK_TYPE_MASK) >> 4 print_debug(6, 'network_type: {}'.format(network_type)) message_type = header & constants.__TYPE_MASK print_debug(6, 'message_type: {}'.format(message_type)) return network_type, message_type, body
def delete_file(self, path): print_debug(2, 'FCOTA deleting file [{}]'.format(path)) try: if ('.' in path): os.remove(path) else: targetedFiles = [] maxDepth = 0 currentHierarchy = self.get_flash_hierarchy() for elem in currentHierarchy: if path in elem: targetedFiles.append(elem) if elem.count('/') > maxDepth: maxDepth = elem.count('/') if len(targetedFiles) > 0: while maxDepth >= 0: for elem in targetedFiles: if elem.count('/') == maxDepth: if '.' in elem: os.remove(elem) else: os.rmdir(elem) maxDepth -= 1 else: print_debug(2, 'targetedFiles empty, no file to delete') return True except Exception as ex: print_debug(2, 'FCOTA file deletion failed: {}'.format(ex)) return False
def update_file_content(self, path, newContent): print_debug(2, 'Updating file [{}]'.format(path)) if '.' in path: listfDir = path.split('/') currentPath = '/' for value in listfDir: if not value: continue parentList = os.listdir(currentPath) if currentPath == '/': currentPath = "{}{}".format(currentPath, value) else: currentPath = "{}/{}".format(currentPath, value) # check if dir exists if value not in parentList: # create dir if '.' in currentPath: continue os.mkdir(currentPath) # update content f = open(path, 'w') f.write(newContent) f.close() print_debug(2, 'File updated') return True else: print_debug(2, 'Cannot write into a folder') return False
def createSocketConnection(self): self._conn_state_mutex.acquire() self._connection_state = mqttConst.STATE_CONNECTING self._conn_state_mutex.release() try: if self._sock: self._poll.unregister(self._sock) self._sock.close() self._sock = None self._sock = socket.socket() self._sock.settimeout(30) self._sock.connect( socket.getaddrinfo(self._host, self._port)[0][-1]) self._poll.register(self._sock, select.POLLIN) except socket.error as err: print_debug(2, "Socket create error: {0}".format(err)) self._conn_state_mutex.acquire() self._connection_state = mqttConst.STATE_DISCONNECTED self._conn_state_mutex.release() return False return True
def __read_activation(self): try: import urequest except: import _urequest as urequest from uhashlib import sha512 print('Wifi connection established... activating device!') self.__pybytes_activation = None data = { "deviceType": os.uname().sysname.lower(), "wirelessMac": binascii.hexlify(machine.unique_id()).upper() } try: data.update({ "activation_hash": binascii.b2a_base64( sha512( data.get("wirelessMac") + '-' + '{}'.format(pycom.wifi_ssid_sta()) + '-' + '{}'.format(pycom.wifi_pwd_sta())).digest()).decode( 'UTF-8').strip() }) except: pass time.sleep(1) try: self.__pybytes_activation = urequest.post( 'https://api.{}/esp-touch/register-device'.format( constants.__DEFAULT_DOMAIN), json=data, headers={'content-type': 'application/json'}) return True except Exception as ex: if self.__pybytes_activation is not None: self.__pybytes_activation.close() print('Failed to send activation request!') print_debug(2, ex) return False