class Logs(object): def __init__(self): self.config = Config() def init_pubnub(func): def wrapper(self, *args, **kwargs): if not hasattr(self, 'pubnub'): pubnub_key = self.config.get_pubnub_keys() self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'], subscribe_key=pubnub_key['subscribe_key']) return func(self, *args, **kwargs) return wrapper @init_pubnub def subscribe(self, uuid, callback, error): channel = self.get_channel(uuid) self.pubnub.subscribe(channels=channel, callback=callback, error=error) @init_pubnub def history(self, uuid, callback, error): channel = self.get_channel(uuid) self.pubnub.history(channel=channel, callback=callback, error=error) def unsubscribe(self, uuid): if hasattr(self, 'pubnub'): channel = self.get_channel(uuid) self.pubnub.unsubscribe(channel=channel) @staticmethod def get_channel(uuid): return 'device-{uuid}-logs'.format(uuid=uuid)
class manageClients(Thread): test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() manageClients.deliveries_collection = mongodb_connection.getCollection( "temp_deliveries") self.pubnub_settings = Pubnub( publish_key=manageClients.pubnub_publish_key, subscribe_key=manageClients.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "clients_channel" self.genericDAO = GenericDAO() self.manageSteeds = manageSteeds() def subscriber_callback(self, message, channel): print(message) # Inserer la demande de livraison faite par le client if (message["msg_code"] == "XX"): # inserer la demande de livraison dans une collection temporaire id_delivery = self.genericDAO.insertObject( manageClients.deliveries_collection, message) def subscriber_error(self, message): print("ERROR : " + message) def connect(self, message): print("CONNECTED TO LOCATION CHANNEL") def reconnect(self, message): print("RECONNECTED TO LOCATION CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM LOCATION CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error, connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel)
class manageGPSLocation(Thread) : test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() manageGPSLocation.collection = mongodb_connection.getCollection("steeds") self.pubnub_settings = Pubnub(publish_key=manageGPSLocation.pubnub_publish_key,subscribe_key=manageGPSLocation.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "channel_test" self.genericDAO = GenericDAO() def subscriber_callback(self, message, channel): print(message) criteriaDict = {} criteriaDict["_id"]=message["_id"] updateDict = {} subUpdateDict = {} subUpdateDict["type"]="Point" subUpdateDict["coordinates"] = [message["lng"],message["lat"]] updateDict["location"]=subUpdateDict self.genericDAO.updateObjects(manageGPSLocation.collection, criteriaDict, updateDict) def subscriber_error(self, message): print("ERROR : "+message) def connect(self, message): print("CONNECTED TO LOCATION CHANNEL") def reconnect(self, message): print("RECONNECTED TO LOCATION CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM LOCATION CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel)
class manageClients(Thread) : test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() manageClients.deliveries_collection = mongodb_connection.getCollection("temp_deliveries") self.pubnub_settings = Pubnub(publish_key=manageClients.pubnub_publish_key,subscribe_key=manageClients.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "clients_channel" self.genericDAO = GenericDAO() self.manageSteeds = manageSteeds() def subscriber_callback(self, message, channel): print(message) # Inserer la demande de livraison faite par le client if(message["msg_code"] == "XX") : # inserer la demande de livraison dans une collection temporaire id_delivery = self.genericDAO.insertObject(manageClients.deliveries_collection, message) def subscriber_error(self, message): print("ERROR : "+message) def connect(self, message): print("CONNECTED TO LOCATION CHANNEL") def reconnect(self, message): print("RECONNECTED TO LOCATION CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM LOCATION CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel)
def main(): publish_key = os.environ['PUBNUB_PUBLISH_KEY'] subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] pubnub = Pubnub(publish_key, subscribe_key, ssl_on=False) try: cpu = system.SystemInfo() while True: pubnub.publish(channel="kiettv.raspberry.os", message=cpu.get_cpu_percent(), callback=None, error=callback) time.sleep(1) except KeyboardInterrupt: pubnub.unsubscribe(channel='kiettv.raspberry.os') exit(0)
class MyPubnubConn(): def __init__(self, channel): self.conn = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, ssl_on=True, # cipherkey randomly generated for purposes of PoC, but it is too short and needs to be # securely stored. # cipher_key=cipher_key ) self.channel = channel self.subscribed = threading.Event() # server doesn't need to subscribe, only publish #self.conn.subscribe(channels=self.channel, callback=self.incoming_message, error=self.error_cb, # connect=self.connect_cb, reconnect=self.reconnect_cb, disconnect=self.disconnect_cb) def incoming_message(self, message, channel): pass #print(message) def error_cb(self, message): pass #print("\tERROR : " + str(message)) def connect_cb(self, message): self.subscribed.set() def reconnect_cb(self, message): self.subscribed.set() #print("\tRECONNECTED") def disconnect_cb(self, message): self.subscribed.clear() #print("\tDISCONNECTED") def send_cb(self, message): pass def discontinue(self): self.conn.unsubscribe(channel=self.channel) def publish(self, message): self.conn.publish(channel=self.channel, message=message, callback=self.send_cb, error=self.error_cb) def wait_until_ready(self): self.subscribed.wait() def print_status(self): print("\nCurrently connected to channel '%s'" % self.channel)
class Net: def callback(self, message): print(message) def __init__(self, channel = "Channel-ux8kwdv1u"): self.channel = channel self.pubnub = Pubnub(publish_key=PUB_KEY, subscribe_key=SUB_KEY) def subscribe(self, callback): self.pubnub.subscribe(channels=self.channel, callback=callback, error=self.callback) def unsubscribe(self): self.pubnub.unsubscribe(channel=self.channel) def publish(self, message): self.pubnub.publish(channel=self.channel, message=message, callback=self.callback, error=self.callback)
class Logs(object): """ This class implements functions that allow processing logs from device. This class is implemented using pubnub python sdk. For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk """ def __init__(self): self.config = Config() def _init_pubnub(func): @wraps(func) def wrapper(self, *args, **kwargs): if not hasattr(self, 'pubnub'): pubnub_key = self.config.get_all()['pubnub'] self.pubnub = Pubnub( publish_key=pubnub_key['publish_key'], subscribe_key=pubnub_key['subscribe_key'] ) return func(self, *args, **kwargs) return wrapper @_init_pubnub def subscribe(self, uuid, callback, error): """ This function allows subscribing to device logs. Testing Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe Examples: >>> def callback(message, channel): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) >>> Logs.subscribe(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.subscribe(channels=channel, callback=callback, error=error) @_init_pubnub def history(self, uuid, callback, error): """ This function allows fetching historical device logs. Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history Examples: >>> def callback(message): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) Logs.history(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.history(channel=channel, callback=callback, error=error) def unsubscribe(self, uuid): """ This function allows unsubscribing to device logs. Args: uuid (str): device uuid. """ if hasattr(self, 'pubnub'): channel = self.get_channel(uuid) self.pubnub.unsubscribe(channel=channel) @staticmethod def get_channel(uuid): """ This function returns pubnub channel for a specific device. Args: uuid (str): device uuid. Returns: str: device channel. """ return 'device-{uuid}-logs'.format(uuid=uuid)
# mark every 10 minutes startup_time = time.time() while not has_internet(): elapsed = time.time() - startup_time if elapsed % 600 == 0: print '%s mins without internet connection...' % (elapsed/60) # connect to cloudant client client.connect() # subscribe to client channel pubnub.subscribe(channels=pubnub_channel_client, callback=callback, error=callback, connect=connect, reconnect=reconnect, disconnect=disconnect) # ensure red led is off GPIO.output(RED_LED, False) try: print('Booting system...') GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=alert) # indicate the system is ready GPIO.output(GREEN_LED, True) while True: time.sleep(100) finally: print('Shutting down...') pubnub.unsubscribe(channel=pubnub_channel_client) GPIO.cleanup()
elif message=="Turn Left": print "Turn Left" GPIO.output(dir1,1) GPIO.output(dir2,0) GPIO.output(mtr2,1) GPIO.output(mtr1,1) sleep(0.5) GPIO.output(dir1,0) GPIO.output(dir2,0) GPIO.output(mtr2,0) GPIO.output(mtr1,0) elif message=="Exit": GPIO.cleanup() pubnub.unsubscribe(channel="test") os._exit(1) def _error(message): print (message) def _reconnect(message): print (message) pubnub.subscribe(channels="test", callback=_callback, error=_error, reconnect=_reconnect) try: while 1: pass except KeyboardInterrupt: GPIO.cleanup() pubnub.unsubscribe(channel="test")
class Logs(object): """ This class implements functions that allow processing logs from device. This class is implemented using pubnub python sdk. For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk """ def __init__(self): self.config = Config() self.device = Device() def _init_pubnub(func): @wraps(func) def wrapper(self, *args, **kwargs): if not hasattr(self, 'pubnub'): pubnub_key = self.config.get_all()['pubnub'] self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'], subscribe_key=pubnub_key['subscribe_key']) return func(self, *args, **kwargs) return wrapper @_init_pubnub def subscribe(self, uuid, callback, error): """ This function allows subscribing to device logs. There are fields (`m`, `t`, `s`, `c`) in the output which can be unclear. They stand for: m - The log message itself. t - The log timestamp. s - Is this a system message? c - The id of the service which produced this log (or null if the device does not support multiple containers). Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe Examples: # Define callback and error. >>> def callback(message, channel): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) >>> Logs.subscribe(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.subscribe(channels=channel, callback=callback, error=error) @_init_pubnub def history(self, uuid, callback, error): """ This function allows fetching historical device logs. Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history Examples: # Define callback and error. >>> def callback(message): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) Logs.history(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.history(channel=channel, callback=callback, error=error) def unsubscribe(self, uuid): """ This function allows unsubscribing to device logs. Args: uuid (str): device uuid. """ if hasattr(self, 'pubnub'): channel = self.get_channel(uuid) self.pubnub.unsubscribe(channel=channel) def get_channel(self, uuid): """ This function returns pubnub channel for a specific device. Args: uuid (str): device uuid. Returns: str: device channel. """ if not hasattr(self, 'logs_channel'): device_info = self.device.get(uuid) if 'logs_channel' in device_info: self.logs_channel = device_info['logs_channel'] else: self.logs_channel = uuid return 'device-{logs_channel}-logs'.format( logs_channel=self.logs_channel)
class manageGPSLocation(Thread): test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() manageGPSLocation.collection = mongodb_connection.getCollection( "steeds") self.pubnub_settings = Pubnub( publish_key=manageGPSLocation.pubnub_publish_key, subscribe_key=manageGPSLocation.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "channel_test" self.genericDAO = GenericDAO() def subscriber_callback(self, message, channel): print(message) criteriaDict = {} criteriaDict["_id"] = message["_id"] updateDict = {} subUpdateDict = {} subUpdateDict["type"] = "Point" subUpdateDict["coordinates"] = [message["lng"], message["lat"]] updateDict["location"] = subUpdateDict self.genericDAO.updateObjects(manageGPSLocation.collection, criteriaDict, updateDict) def subscriber_error(self, message): print("ERROR : " + message) def connect(self, message): print("CONNECTED TO LOCATION CHANNEL") def reconnect(self, message): print("RECONNECTED TO LOCATION CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM LOCATION CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error, connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel)
try: pubnub.subscribe(channels=subchannel, callback=_kill, error=_error) ## Change to callback while True: previous_state = current_state current_state = GPIO.input(sensor) if current_state != previous_state: new_state = "HIGH" if current_state else "LOW" if current_state: # Motion is Detected lock.acquire() cam.start_preview() # Comment in future cam.preview_fullscreen = False cam.preview_window = (10,10, 320,240) print('Motion Detected') for i in range(imgCount): curTime = (time.strftime("%I:%M:%S")) + ".jpg" cam.capture(curTime, resize=(320,240)) t = threading.Thread(target=processImage, args = (curTime,)) t.daemon = True t.start() time.sleep(frameSleep) cam.stop_preview() lock.release() time.sleep(camSleep) except KeyboardInterrupt: cam.stop_preview() pubnub.unsubscribe(subchannel) sys.exit(0)
class AppWindow(QtGui.QMainWindow, remote.Ui_MainWindow): resSlot = QtCore.pyqtSignal(str, str) def __init__(self, parent=None, **kwargs): super(AppWindow, self).__init__(parent) self.setupUi(self) self.I = kwargs.get('I', None) self.setWindowTitle(self.I.H.version_string + ' : ' + params.get('name', '').replace('\n', ' ')) self.pubEdit.setText("pub-c-22260663-a169-4935-9c74-22925f4398af") self.subEdit.setText("sub-c-3431f4ba-2984-11e6-a01f-0619f8945a4f") self.channelLabel.setText(self.I.hexid) self.resetKeys() # Connect to pubnub self.resSlot.connect(self.writeResults) self.thingSpeakCommand = None self.timer = QtCore.QTimer() self.timer.timeout.connect(self.uploadToThingSpeak) self.uploadToThingSpeak(); self.timer.start(15 * 1e3) # 15 seconds import inspect funcs = dir(self.I) self.methods = {} self.function_list = [] for a in funcs: fn = getattr(self.I, a) try: args = inspect.getargspec(fn).args except: args = [] if len(args) > 0: if inspect.ismethod(fn): self.methods[a] = (fn, args) # list of tuples of all methods in device handler if args[0] == 'self': self.function_list.append([a, args[1:]]) def uploadToThingSpeak(self): if self.thingSpeakCommand: try: result = self.thingSpeakCommand[0](*self.thingSpeakCommand[1]) params = urllib.urlencode({'field1': float(result), 'key': str(self.thingSpeakKey.text())}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = httplib.HTTPConnection("api.thingspeak.com:80") conn.request("POST", "/update", params, headers) response = conn.getresponse() self.results_2.append('%s : %s' % (response.status, response.reason)) data = response.read() conn.close() except Exception as e: self.results_2.append('Error : %s' % (e.message)) pass def setThingSpeakCommand(self): try: message = str(self.cmdEditThingSpeak.text()) fn_name = message.split('(')[0] args = message[message.find("(") + 1:message.find(")")].strip().split(',') total_args = [] for t in args: if not len(t): continue if t[0] == "'" or t[0] == '"': total_args.append(t[1:-1]) else: total_args.append(string.atoi(t)) method = self.methods.get(fn_name)[0] if method == None: print('no such command :', fn_name) return 'no such command : %s' % fn_name else: # while self.hw_lock and self.active: pass # self.hw_lock=True self.thingSpeakCommand = [method, total_args] except Exception as e: self.results_2.append('Set Error : %s' % (e.message)) pass def callback(self, message, channel): msg_type = message[0] message = str(message)[1:] try: if msg_type == 'Q': # Query self.resSlot.emit(message, 'in') fn_name = message.split('(')[0] args = message[message.find("(") + 1:message.find(")")].strip().split(',') total_args = [] for t in args: if not len(t): continue if t[0] == "'" or t[0] == '"': total_args.append(t[1:-1]) else: total_args.append(string.atoi(t)) method = self.methods.get(fn_name)[0] if method == None: print('no such command :', fn_name) return 'no such command : %s' % fn_name else: # while self.hw_lock and self.active: pass # self.hw_lock=True result = method(*total_args) # self.hw_lock=False jsonres = json.dumps(result, cls=NumpyEncoder) self.pubnub.publish(channel=self.I.hexid, message='R' + jsonres) # R stands for response . Q for Query self.resSlot.emit('%s %s %s %d %s... %s' % ( method.__name__, str(total_args), str(type(jsonres)), len(jsonres), str(jsonres[:20]), self.I.hexid + 'response'), 'out') elif msg_type == 'R': self.resSlot.emit(str(message), 'reply') except Exception as e: self.responseLabel.setText(e.message) def writeResults(self, txt, t): if t == 'in': self.results.append('RECV:<span style="background-color: #FFFF00">' + txt + '</span') elif t == 'out': self.results.append('SEND:<span style="background-color: #00FF00">' + txt + '</span') elif t == 'reply': self.results.append('GOT :<span style="background-color: #00FFFF">' + txt + '</span') def execRemote(self): chan = hex(0x1000000000000000 | int(str(self.sendID.text()), 16)); msg = str(self.cmdEdit.text()) self.pubnub.publish(channel=chan, message='Q' + msg) self.resSlot.emit('[' + chan + ']: ' + msg, 'out') def setListenState(self, state): if state: # Enable listen try: self.pubnub.subscribe(self.I.hexid, callback=self.callback) except Exception as e: self.responseLabel.setText(e) else: self.pubnub.unsubscribe(self.I.hexid) def resetKeys(self): try: from pubnub import Pubnub self.pubnub = Pubnub( publish_key=str(self.pubEdit.text()), subscribe_key=str(self.subEdit.text())) except Exception as e: self.responseLabel.setText(e) def __del__(self): try: self.pubnub.unsubscribe(self.I.hexid) except: pass # try:self.pubnub.unsubscribe(self.I.hexid+'response') # except:pass def closeEvent(self, event): try: self.pubnub.unsubscribe(self.I.hexid) except: pass # try:self.pubnub.unsubscribe(self.I.hexid+'response') # except:pass self.finished = True
class manageSteeds(Thread) : test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() self.steeds_collection = mongodb_connection.getCollection("available_steeds") self.deliveries_collection = mongodb_connection.getCollection("temp_deliveries") self.deliveries_steeds_collection = mongodb_connection.getCollection("temp_deliveries_steeds") self.pubnub_settings = Pubnub(publish_key=manageSteeds.pubnub_publish_key,subscribe_key=manageSteeds.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "steeds_channel" self.genericDAO = GenericDAO() self.scheduler = sched.scheduler() # Instansiate a scheduler def subscriber_callback(self, message, channel): x = 1 def subscriber_error(self, message): print("ERROR : "+message) def connect(self, message): print("CONNECTED TO STEEDS CHANNEL") def reconnect(self, message): print("RECONNECTED TO STEEDS CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM STEEDS CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error ,connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel) # how to instansiate a scheduler #Out of Pubnub functions def manageClientDeliveryRequest(self, iddelivery): # Search delivery by id (criteria) delivery_criteria = {} delivery_criteria["_id"] = iddelivery temp_delivery = self.genericDAO.getOneObject(self, self.deliveries_colllection, delivery_criteria) #Extract pickup coordinates from temporary delivery client_coordinates = [temp_delivery["pickup_lng"], temp_delivery["pickup_lat"]] # Get nearest steeds to pickup location nearest_steeds = self.genericDAO.getNearestSteeds(self, self.steeds_collection, client_coordinates) # Save available steeds for delivery delivery_steeds = {} delivery_steeds["iddelivery"] = iddelivery delivery_steeds["available_steeds"] = nearest_steeds self.genericDAO.insertObject(self.deliveries_steeds_collection, delivery_steeds) #Send delivery request to seeder self.sendDeliveryRequestToSteed(iddelivery) def sendDeliveryRequestToSteed(self, iddelivery): # Search delivery by id (criteria) delivery_criteria = {} delivery_criteria["_id"] = iddelivery temp_delivery = self.genericDAO.getOneObject(self, self.deliveries_colllection, delivery_criteria) #Check received_positive_response field received_response = temp_delivery["received_positive_response"] if(not(received_response)) : #Search available steeds for delivery delivery_steeds_criteria = {} delivery_steeds_criteria["iddelivery"] = iddelivery available_steeds = self.genericDAO.getOneObject(self, self.deliveries_steeds_collection, delivery_steeds_criteria) #Send steed delivery request self.publish("XXXX"+iddelivery+available_steeds[0]["_id"]+" "+"Other delivery details") #Delete 1st steed from available steeds list new_available_steeds = available_steeds[1:len(available_steeds)-1] #Update delivery available steeds update_search_criteria = {} update_search_criteria["iddelivery"] = iddelivery update_field = {} update_field["available_steeds"] = new_available_steeds #Add update function to Generic DAO from old projects #Schedule method call self.scheduler.enter(10, 1, self.sendDeliveryRequestToSteed(iddelivery) ) self.scheduler.run()
class Thermostat(threading.Thread, metaclass=utils.Singleton): """ Main class. Contains decision making algorithm. Gathers all available input and data from sources. Maintains settings and history in memory. Spawns a number of daemon threads to perform background tasks. Notes: - All floating point arithmetic should use Decimal type, pass in number as str to maintain precision """ def __init__(self): super().__init__() self._settings = OrderedDict(sorted(utils.init_settings().items(), key=lambda t: t[0])) self._history = utils.init_history() self._current_temperature = Decimal(0) self._temperature_range = (Decimal(0), Decimal(0)) self._on_rpi = utils.on_rpi() self._mode = utils.Mode.OFF self._state = utils.State.IDLE self.logger = getLogger('app.thermostat') self.temperature_offset = Decimal('0.8') # DEMO value, original Decimal('1.5') self.last_state_update = 0 self.cost_table = None # must init after thread starts self.pubnub = Pubnub( publish_key=config.PUBLISH_KEY, subscribe_key=config.SUBSCRIBE_KEY, uuid=config.THERMOSTAT_ID, ) self.weather_thread = weather.WeatherAPI( self._settings['temperature_unit'], self._settings['city'], self._settings['country_code'], ) # TODO: actively record user actions (input range, changes after prediction) self.history_thread = threading.Timer(600, self.set_history) self.history_thread.daemon = True self.locks = { 'settings': threading.Lock(), 'temperature_range': threading.Lock(), } def run(self): """ Entry method. Main event loop is here. """ # initialize db self.cost_table = sql.CostTable() # initialize sensor and pins if self.on_rpi: utils.init_rpi() sensor.init_sensor() self.current_temperature = sensor.read_temperature() # start daemon thread to fetch weather data self.weather_thread.start() # start daemon thread to record history data self.history_thread.start() # subscribe to remote access messages self.pubnub.subscribe( channels=config.THERMOSTAT_ID, callback=self._callback, error=self._error, ) while True: if self.mode != utils.Mode.OFF: self.update_state() time.sleep(config.UPDATE_INTERVAL) def stop(self): """ Exit handler. Write data stored in memory back to files. """ self.pubnub.unsubscribe(config.THERMOSTAT_ID) utils.write_to_file(config.SETTINGS_FILENAME, self._settings) utils.write_to_file(config.HISTORY_FILENAME, self._history) self.cost_table.close() self.logger.info('cleanup completed') def toggle_power(self): self.mode = utils.Mode.AUTO if self.mode == utils.Mode.OFF else utils.Mode.OFF self.state = utils.State.IDLE # self.last_state_update = time.time() self.publish_mode() def toggle_mode(self, mode=None): if isinstance(mode, utils.Mode): self.mode = mode else: if self.mode == utils.Mode.AUTO: self.mode = utils.Mode.HEAT elif self.mode == utils.Mode.HEAT: self.mode = utils.Mode.COOL elif self.mode == utils.Mode.COOL: self.mode = utils.Mode.AUTO self.publish_mode() def update_state(self): """ Decision maker. Gathers input and data to update thermostat state. """ self.logger.debug('thermostat state is {0}'.format(self.state)) if self.on_rpi: self.current_temperature = sensor.read_temperature() low, high = self.temperature_range if self.current_temperature < (low - self.temperature_offset): new_state = utils.State.HEAT elif self.current_temperature > (high + self.temperature_offset): new_state = utils.State.COOL else: # within range, make decision new_state = self.make_decision() # prevent oscillation if (time.time() - self.last_state_update) > config.OSCILLATION_DELAY: if self.state != new_state: self.last_state_update = time.time() # check mode to determine if new state is allowed if self.mode == utils.Mode.HEAT: self.state = new_state if new_state != utils.State.COOL else self.state elif self.mode == utils.Mode.COOL: self.state = new_state if new_state != utils.State.HEAT else self.state else: self.state = new_state self.publish_state() self.logger.debug('thermostat state updated to {0}'.format(self.state)) def make_decision(self): params_list = list() # TODO: likely needs an additional multiplier to amplify difference # TODO: score is always max when this is the only parameter in DM rating = (self.temperature_range_equilibrium - self.current_temperature) * (self.temperature_range_ceiling - self.temperature_range_floor) params_list.append(('internal_temperature', rating)) # use external temperature if the data is recent # TODO: factor in external humidity if (datetime.datetime.now() - self.weather_thread.last_updated).total_seconds() < 3600: rating = (self.temperature_range_ceiling - self.weather_thread.temperature) / 10 params_list.append(('external_temperature', rating)) # use history if the data exists past_temperature = self.get_history() if past_temperature is not None: rating = past_temperature - self.current_temperature params_list.append(('history_temperature', rating)) # use energy cost if data exists cost_data = self.cost_table.select( select='start_time,cost', where={'country_code': self._settings['country_code'], 'city': self._settings['city']} ) if cost_data: cost_data = dict(cost_data) lowest_cost = min(cost_data.values()) current_hour = utils.round_time(datetime.datetime.now(), 3600).hour current_cost = cost_data.get(current_hour) if current_cost is not None: ratio = Decimal(lowest_cost) / Decimal(current_cost) rating = ratio * params_list[0][1] # ratio * (internal temperature rating) params_list.append(('energy_cost', rating)) matrix = decision.build_decision_matrix(params_list) return decision.evaluate_decision_matrix(matrix) def get_setting(self, name): """ Get setting value. :param name: setting name :return: setting value, None if setting does not exist """ return self._settings.get(name) def set_setting(self, name, value): """ Set setting value. :param name: setting name :param value: setting value :return: None """ if name in self._settings: self._settings[name] = value def get_history(self, dt=None): """ Get temperature at specified datetime. If dt is None, defaults to current time. Full datetime is needed to evaluate day of week. :param dt: datetime.datetime object :return: temperature """ if dt is None: dt = datetime.datetime.now() elif not isinstance(dt, datetime.datetime): self.logger.error('must be datetime object') return None rounded_dt = utils.round_time(dt) day = utils.WeekDay(rounded_dt.weekday()).name time_block = rounded_dt.strftime('%H:%M') temperature = self._history[day][time_block] return Decimal(temperature) if temperature is not None else None def set_history(self, dt=None, temperature=None): """ Set temperature at specified datetime. If dt is None, defaults to current time. Full datetime is need to evaluate day of week. If temperature is None, defaults to current temperature. :param dt: datetime.datetime object :param temperature: temperature to record :return: None """ if temperature is None: temperature = self.current_temperature if dt is None: dt = datetime.datetime.now() elif not isinstance(dt, datetime.datetime): self.logger.error('must be datetime object') return None rounded_dt = utils.round_time(dt) day = utils.WeekDay(rounded_dt.weekday()).name time_block = rounded_dt.strftime('%H:%M') self._history[day][time_block] = str(temperature) # store as str to avoid conversion to JSON float def publish_temperatures(self, current=True, range=True): data = { 'action': 'temperature_data', 'data': { 'current_temperature': round(self.current_temperature) if current else None, 'temperature_low': round(self.temperature_range_floor) if range else None, 'temperature_high': round(self.temperature_range_ceiling) if range else None, } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_mode(self): data = { 'action': 'mode_data', 'data': { 'mode': str(self.mode).lower() } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_state(self): data = { 'action': 'state_data', 'data': { 'state': str(self.state).lower() } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_settings(self): data = { 'action': 'settings_data', 'data': utils.prettify_settings(self.settings) } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_history(self): data = { 'action': 'history_data', 'data': utils.get_history_graph_data_webapp(self._history) } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) def _callback(self, message, channel): """ Pubnub message callback. :param message: received payload :param channel: channel name :return: None """ self.logger.debug(message) if message['action'] == 'request_temperatures': self.publish_temperatures(range=(message['value'] == 'all')) elif message['action'] == 'request_mode': self.publish_mode() elif message['action'] == 'request_settings': self.publish_settings() elif message['action'] == 'request_history': self.publish_history() elif message['action'] == 'update_temperature_range': low = message.get('temperature_low') high = message.get('temperature_high') if low is not None and high is not None: self.temperature_range = (Decimal(low), Decimal(high)) elif message['action'] == 'update_mode': mode = message.get('mode') if mode is not None: mode = utils.Mode[mode.upper()] self.toggle_mode(mode) elif message['action'] == 'update_setting': name = message.get('setting_name') value = message.get('setting_value') if name is not None and value is not None: self.settings = utils.unprettify_setting_name(self.settings, name, value) def _error(self, message): """ Pubnub error callback. :param message: :return: None """ self.logger.error(message) @staticmethod def validate_temperature(value): if value < config.MIN_TEMPERATURE: raise errors.TemperatureValidationError('Temperature cannot be below {}'.format(config.MIN_TEMPERATURE)) if value > config.MAX_TEMPERATURE: raise errors.TemperatureValidationError('Temperature cannot be above {}'.format(config.MAX_TEMPERATURE)) @property def on_rpi(self): return self._on_rpi @property def is_on(self): return self.mode != utils.Mode.OFF @property def is_active(self): return self.state != utils.State.IDLE @property def mode(self): return self._mode @mode.setter def mode(self, mode): if self.on_rpi and mode == utils.Mode.OFF: GPIO.output(config.FAN_PIN, config.RELAY_OFF) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_OFF) self._mode = mode @property def state(self): return self._state @state.setter def state(self, state): if self.on_rpi: if state == utils.State.IDLE: GPIO.output(config.FAN_PIN, config.RELAY_OFF) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_OFF) elif state == utils.State.HEAT: GPIO.output(config.FAN_PIN, config.RELAY_ON) GPIO.output(config.HEAT_PIN, config.RELAY_ON) GPIO.output(config.COOL_PIN, config.RELAY_OFF) elif state == utils.State.COOL: GPIO.output(config.FAN_PIN, config.RELAY_ON) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_ON) self._state = state @property def settings(self): return self._settings @settings.setter def settings(self, t): # TODO: validation self.locks['settings'].acquire(False) key, value = t if self._settings.get(key): # TODO: make country code changeable if key == 'city': self.weather_thread.location = (value, self.weather_thread._location['country_code']) self._settings[key] = value self.publish_settings() self.locks['settings'].release() @property def current_temperature(self): return self._current_temperature @current_temperature.setter def current_temperature(self, value): self._current_temperature = value @property def temperature_range(self): return self._temperature_range @temperature_range.setter def temperature_range(self, t_range): self.locks['temperature_range'].acquire(False) try: low, high = t_range if high < low: raise errors.TemperatureValidationError('Invalid range.') self.validate_temperature(low) self.validate_temperature(high) self._temperature_range = t_range # send update self.publish_temperatures(current=False) finally: self.locks['temperature_range'].release() @property def temperature_range_floor(self): return min(self.temperature_range) @property def temperature_range_ceiling(self): return max(self.temperature_range) @property def temperature_range_equilibrium(self): # DEMO value return self.temperature_range_ceiling # biased towards range ceiling low, high = self.temperature_range bias = (high - low) / 4 equilibrium = sum(self.temperature_range) / 2 return equilibrium + bias
class BaseAppiumTest(unittest.TestCase): driver = None screenshot_count = 0 screenshot_dir = None desired_capabilities_cloud = {} ignoreBuddyTests = 0 buddyCommandName = "" buddyName = "" buddyCheckFileName = "" buddyCheckComplete = False channel = "Trilleon-Automation" DBID = "" project_id = "" gridIdentity = "" buddyIdentity = "" pubnub = None jsonGridPrefix = "" jsonBuddyGridPrefix = "" heartbeat_index = 1 last_heartbeat_detected = None max_time_since_heartbeat = 80 partialDataDelimiter = "$$$" fatalErrorDetected = False fatalErrorMessage = "" ready = False started = False complete = False timeout_default = 300 test_execution_timeout = 4800 parsing_xml = False results = "" auto_screenshot_index = 0 run_command = "" test_run_id_internal = "" def setUp(self, appium_url=None, platform_name=None, bundle_id = None, application_file=None, application_package=None, screenshot_dir=None, application_activity=None, automation_name=None): global test_run_id test_run_id = str(uuid.uuid4()) self.test_run_id_internal = test_run_id log("test_run_id [" + test_run_id + "]") with open("test_run_id.txt", "w") as f: f.write(test_run_id) self.pubnub = Pubnub(publish_key="TODO: YOUR KEY HERE!",subscribe_key="TODO: YOUR KEY HERE!") self.setChannelPrefixes() self.pubnub.subscribe(channels=self.channel, callback=callback, error=error) self.set_screenshot_dir('%s/screenshots' % (os.getcwd())) # Buddy test run information. self.buddyName = "Trilleon-Automation-" + os.environ.get('BUDDY') self.ignoreBuddyTests = os.environ.get('IGNOREBUDDYSYSTEM') self.buddyCommandName = "manual_set_buddy_" + os.environ.get('BUDDY_RELATIONSHIP') # Set Buddy Check-In file path. directoryPieces = os.getcwd().split("/") parentDirectory = "" index = len(directoryPieces) for piece in directoryPieces: index -= 1 if index > 0: parentDirectory += piece + "/" self.buddyCheckFileName = parentDirectory + self.buddyName.replace(".", "").replace("-", "") + ".txt" # Set test run command. self.run_command = os.environ.get('RUN_COMMAND') with open("testresultsjson.txt", "w") as f: f.write("[") # Set up driver if os.environ['DEVICE_PLATFORM'] == "ios": self.desired_capabilities_cloud["showXcodeLog"] = True self.desired_capabilities_cloud["useNewWDA"] = False self.desired_capabilities_cloud["wdaLocalPort"] = os.environ['WDA_LOCAL_PORT'] self.desired_capabilities_cloud["autoAcceptAlerts"] = True # iOS -- Dismisses device level popups automatically. else: self.desired_capabilities_cloud["systemPort"] = os.environ['UNIQUE_BOOT_PORT'] self.desired_capabilities_cloud["autoGrantPermissions"] = True # Android 1.6.3+ -- Gives app permissions before starting. self.desired_capabilities_cloud["app"] = os.environ['APPLICATION'] self.desired_capabilities_cloud["udid"] = os.environ['DEVICE_UDID'] self.desired_capabilities_cloud["automationName"] = os.environ['DRIVER'] self.desired_capabilities_cloud["platformName"] = os.environ['DEVICE_PLATFORM'] if 'PLATFORM_VERSION' in os.environ: self.desired_capabilities_cloud["platformVersion"] = os.environ['PLATFORM_VERSION'] self.desired_capabilities_cloud["deviceName"] = os.environ['DEVICE_UDID'] self.desired_capabilities_cloud["newCommandTimeout"] = 99999 self.desired_capabilities_cloud["launchTimeout"] = 99999 time.sleep(5) #try: self.driver = webdriver.Remote("http://" + os.environ['HOST'] + ":" + os.environ['UNIQUE_PORT'] + "/wd/hub", self.desired_capabilities_cloud) #except Exception as e: #log("Error Instantiating Driver [" + str(e) + "]. Attempting to continue anyway.") time.sleep(10) def postMessage(self, message): postString = self.jsonGridPrefix + message self.pubnub.publish(self.channel, postString, callback=_callback, error=error) log("Request posted to PubNub [" + postString + "]") def setChannelPrefixes(self): global test_run_id log("PUBSUB CHANNEL ID: " + self.channel) self.gridIdentity = "Trilleon-Automation-" + os.environ['GRID_IDENTITY_PREFIX'] self.jsonGridPrefix = "{\"test_run_id\":\"" + test_run_id + "\"},{\"grid_identity\":\"" + self.gridIdentity + "\"},{\"grid_source\":\"server\"},{\"grid_context\":\"StandardAlert\"}," self.jsonBuddyGridPrefix = "{\"test_run_id\":\"" + test_run_id + "\"},{\"grid_identity\":\"" + self.buddyName + "\"},{\"grid_source\":\"server\"},{\"grid_context\":\"BuddyMessage\"}," # Close down the driver, call XML and JSON finalizer, close PubNub connection, and handle any fatal execution error. def tearDown(self): self.pubnub.unsubscribe(self.channel) self.writeAutomationResults('TEST-all.xml') if self.fatalErrorDetected == True: with open("FatalErrorDetected.txt", "w") as f: f.write(self.fatalErrorMessage) time.sleep(5) try: self.driver.quit() except: log("Error encountered while quitting driver") with open("test_status.txt", "w") as f: f.write(os.environ['TEST_RUN_STATUS']) pylogtext = "" with open("PyLog.txt", "r") as f: pylogtext = f.read() if "CRITICAL_SERVER_FAILURE_IN_APPIUM_TEST" in pylogtext: raise Exception("CRITICAL_SERVER_FAILURE_IN_APPIUM_TEST Detected!") # Writes all XML to storage file, and verifies JSON validity so that HTML report is rendered correctly. def writeAutomationResults(self, filename): # Verify that JSON recorded is valid. Add placeholder JSON if it is not. text = "" with open("testresultsjson.txt", "r") as f: text = f.read() # Replace any missing commas between JSON objects and attributes. if len(self.results) > 0: if "failure" in self.results: os.environ['TEST_RUN_STATUS'] = "UNSTABLE" else: os.environ['TEST_RUN_STATUS'] = "SUCCESS" # Game error popup check. if self.fatalErrorDetected: os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_RUN" with open("testresultsjson.txt", "a") as f: f.write("{\"order_ran\":\"999\", \"status\":\"Failed\", \"name\":\"ERROR_POPUP\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"ERROR_POPUP\", \"result_details\":\"The game produced an error popup that affected automation execution. Check screenshot for details.\", \"assertions\":[]},") with open(os.getcwd() + "/" + filename, "w") as f: f.write(self.results) else: # Failure occurred - likely an app crash. Send PubNub History to help investigate where it happened. log("FATAL APPLICATION ERROR DISRUPTED AUTOMATION") # If there is existing JSON, report that app crashed or hanged. if len(text) > 5: os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_RUN" with open("testresultsjson.txt", "a") as f: f.write("{\"order_ran\":\"999\", \"status\":\"Failed\", \"name\":\"GAME_CRASHED_OR_HANGED\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"CRASH_HANG\", \"result_details\":\"The game either suddenly crashed, or automation execution encountered an unhandled fatal error that halted test execution. View screenshot for details.\", \"assertions\":[]},") else: # If results were not recorded, then the app never loaded, crashed, or automation could not launch. if "checking_in" not in self.get_communication_history(): os.environ['TEST_RUN_STATUS'] = "CRASH_DURING_LAUNCH" with open("testresultsjson.txt", "w") as f: f.write("[{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game crashed on load, or otherwise failed to reach a state where automation could register on the pubsub communication channel.\", \"assertions\":[]},") else: os.environ['TEST_RUN_STATUS'] = "CRASH_AFTER_LAUNCH" if "order_ran" in self.get_communication_history(): with open("testresultsjson.txt", "a") as f: f.write(",{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game launched, and automation registered on the pubsub communication channel, but either the app crashed, or automation was blocked from beginning its test run.\", \"assertions\":[]},") else: with open("testresultsjson.txt", "w") as f: f.write("[{\"order_ran\":\"0\", \"status\":\"Failed\", \"name\":\"GAME_LAUNCH_FAILURE\", \"class\":\"FATAL_ERROR\", \"test_categories\":\"LAUNCH_FAILURE\", \"result_details\":\"The game launched, and automation registered on the pubsub communication channel, but either the app crashed, or automation was blocked from beginning its test run.\", \"assertions\":[]},") def set_screenshot_dir(self, screenshot_dir): log("Saving screenshots at: " + screenshot_dir) self.screenshot_dir = screenshot_dir if not os.path.exists(screenshot_dir): os.mkdir(self.screenshot_dir) def take_screenshot(self, path): log("Attempting to save screenshot at path [" + self.screenshot_dir + path + "]") try: self.driver.save_screenshot(self.screenshot_dir + path) except Exception as e: log("Exception! " + str(e)) def handle_device_alert(accept): if os.environ['DEVICE_PLATFORM'] == "android": # Try to dismiss any Android account alerts. try: acceptButton = self.driver.find_element_by_xpath("//*[@text='Allow']") action = TouchAction(self.driver) action.tap(acceptButton).perform() log("Chose 'Allow' for Google Play alert.") except Exception as e: log("Exception accepting Android alert! " + str(e)) try: acceptButton = self.driver.find_element_by_xpath("//*[@text='OK']") action = TouchAction(self.driver) action.tap(acceptButton).perform() log("Chose 'OK' for Google Play alert.") except Exception as e: log("Exception accepting Android alert! " + str(e)) else: log("Attempting to dismiss any iOS device-level alert.") try: if accept == True: self.driver.switch_to.alert.accept() else: self.driver.switch_to.alert.dismiss() except Exception as e: log("Internal Error Accepting iOS Alert (this is not likely a problem, due to interval dismissal logic): " + str(e)) # Write name of self to parent level file that selected Buddy will check to verify both Buddy's have begun their test runs. def buddy_check_in(self): #directoryPieces = os.getcwd().split("/") #self.parentDirectory = "" #index = len(directoryPieces) #for piece in directoryPieces: #index -= 1 #if index > 0: #self.parentDirectory += piece + "/" #with open(self.buddyCheckFileName, "a") as f: #f.write(self.gridIdentity) log("Writing Device Identity [" + self.gridIdentity + "] To File [" + self.buddyCheckFileName + "].") # See if Buddy has checked in as active and running. Will be used to skip Buddy tests if associated Buddy cannot perform its function. def buddy_check(self): return True #fileReadTest = "" #with open(self.buddyCheckFileName, "r") as f: #fileReadTest = f.read() #self.buddyCheckComplete = True log("Reading Buddy Check File. Expecting To See Buddy Name [" + self.buddyName + "] In File Contents [" + fileReadTest + "].") #if self.buddyName not in fileReadTest: #TODO: self.postMessage("{\"buddy_ignore_all\":0}") #return False #else: #return True # Break final XML generated by AutomationReport.cs out from total pubsub history and format it properly before placing it in final xml file. def get_xml_from_client_run(self): log("Command Recieved: [Get XML]") global test_run_id recent_posts = self.get_communication_history() # Toke "xml_complete" is required for this script to know that a client has reached completion of its test run. if ("xml_complete" not in recent_posts and "completed_automation" not in recent_posts) or self.parsing_xml == True: return False self.parsing_xml = True log("Xml Complete token detected. Begining parsing of test results xml.") time.sleep(5) # Split wall of pubsub text on start token and determine how many fragments of XML our test run intends to communicate. splitPieces = recent_posts.split("xml_start") if len(splitPieces) < 2: log("Failed to detect xml_start token in recent posts [" + recent_posts + "]") return False rawSplit = splitPieces[1] delimiter = "|" start = rawSplit.index(delimiter) + len(delimiter) end = rawSplit.index(delimiter, start) xmlPiecesCount = int(rawSplit[start:end]) # XML is potentially fragmented when large test runs exceed a 4000 character pubsub message limit. log("XML Pieces Count [" + str(xmlPiecesCount) + "]") # PubNub does not guarantee sequential order of messages. We know how many xml fragments to expect, but not the order they will be recieved. xml_parsed = [None] * xmlPiecesCount list_history = recent_posts.split(test_run_id) # Split each fragment from history and place it into a dictionary that guarantees the proper order of reconstituted xml. for v in list_history: post = v if "xml_fragment_" in post: log("XML Fragment Detected. Parsing.") delimiter = "||" start = post.find(delimiter, 0) + 2 end = post.find(delimiter, start + 2) index = int(post.split("xml_fragment_")[1][0]) xml_parsed[index] = post[start:end] all_xml = "" for p in xml_parsed: if p != None: all_xml += p self.results = all_xml.replace('@APOS@', '"') if len(all_xml) == 0: log("XML empty after processing. Recent posts [" + recent_posts + "]") log("ALL_XML_FINAL " + all_xml) return True # Find, extract, format, and save all single-test JSON results for server HTML report def get_json_from_client_run(self, force): log("Command Recieved: [Get JSON]") recent_posts = self.get_communication_history() if "completed_automation" in recent_posts or force == True: log("Generating JSON.") initialDelimiter = "SINGLE_TEST_RESULTS_JSON|" delimiter = "|" rawAll = recent_posts.split(initialDelimiter) json = "" index = 0 partialInitialDelimiter = "SINGLE_TEST_RESULTS_JSON_MULTI_PART|" for x in rawAll: if index > 0 and len(x) > 0: # Handle test results reporting that was too large to send in a single message, requiring several parts of a single result report. if partialInitialDelimiter in x: rawPartials = x.split(partialInitialDelimiter) # All partial message pieces indexPartial = 0 piecesFinal = ["" for x in range(len(rawPartials))] for z in rawPartials: if indexPartial == 0: json += rawPartials[0].split(delimiter)[0] # First, record the test that preceded the partial test details report. else: piecesPartial = z.split(self.partialDataDelimiter) piecesFinal[int(piecesPartial[0])] = piecesPartial[1].split(delimiter)[0] # The first piece after splicing is the index/order of this piece. Set that piece equal to the actual message data. indexPartial += 1 for f in piecesFinal: json += f # Should piece together valid json in correct order if previous for loop correctly handled ordering. else: json += x.split(delimiter)[0] index += 1 # "@APOS@" token is a special encoding of double qoutes to prevent issues with PubNub message encoding and proper formatting of JSON json = json.replace("@APOS@", "\"").replace("}{", "},{") if not json.endswith("]"): if json.endswith(","): json = json[:-1] + "]" else: json += "]" fileContent = "" with open("testresultsjson.txt", "r") as f: fileContent = f.read() log("JSON FINAL LENGTH [" + str(len(json)) + "]") try: log("JSON FINAL ACTUAL [" + json + "]") except Exception as e: log("Failed to parse final json [" + str(e) + "]") if json not in fileContent: with open("testresultsjson.txt", "a") as f: f.write(json) return True else: return False # This is used by the server to check for client "heartbeats", or to request them, to verify that client has not crashed or hanged in execution. def has_heartbeat(self): global heartbeats if self.last_heartbeat_detected == None: self.last_heartbeat_detected = datetime.now() if len(heartbeats) > 0: log("Registered heartbeat #" + str(self.heartbeat_index)) self.heartbeat_index += 1 self.last_heartbeat_detected = datetime.now() timeDifference = (datetime.now() - self.last_heartbeat_detected).total_seconds() if timeDifference > self.max_time_since_heartbeat: self.postMessage("{\"health_check\":0}") log("Any heartbeat ??" + heartbeats) time.sleep(15) if len(heartbeats) > 0: log("Registered heartbeat #" + str(self.heartbeat_index) + " after explicit request") self.heartbeat_index += 1 self.last_heartbeat_detected = datetime.now() return True else: log("Heartbeat not detected in expected timeframe. Time since last heartbeat [" + str(timeDifference) + " seconds]") return False else: return True # Get pubsub history saved in text file with each recieved callback. def get_communication_history(self): results = "" try: with open("RelevantPubNubCommunications.txt", "r") as f: results = f.read() except Exception as e: log("Exception Reading History Text: " + str(e)) return results # Get only the pubsub message history that contains a provided search term. def get_specific_json_from_history(self, jsonFlag): #Return only messages bound for this server-client relationship. historyText = self.get_communication_history() if len(historyText) == 0: return "" history = historyText.split("messageFull") global test_run_id resultsString = "" for x in history: rawMessage = urllib.unquote(str(x)).replace("\\", "").replace("\"", "'").replace("+", " ") splitMessage = rawMessage.split("test_run_id") for s in splitMessage: if test_run_id in s and (True if jsonFlag == None else (jsonFlag in s)): resultsString += s return resultsString # See if client has requested the server take a screenshot. def check_for_client_requests(self, command): if command == "screenshot": fileName = "" with open("screenshot_requests.txt", "r") as f: fileName = f.read().strip() f = open("screenshot_requests.txt", "w") f.write("") f.close() if any(c.isalpha() for c in fileName): if "Interval" in fileName: fileName = str(self.auto_screenshot_index) + "_" + "interval" self.auto_screenshot_index += 1 else: fileName = "/" + fileName + ".png" # If this file already exists, don't take it again. if os.path.exists(self.screenshot_dir + fileName) == False: try: self.take_screenshot(fileName) self.postMessage("{\"request_response\":\"screenshot\"}") log("Screenshot successfully saved [" + fileName + "]") except BaseException as e: log("Exception Taking Screenshot! " + str(e)) if command == "handle_client_commands": commandFull = "" with open("client_request_queue.txt", "r") as f: commandFull = f.read() if "|" in commandFull: # Handle command commandActual = commandFull.split("|")[0] commandValue = commandFull.split("|")[1] if commandActual == "HANDLE_DEVICE_ALERT": self.handle_device_alert(False if commandValue == "0" else True) #Clear command queue f = open("client_request_queue.txt", "w") f.write("") f.close() self.postMessage("{\"server_broker_response\":\"" + commandActual + "\"}") else: try: self.driver.switch_to.alert.accept() except BaseException as e: log("") def find_json_for_performance_attribute(self, delimiter): recent_posts = self.get_communication_history() endDelimiter = "|" json = "" index = 0 fullInitialDelimiter = delimiter + "_MULTI_PART|" rawAll = recent_posts.split(fullInitialDelimiter) piecesFinal = ["" for x in range(len(rawAll) - 1)] log("Finding " + delimiter) for x in rawAll: if index > 0 and len(x) > 0: # Handle test results reporting that was too large to send in a single message, requiring several parts of a single result report. rawPartials = x.split(fullInitialDelimiter) # All partial message pieces for z in rawPartials: piecesPartial = z.split(self.partialDataDelimiter) if index - 1 == int(piecesPartial[0]): piecesFinal[int(piecesPartial[0])] = piecesPartial[1].split(endDelimiter)[0] # The first piece after splicing is the index/order of this piece. Set that piece equal to the actual message data. break index += 1 for f in piecesFinal: json += f # Should piece together valid json in correct order if previous for loop correctly handled ordering. # "@APOS@" token is a special encoding of double qoutes to prevent issues with PubNub message encoding and proper formatting of JSON json = json.replace("@APOS@", "\"").replace("}{", "},{") if not json.endswith("]"): json += "]" return json # Check if specific messages have been communicated over PubNub and extract relevant details. def check_for_client_responses(self, command, postAllParsed): historyString = "" if command == "heap_json": log("Collecting Heap Json") fileWrite = open("HeapJson.txt", "w") fileWrite.write(self.find_json_for_performance_attribute("HEAP_JSON")) fileWrite.close() return True if command == "garbage_collection_json": log("Collecting GC Json") fileWrite = open("GarbageCollectionJson.txt", "w") fileWrite.write(self.find_json_for_performance_attribute("GC_JSON")) fileWrite.close() return True if command == "fps_json": log("Collecting FPS Json") fileWrite = open("FpsJson.txt", "w") fileWrite.write(self.find_json_for_performance_attribute("FPS_JSON")) fileWrite.close() return True if command == "exceptions_data": log("Collecting Exceptions Values") fileWrite = open("ExceptionsJson.txt", "w") fileWrite.write(self.find_json_for_performance_attribute("EXCEPTION_DATA")) fileWrite.close() return True if command == "garbage_collection": historyString = self.get_specific_json_from_history("GARBAGE_COLLECTION") if len(historyString) > 0: log("Collecting Garbage Collection Values") initialDelimiter = "GARBAGE_COLLECTION|" finalDelimiter = "|" raw = historyString.split(initialDelimiter)[1] data = raw.split(finalDelimiter)[0] fileWrite = open("GarbageCollection.txt", "w") fileWrite.write(data) fileWrite.close() return True if command == "heap_size": historyString = self.get_specific_json_from_history("HEAP_SIZE") if len(historyString) > 0: log("Collecting Heap Size Values") initialDelimiter = "HEAP_SIZE|" finalDelimiter = "|" raw = historyString.split(initialDelimiter)[1] data = raw.split(finalDelimiter)[0] fileWrite = open("HeapSize.txt", "w") fileWrite.write(data) fileWrite.close() return True if command == "fps": historyString = self.get_specific_json_from_history("FPS_VALUES") if len(historyString) > 0: log("Collecting FPS Values") initialDelimiter = "FPS_VALUES|" finalDelimiter = "|" raw = historyString.split(initialDelimiter)[1] data = raw.split(finalDelimiter)[0] fileWrite = open("Fps.txt", "w") fileWrite.write(data) fileWrite.close() return True if command == "game_launch_seconds": historyString = self.get_specific_json_from_history("GAME_LAUNCH_SECONDS") if len(historyString) > 0: log("Collecting Game Initialization Time") initialDelimiter = "GAME_LAUNCH_SECONDS|" finalDelimiter = "|" raw = historyString.split(initialDelimiter)[1] data = raw.split(finalDelimiter)[0] fileWrite = open("GameInitializationTime.txt", "w") fileWrite.write(data) fileWrite.close() return True if command == "device_details_html": historyString = self.get_specific_json_from_history("DEVICE_DETAILS_HTML") if len(historyString) > 0: log("Collecting Device Details HTML") initialDelimiter = "DEVICE_DETAILS_HTML|" finalDelimiter = "|" raw = historyString.split(initialDelimiter)[1] html = raw.split(finalDelimiter)[0].replace("@APOS@", "\"").replace("@QUOT@", "'") fileWrite = open("TestRunHeaderHtml.txt", "w") fileWrite.write(html) fileWrite.close() return True recent_posts = self.get_communication_history() if command == "ready" and ("checking_in" in recent_posts or "DBID||" in recent_posts): time.sleep(5) if "DBID||" in recent_posts: dbidPiece = recent_posts.split("DBID||")[1] self.DBID = dbidPiece.split("||")[0] return True if command == "started" and ("starting_automation" in recent_posts or "Starting Automation" in recent_posts or "SINGLE_TEST_RESULTS_JSON" in recent_posts): return True if command == "complete" and "completed_automation" in recent_posts: return True if command == "fatal_error_check" and "Fatal Error. Shutting down automation" in recent_posts: if self.fatalErrorDetected == False: self.fatalErrorDetected = True self.fatalErrorMessage = "Fatal Error popup occurred. Game Unavailable!" self.take_screenshot("/fatal_error.png") return True return False
class AppWindow(QtGui.QMainWindow, remote.Ui_MainWindow): resSlot = QtCore.pyqtSignal(str,str) def __init__(self, parent=None,**kwargs): super(AppWindow, self).__init__(parent) self.setupUi(self) self.I=kwargs.get('I',None) self.setWindowTitle(self.I.H.version_string+' : '+params.get('name','').replace('\n',' ') ) self.pubEdit.setText("pub-c-22260663-a169-4935-9c74-22925f4398af") self.subEdit.setText("sub-c-3431f4ba-2984-11e6-a01f-0619f8945a4f") self.channelLabel.setText(self.I.hexid) self.resetKeys() #Connect to pubnub self.resSlot.connect(self.writeResults) self.thingSpeakCommand = None self.timer=QtCore.QTimer() self.timer.timeout.connect(self.uploadToThingSpeak) self.uploadToThingSpeak(); self.timer.start(15*1e3) #15 seconds import inspect funcs=dir(self.I) self.methods={} self.function_list=[] for a in funcs: fn=getattr(self.I,a) try: args=inspect.getargspec(fn).args except: args=[] if len(args)>0: if inspect.ismethod(fn): self.methods[a]=(fn,args) #list of tuples of all methods in device handler if args[0]=='self': self.function_list.append([a,args[1:] ]) def uploadToThingSpeak(self): if self.thingSpeakCommand: try: result = self.thingSpeakCommand[0](*self.thingSpeakCommand[1]) params = urllib.urlencode({'field1': float(result), 'key':str(self.thingSpeakKey.text())}) headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"} conn = httplib.HTTPConnection("api.thingspeak.com:80") conn.request("POST", "/update", params, headers) response = conn.getresponse() self.results_2.append('%s : %s'%( response.status, response.reason)) data = response.read() conn.close() except Exception as e: self.results_2.append('Error : %s'%( e.message)) pass def setThingSpeakCommand(self): try: message = str(self.cmdEditThingSpeak.text()) fn_name=message.split('(')[0] args = message[message.find("(")+1:message.find(")")].strip().split(',') total_args=[] for t in args: if not len(t):continue if t[0]=="'" or t[0]=='"':total_args.append(t[1:-1]) else:total_args.append(string.atoi(t)) method = self.methods.get(fn_name)[0] if method == None : print ('no such command :',fn_name) return 'no such command : %s'%fn_name else: #while self.hw_lock and self.active: pass #self.hw_lock=True self.thingSpeakCommand=[method,total_args] except Exception as e: self.results_2.append('Set Error : %s'%( e.message)) pass def setListenState(self,state): if state: #Enable listen try: self.pubnub.subscribe(self.I.hexid,callback = self.callback, error=self._error, connect=self._connect, reconnect=self._reconnect, disconnect=self._disconnect) except Exception as e: self.responseLabel.setText (e) else: self.pubnub.unsubscribe(self.I.hexid) def resetKeys(self): try: from pubnub import Pubnub self.pubnub = Pubnub( publish_key = str(self.pubEdit.text()), subscribe_key = str(self.subEdit.text())) except Exception as e: self.responseLabel.setText (e) def callback(self,full_message, channel): msg_type = full_message[0] senderId = str(full_message)[1:19] message = str(full_message)[19:] try: if msg_type == 'Q' : #Query self.resSlot.emit(message,'in') fn_name=message.split('(')[0] args = message[message.find("(")+1:message.find(")")].strip().split(',') total_args=[] for t in args: if not len(t):continue if t[0]=="'" or t[0]=='"':total_args.append(t[1:-1]) else:total_args.append(string.atoi(t)) method = self.methods.get(fn_name)[0] if method == None : print ('no such command :',fn_name) return 'no such command : %s'%fn_name else: #while self.hw_lock and self.active: pass #self.hw_lock=True result=method(*total_args) #self.hw_lock=False jsonres = json.dumps(result,cls=NumpyEncoder) self.pubnub.publish(channel = senderId,message = 'R'+self.I.hexid+jsonres) #R stands for response . Q for Query self.resSlot.emit('%s %s %s %d %s... %s'%(method.__name__,str(total_args),str(type(jsonres)),len(jsonres),str(jsonres[:20]),self.I.hexid+'response'),'out') elif msg_type == 'R': self.resSlot.emit(senderId+' > '+message,'reply') except Exception as e: self.responseLabel.setText (e.message) def writeResults(self,txt,t): if t == 'in': self.results.append('RECV:<span style="background-color: #FFFF00">' + txt +'</span') elif t == 'out': self.results.append('SEND:<span style="background-color: #00FF00">' + txt +'</span') elif t == 'reply': self.results.append('GOT :<span style="background-color: #00FFFF">' + txt +'</span') elif t == 'msg': self.results.append('MSG :<span style="background-color: #000;color:#FFF">' + txt +'</span') def execRemote(self): chan = hex(0x1000000000000000|int(str(self.sendID.text()),16)) ; msg = str(self.cmdEdit.text()) self.pubnub.publish(channel = chan,message = 'Q'+self.I.hexid+msg) self.resSlot.emit('[' + chan + ']: ' + msg,'out') def _connect(self,m): self.resSlot.emit("Connected to PubNub! Listening on "+m,'msg') def _reconnect(self,m): self.resSlot.emit("Reconnected to PubNub!",'msg') def _disconnect(self,m): self.resSlot.emit("Disconnected from PubNub!",'msg') def _error(self,m): self.resSlot.emit(" PubNub Error!",'msg') def __del__(self): try:self.pubnub.unsubscribe(self.I.hexid) except:pass try:self.pubnub.unsubscribe(self.I.hexid+'response') except:pass def closeEvent(self, event): try:self.pubnub.unsubscribe(self.I.hexid) except:pass try:self.pubnub.unsubscribe(self.I.hexid+'response') except:pass self.finished=True
class Thermostat(threading.Thread, metaclass=utils.Singleton): """ Main class. Contains decision making algorithm. Gathers all available input and data from sources. Maintains settings and history in memory. Spawns a number of daemon threads to perform background tasks. Notes: - All floating point arithmetic should use Decimal type, pass in number as str to maintain precision """ def __init__(self): super().__init__() self._settings = OrderedDict( sorted(utils.init_settings().items(), key=lambda t: t[0])) self._history = utils.init_history() self._current_temperature = Decimal(0) self._temperature_range = (Decimal(0), Decimal(0)) self._on_rpi = utils.on_rpi() self._mode = utils.Mode.OFF self._state = utils.State.IDLE self.logger = getLogger('app.thermostat') self.temperature_offset = Decimal( '0.8') # DEMO value, original Decimal('1.5') self.last_state_update = 0 self.cost_table = None # must init after thread starts self.pubnub = Pubnub( publish_key=config.PUBLISH_KEY, subscribe_key=config.SUBSCRIBE_KEY, uuid=config.THERMOSTAT_ID, ) self.weather_thread = weather.WeatherAPI( self._settings['temperature_unit'], self._settings['city'], self._settings['country_code'], ) # TODO: actively record user actions (input range, changes after prediction) self.history_thread = threading.Timer(600, self.set_history) self.history_thread.daemon = True self.locks = { 'settings': threading.Lock(), 'temperature_range': threading.Lock(), } def run(self): """ Entry method. Main event loop is here. """ # initialize db self.cost_table = sql.CostTable() # initialize sensor and pins if self.on_rpi: utils.init_rpi() sensor.init_sensor() self.current_temperature = sensor.read_temperature() # start daemon thread to fetch weather data self.weather_thread.start() # start daemon thread to record history data self.history_thread.start() # subscribe to remote access messages self.pubnub.subscribe( channels=config.THERMOSTAT_ID, callback=self._callback, error=self._error, ) while True: if self.mode != utils.Mode.OFF: self.update_state() time.sleep(config.UPDATE_INTERVAL) def stop(self): """ Exit handler. Write data stored in memory back to files. """ self.pubnub.unsubscribe(config.THERMOSTAT_ID) utils.write_to_file(config.SETTINGS_FILENAME, self._settings) utils.write_to_file(config.HISTORY_FILENAME, self._history) self.cost_table.close() self.logger.info('cleanup completed') def toggle_power(self): self.mode = utils.Mode.AUTO if self.mode == utils.Mode.OFF else utils.Mode.OFF self.state = utils.State.IDLE # self.last_state_update = time.time() self.publish_mode() def toggle_mode(self, mode=None): if isinstance(mode, utils.Mode): self.mode = mode else: if self.mode == utils.Mode.AUTO: self.mode = utils.Mode.HEAT elif self.mode == utils.Mode.HEAT: self.mode = utils.Mode.COOL elif self.mode == utils.Mode.COOL: self.mode = utils.Mode.AUTO self.publish_mode() def update_state(self): """ Decision maker. Gathers input and data to update thermostat state. """ self.logger.debug('thermostat state is {0}'.format(self.state)) if self.on_rpi: self.current_temperature = sensor.read_temperature() low, high = self.temperature_range if self.current_temperature < (low - self.temperature_offset): new_state = utils.State.HEAT elif self.current_temperature > (high + self.temperature_offset): new_state = utils.State.COOL else: # within range, make decision new_state = self.make_decision() # prevent oscillation if (time.time() - self.last_state_update) > config.OSCILLATION_DELAY: if self.state != new_state: self.last_state_update = time.time() # check mode to determine if new state is allowed if self.mode == utils.Mode.HEAT: self.state = new_state if new_state != utils.State.COOL else self.state elif self.mode == utils.Mode.COOL: self.state = new_state if new_state != utils.State.HEAT else self.state else: self.state = new_state self.publish_state() self.logger.debug('thermostat state updated to {0}'.format( self.state)) def make_decision(self): params_list = list() # TODO: likely needs an additional multiplier to amplify difference # TODO: score is always max when this is the only parameter in DM rating = (self.temperature_range_equilibrium - self.current_temperature) * (self.temperature_range_ceiling - self.temperature_range_floor) params_list.append(('internal_temperature', rating)) # use external temperature if the data is recent # TODO: factor in external humidity if (datetime.datetime.now() - self.weather_thread.last_updated).total_seconds() < 3600: rating = (self.temperature_range_ceiling - self.weather_thread.temperature) / 10 params_list.append(('external_temperature', rating)) # use history if the data exists past_temperature = self.get_history() if past_temperature is not None: rating = past_temperature - self.current_temperature params_list.append(('history_temperature', rating)) # use energy cost if data exists cost_data = self.cost_table.select(select='start_time,cost', where={ 'country_code': self._settings['country_code'], 'city': self._settings['city'] }) if cost_data: cost_data = dict(cost_data) lowest_cost = min(cost_data.values()) current_hour = utils.round_time(datetime.datetime.now(), 3600).hour current_cost = cost_data.get(current_hour) if current_cost is not None: ratio = Decimal(lowest_cost) / Decimal(current_cost) rating = ratio * params_list[0][ 1] # ratio * (internal temperature rating) params_list.append(('energy_cost', rating)) matrix = decision.build_decision_matrix(params_list) return decision.evaluate_decision_matrix(matrix) def get_setting(self, name): """ Get setting value. :param name: setting name :return: setting value, None if setting does not exist """ return self._settings.get(name) def set_setting(self, name, value): """ Set setting value. :param name: setting name :param value: setting value :return: None """ if name in self._settings: self._settings[name] = value def get_history(self, dt=None): """ Get temperature at specified datetime. If dt is None, defaults to current time. Full datetime is needed to evaluate day of week. :param dt: datetime.datetime object :return: temperature """ if dt is None: dt = datetime.datetime.now() elif not isinstance(dt, datetime.datetime): self.logger.error('must be datetime object') return None rounded_dt = utils.round_time(dt) day = utils.WeekDay(rounded_dt.weekday()).name time_block = rounded_dt.strftime('%H:%M') temperature = self._history[day][time_block] return Decimal(temperature) if temperature is not None else None def set_history(self, dt=None, temperature=None): """ Set temperature at specified datetime. If dt is None, defaults to current time. Full datetime is need to evaluate day of week. If temperature is None, defaults to current temperature. :param dt: datetime.datetime object :param temperature: temperature to record :return: None """ if temperature is None: temperature = self.current_temperature if dt is None: dt = datetime.datetime.now() elif not isinstance(dt, datetime.datetime): self.logger.error('must be datetime object') return None rounded_dt = utils.round_time(dt) day = utils.WeekDay(rounded_dt.weekday()).name time_block = rounded_dt.strftime('%H:%M') self._history[day][time_block] = str( temperature) # store as str to avoid conversion to JSON float def publish_temperatures(self, current=True, range=True): data = { 'action': 'temperature_data', 'data': { 'current_temperature': round(self.current_temperature) if current else None, 'temperature_low': round(self.temperature_range_floor) if range else None, 'temperature_high': round(self.temperature_range_ceiling) if range else None, } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_mode(self): data = { 'action': 'mode_data', 'data': { 'mode': str(self.mode).lower() } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_state(self): data = { 'action': 'state_data', 'data': { 'state': str(self.state).lower() } } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_settings(self): data = { 'action': 'settings_data', 'data': utils.prettify_settings(self.settings) } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) self.logger.debug('published message: {0}'.format(data)) def publish_history(self): data = { 'action': 'history_data', 'data': utils.get_history_graph_data_webapp(self._history) } self.pubnub.publish(config.THERMOSTAT_ID, data, error=self._error) def _callback(self, message, channel): """ Pubnub message callback. :param message: received payload :param channel: channel name :return: None """ self.logger.debug(message) if message['action'] == 'request_temperatures': self.publish_temperatures(range=(message['value'] == 'all')) elif message['action'] == 'request_mode': self.publish_mode() elif message['action'] == 'request_settings': self.publish_settings() elif message['action'] == 'request_history': self.publish_history() elif message['action'] == 'update_temperature_range': low = message.get('temperature_low') high = message.get('temperature_high') if low is not None and high is not None: self.temperature_range = (Decimal(low), Decimal(high)) elif message['action'] == 'update_mode': mode = message.get('mode') if mode is not None: mode = utils.Mode[mode.upper()] self.toggle_mode(mode) elif message['action'] == 'update_setting': name = message.get('setting_name') value = message.get('setting_value') if name is not None and value is not None: self.settings = utils.unprettify_setting_name( self.settings, name, value) def _error(self, message): """ Pubnub error callback. :param message: :return: None """ self.logger.error(message) @staticmethod def validate_temperature(value): if value < config.MIN_TEMPERATURE: raise errors.TemperatureValidationError( 'Temperature cannot be below {}'.format( config.MIN_TEMPERATURE)) if value > config.MAX_TEMPERATURE: raise errors.TemperatureValidationError( 'Temperature cannot be above {}'.format( config.MAX_TEMPERATURE)) @property def on_rpi(self): return self._on_rpi @property def is_on(self): return self.mode != utils.Mode.OFF @property def is_active(self): return self.state != utils.State.IDLE @property def mode(self): return self._mode @mode.setter def mode(self, mode): if self.on_rpi and mode == utils.Mode.OFF: GPIO.output(config.FAN_PIN, config.RELAY_OFF) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_OFF) self._mode = mode @property def state(self): return self._state @state.setter def state(self, state): if self.on_rpi: if state == utils.State.IDLE: GPIO.output(config.FAN_PIN, config.RELAY_OFF) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_OFF) elif state == utils.State.HEAT: GPIO.output(config.FAN_PIN, config.RELAY_ON) GPIO.output(config.HEAT_PIN, config.RELAY_ON) GPIO.output(config.COOL_PIN, config.RELAY_OFF) elif state == utils.State.COOL: GPIO.output(config.FAN_PIN, config.RELAY_ON) GPIO.output(config.HEAT_PIN, config.RELAY_OFF) GPIO.output(config.COOL_PIN, config.RELAY_ON) self._state = state @property def settings(self): return self._settings @settings.setter def settings(self, t): # TODO: validation self.locks['settings'].acquire(False) key, value = t if self._settings.get(key): # TODO: make country code changeable if key == 'city': self.weather_thread.location = ( value, self.weather_thread._location['country_code']) self._settings[key] = value self.publish_settings() self.locks['settings'].release() @property def current_temperature(self): return self._current_temperature @current_temperature.setter def current_temperature(self, value): self._current_temperature = value @property def temperature_range(self): return self._temperature_range @temperature_range.setter def temperature_range(self, t_range): self.locks['temperature_range'].acquire(False) try: low, high = t_range if high < low: raise errors.TemperatureValidationError('Invalid range.') self.validate_temperature(low) self.validate_temperature(high) self._temperature_range = t_range # send update self.publish_temperatures(current=False) finally: self.locks['temperature_range'].release() @property def temperature_range_floor(self): return min(self.temperature_range) @property def temperature_range_ceiling(self): return max(self.temperature_range) @property def temperature_range_equilibrium(self): # DEMO value return self.temperature_range_ceiling # biased towards range ceiling low, high = self.temperature_range bias = (high - low) / 4 equilibrium = sum(self.temperature_range) / 2 return equilibrium + bias
class Logs(object): """ This class implements functions that allow processing logs from device. This class is implemented using pubnub python sdk. For more details about pubnub, please visit: https://www.pubnub.com/docs/python/pubnub-python-sdk """ def __init__(self): self.config = Config() def _init_pubnub(func): @wraps(func) def wrapper(self, *args, **kwargs): if not hasattr(self, 'pubnub'): pubnub_key = self.config.get_all()['pubnub'] self.pubnub = Pubnub(publish_key=pubnub_key['publish_key'], subscribe_key=pubnub_key['subscribe_key']) return func(self, *args, **kwargs) return wrapper @_init_pubnub def subscribe(self, uuid, callback, error): """ This function allows subscribing to device logs. Testing Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#subscribe Examples: >>> def callback(message, channel): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) >>> Logs.subscribe(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.subscribe(channels=channel, callback=callback, error=error) @_init_pubnub def history(self, uuid, callback, error): """ This function allows fetching historical device logs. Args: uuid (str): device uuid. callback (function): this callback is called on receiving a message from the channel. error (function): this callback is called on an error event. For more details about callbacks in pubnub subscribe, visit here: https://www.pubnub.com/docs/python/api-reference#history Examples: >>> def callback(message): ... print(message) >>> def error(message): ... print('Error:'+ str(message)) Logs.history(uuid=uuid, callback=callback, error=error) """ channel = self.get_channel(uuid) self.pubnub.history(channel=channel, callback=callback, error=error) def unsubscribe(self, uuid): """ This function allows unsubscribing to device logs. Args: uuid (str): device uuid. """ if hasattr(self, 'pubnub'): channel = self.get_channel(uuid) self.pubnub.unsubscribe(channel=channel) @staticmethod def get_channel(uuid): """ This function returns pubnub channel for a specific device. Args: uuid (str): device uuid. Returns: str: device channel. """ return 'device-{uuid}-logs'.format(uuid=uuid)
class manageSteeds(Thread): test = os.environ pubnub_publish_key = os.environ['PUBNUB_PUBLISH_KEY'] pubnub_subscribe_key = os.environ['PUBNUB_SUBSCRIBE_KEY'] mongodb_connection = None collection = None def __init__(self): Thread.__init__(self) mongodb_connection = ConnectionToDatabase() self.steeds_collection = mongodb_connection.getCollection( "available_steeds") self.deliveries_collection = mongodb_connection.getCollection( "temp_deliveries") self.deliveries_steeds_collection = mongodb_connection.getCollection( "temp_deliveries_steeds") self.pubnub_settings = Pubnub( publish_key=manageSteeds.pubnub_publish_key, subscribe_key=manageSteeds.pubnub_subscribe_key) # Rename to location channel self.pubnub_channel = "steeds_channel" self.genericDAO = GenericDAO() self.scheduler = sched.scheduler() # Instansiate a scheduler def subscriber_callback(self, message, channel): x = 1 def subscriber_error(self, message): print("ERROR : " + message) def connect(self, message): print("CONNECTED TO STEEDS CHANNEL") def reconnect(self, message): print("RECONNECTED TO STEEDS CHANNEL") def disconnect(self, message): print("DISCONNECTED FROM STEEDS CHANNEL") def subscribe(self): # souscrire au channel self.pubnub_settings.subscribe(channels=self.pubnub_channel, callback=self.subscriber_callback, error=self.subscriber_error, connect=self.connect, reconnect=self.reconnect, disconnect=self.disconnect) def publish(self, message): self.pubnub_settings.publish(channel=self.pubnub_channel, message=message) def unsubscribe(self): # se desinscire du channel self.pubnub_settings.unsubscribe(self.pubnub_channel) # how to instansiate a scheduler #Out of Pubnub functions def manageClientDeliveryRequest(self, iddelivery): # Search delivery by id (criteria) delivery_criteria = {} delivery_criteria["_id"] = iddelivery temp_delivery = self.genericDAO.getOneObject( self, self.deliveries_colllection, delivery_criteria) #Extract pickup coordinates from temporary delivery client_coordinates = [ temp_delivery["pickup_lng"], temp_delivery["pickup_lat"] ] # Get nearest steeds to pickup location nearest_steeds = self.genericDAO.getNearestSteeds( self, self.steeds_collection, client_coordinates) # Save available steeds for delivery delivery_steeds = {} delivery_steeds["iddelivery"] = iddelivery delivery_steeds["available_steeds"] = nearest_steeds self.genericDAO.insertObject(self.deliveries_steeds_collection, delivery_steeds) #Send delivery request to seeder self.sendDeliveryRequestToSteed(iddelivery) def sendDeliveryRequestToSteed(self, iddelivery): # Search delivery by id (criteria) delivery_criteria = {} delivery_criteria["_id"] = iddelivery temp_delivery = self.genericDAO.getOneObject( self, self.deliveries_colllection, delivery_criteria) #Check received_positive_response field received_response = temp_delivery["received_positive_response"] if (not (received_response)): #Search available steeds for delivery delivery_steeds_criteria = {} delivery_steeds_criteria["iddelivery"] = iddelivery available_steeds = self.genericDAO.getOneObject( self, self.deliveries_steeds_collection, delivery_steeds_criteria) #Send steed delivery request self.publish("XXXX" + iddelivery + available_steeds[0]["_id"] + " " + "Other delivery details") #Delete 1st steed from available steeds list new_available_steeds = available_steeds[1:len(available_steeds) - 1] #Update delivery available steeds update_search_criteria = {} update_search_criteria["iddelivery"] = iddelivery update_field = {} update_field["available_steeds"] = new_available_steeds #Add update function to Generic DAO from old projects #Schedule method call self.scheduler.enter(10, 1, self.sendDeliveryRequestToSteed(iddelivery)) self.scheduler.run()