def plot_doorstate(args): """Generate plots.""" resp = requests.get( args.url, params={ 'from': to_timestamp(datetime.now(tzlocal()) - timedelta(days=365)) }, ) resp_json = _json_response_error_handling(resp) if not isinstance(resp_json, list): print('Invalid reponse from API:', resp_json) if args.plot_type == 'by-week': plot_by_week(resp_json, args.out) elif args.plot_type == 'by-hour': plot_by_hour(resp_json, args.out)
def update_doorstate(): """Update doorstate (opened, close, ...).""" required_params = {'time', 'state', 'hmac'} data = request.json or request.form # validate try: for param in required_params: if not data.get(param, None): raise ValueError(param, 'Parameter is missing') if not hmac.compare_digest( calculate_hmac(data['time'], data['state'], ARGS.key), data['hmac']): raise ValueError( 'hmac', 'HMAC digest is wrong. Do you have the right key?') if not data['time'].isnumeric(): raise ValueError('time', 'Time has to be an integer timestamp.') time = datetime.fromtimestamp(int(data['time']), tzlocal()) if abs(time - datetime.now(tzlocal())).total_seconds() > 60: raise ValueError( 'time', 'Time is too far in the future or past. Use NTP!') if data['state'] not in DoorState.__members__: raise ValueError( 'state', 'State has to be one of {}.'.format(', '.join( DoorState.__members__.keys()))) state = DoorState[data['state']] latest_door_state = OpeningPeriod.get_latest_state() if latest_door_state: if latest_door_state.state == state: # already opened/closed Event.touch_last_update() return jsonify({ 'time': latest_door_state.last_change_timestamp, 'state': latest_door_state.state.name, '_text': 'door was already {} at {}'.format( latest_door_state.state.name, latest_door_state.last_change_timestamp, ), }) elif latest_door_state.last_change_timestamp >= to_timestamp(time): raise ValueError('time', 'New entry must be newer than latest entry.') elif state == DoorState.closed: # no entry: we assume the door was closed before -> already closed return jsonify({ 'time': 0, 'state': DoorState.closed.name, '_text': "door was already closed." " To be honest, we don't have any data yet but the first entry has to be 'opened'.", }) except ValueError as err: abort(400, {err.args[0]: err.args[1]}) # update doorstate if latest_door_state and latest_door_state.is_open and state == DoorState.closed: latest_door_state.closed = time APP.logger.debug( 'Closing door. Resulting entry: open from %(opened)i till %(closed)i', latest_door_state.to_dict()) elif (not latest_door_state or not latest_door_state.is_open) and state == DoorState.opened: latest_door_state = OpeningPeriod(opened=time) APP.logger.debug( 'Opening door. New entry: open from %(opened)i till t.b.a.', latest_door_state.to_dict()) DB.session.add(latest_door_state) else: abort(500, 'This should not happen') DB.session.commit() Event.touch_last_update() return jsonify({ 'time': latest_door_state.last_change_timestamp, 'state': latest_door_state.state.name, '_text': 'door is now {} (time: {})'.format( latest_door_state.state.name, latest_door_state.last_change_timestamp, ), })
def last_change_timestamp(self): """Return the timestamp of the last change of this entry.""" return to_timestamp(self.opened if self.is_open else self.closed)
def closed_timestamp(self): """Return the integer timestamp for the closed time of this entry.""" return to_timestamp(self.closed) if self.closed else None
def opened_timestamp(self): """Return the integer timestamp for the opened time of this entry.""" return to_timestamp(self.opened)