def get_sensors(self): logging.debug("getting temperature and other sensor data") try: val = requests.get("https://dweet.io/get/latest/dweet/for/%s-sensors" % self._thingid, timeout=self._requests_timeout).json() delta = (TimeStamp()-TimeStamp.from_iso_str(val["with"][0]["created"])).total_seconds()*1000 if delta > self._temp_tolerance_ms: raise Exception("temperature data is too old (> %d ms)" % self._temp_tolerance_ms) self.temp = float(val["with"][0]["content"]["temp"]); self._humi = float(val["with"][0]["content"].get("humi", None)); self._sensors_errors = 0 except Exception as e: self._sensors_errors = self._sensors_errors + 1 if self._sensors_errors >= self._sensors_errors_tolerance: logging.error("too many sensors reading errors (%d out of %d): resetting data: %s" % (self._sensors_errors, self._sensors_errors_tolerance, e)) self.temp = None self._humi = None else: logging.warning("sensor error, retaining old data: %s" % e) logging.debug("sensor data: temp=%s, humidity=%s" % (self.temp and "%.1f°C" % self.temp or "n/a", self._humi and "%.1f%%" % self._humi or "n/a"))
def get_latest_command(self): logging.debug('checking for commands') skipped = 0 try: r = requests.get("https://dweet.io/get/dweets/for/%s" % self._thingid, timeout=self._requests_timeout) except requests.exceptions.RequestException as e: logging.error('failed to get latest commands: %s' % e) return False if r.status_code != 200: logging.error('invalid status code received: %d' % r.status_code) return False try: for idx,item in enumerate( r.json()['with'] ): try: now_ts = TimeStamp() msg_ts = TimeStamp.from_iso_str( item['created'] ) if (now_ts-msg_ts).total_seconds() <= self._cmd_expiry_s: # consider this message: it is still valid # nonce must be unique nonce = item['content']['nonce'] nonce_is_unique = True for ridx, ritem in reversed( list(enumerate(r.json()['with']))[idx+1:] ): if 'nonce' in ritem['content'] and nonce == ritem['content']['nonce']: nonce_is_unique = False break if nonce_is_unique: msg = self.decrypt_msg(item['content']) if not nonce_is_unique: logging.warning('duplicated nonce (%s): possible replay attack, ignoring' % nonce) elif msg is None: logging.warning('cannot decrypt message, check password') else: msg_real_ts = TimeStamp.from_iso_str( msg['timestamp'] ) msg_delta = msg_ts - msg_real_ts if abs(msg_delta.total_seconds())*1000 > self._tolerance_ms: logging.warning('message timestamps mismatch, ignoring') elif msg['type'].lower() == 'command': logging.debug('found valid command') logging.debug(json.dumps(msg, indent=2)) save = False if msg['override_program'] != self._override_program: save = True if msg['program'] != self._program: save = True lid = msg['id'] self._override_program = msg['override_program'] self._program = msg['program'] self._lastcmd_id = lid if save: if self.save_conf(): logging.info('new configuration saved') else: logging.error('cannot save new configuration') break else: # messages from now on are too old, no need to parse the rest logging.debug('found first outdated message, skipping the rest') break except (KeyError, TypeError) as e: logging.debug('error parsing, skipped: %s' % e) skipped = skipped+1 pass except Exception as e: logging.error('error parsing response: %s' % e) return False if skipped > 0: logging.warning('invalid messages skipped: %d' % skipped) return True