def __init__( self, entity_id: str, state: str, attributes: Optional[Mapping] = None, last_changed: Optional[datetime.datetime] = None, last_updated: Optional[datetime.datetime] = None, context: Optional[Context] = None, # Temp, because database can still store invalid entity IDs # Remove with 1.0 or in 2020. temp_invalid_id_bypass: Optional[bool] = False, ) -> None: """Initialize a new state.""" state = str(state) if not valid_entity_id(entity_id) and not temp_invalid_id_bypass: raise InvalidEntityFormatError( ("Invalid entity id encountered: {}. " "Format should be <domain>.<object_id>").format(entity_id)) if not valid_state(state): raise InvalidStateError( ("Invalid state encountered for entity id: {}. " "State max length is 255 characters.").format(entity_id)) self.entity_id = entity_id.lower() self.state = state self.attributes = MappingProxyType(attributes or {}) self.last_updated = last_updated or dt_util.utcnow() self.last_changed = last_changed or self.last_updated self.context = context or Context()
def __init__(self, entity_id: str, state: Any, attributes: Optional[Dict] = None, last_changed: Optional[datetime.datetime] = None, last_updated: Optional[datetime.datetime] = None, context: Optional[Context] = None) -> None: """Initialize a new state.""" state = str(state) if not valid_entity_id(entity_id): raise InvalidEntityFormatError( ("Invalid entity id encountered: {}. " "Format should be <domain>.<object_id>").format(entity_id)) if not valid_state(state): raise InvalidStateError( ("Invalid state encountered for entity id: {}. " "State max length is 255 characters.").format(entity_id)) self.entity_id = entity_id.lower() self.state = state self.attributes = MappingProxyType(attributes or {}) self.last_updated = last_updated or dt_util.utcnow() self.last_changed = last_changed or self.last_updated self.context = context or Context()
async def get(self, request, datetime=None): """Retrieve logbook entries.""" if datetime: datetime = dt_util.parse_datetime(datetime) if datetime is None: return self.json_message("Invalid datetime", HTTP_BAD_REQUEST) else: datetime = dt_util.start_of_local_day() period = request.query.get("period") if period is None: period = 1 else: period = int(period) entity_ids = request.query.get("entity") if entity_ids: try: entity_ids = cv.entity_ids(entity_ids) except vol.Invalid: raise InvalidEntityFormatError( f"Invalid entity id(s) encountered: {entity_ids}. " "Format should be <domain>.<object_id>") from vol.Invalid end_time = request.query.get("end_time") if end_time is None: start_day = dt_util.as_utc(datetime) - timedelta(days=period - 1) end_day = start_day + timedelta(days=period) else: start_day = datetime end_day = dt_util.parse_datetime(end_time) if end_day is None: return self.json_message("Invalid end_time", HTTP_BAD_REQUEST) hass = request.app["hass"] entity_matches_only = "entity_matches_only" in request.query context_id = request.query.get("context_id") if entity_ids and context_id: return self.json_message("Can't combine entity with context_id", HTTP_BAD_REQUEST) def json_events(): """Fetch events and generate JSON.""" return self.json( _get_events( hass, start_day, end_day, entity_ids, self.filters, self.entities_filter, entity_matches_only, context_id, )) return await hass.async_add_executor_job(json_events)
def __init__(self, entity_id, state, attributes=None, last_changed=None, last_updated=None): """Initialize a new state.""" if not valid_entity_id(entity_id): raise InvalidEntityFormatError(( "Invalid entity id encountered: {}. " "Format should be <domain>.<object_id>").format(entity_id)) self.entity_id = entity_id.lower() self.state = str(state) self.attributes = MappingProxyType(attributes or {}) self.last_updated = last_updated or dt_util.utcnow() self.last_changed = last_changed or self.last_updated
def __init__(self, entity_id, state, attributes=None, last_changed=None, last_updated=None): if not ENTITY_ID_PATTERN.match(entity_id): raise InvalidEntityFormatError(( "Invalid entity id encountered: {}. " "Format should be <domain>.<object_id>").format(entity_id)) self.entity_id = entity_id.lower() self.state = state self.attributes = attributes or {} self.last_updated = dt_util.strip_microseconds( last_updated or dt_util.utcnow()) # Strip microsecond from last_changed else we cannot guarantee # state == State.from_dict(state.as_dict()) # This behavior occurs because to_dict uses datetime_to_str # which does not preserve microseconds self.last_changed = dt_util.strip_microseconds( last_changed or self.last_updated)
return self.json_message("Invalid datetime", HTTPStatus.BAD_REQUEST) else: datetime_dt = dt_util.start_of_local_day() if (period_str := request.query.get("period")) is None: period: int = 1 else: period = int(period_str) if entity_ids_str := request.query.get("entity"): try: entity_ids = cv.entity_ids(entity_ids_str) except vol.Invalid: raise InvalidEntityFormatError( f"Invalid entity id(s) encountered: {entity_ids_str}. " "Format should be <domain>.<object_id>") from vol.Invalid else: entity_ids = None if (end_time_str := request.query.get("end_time")) is None: start_day = dt_util.as_utc(datetime_dt) - timedelta(days=period - 1) end_day = start_day + timedelta(days=period) else: start_day = datetime_dt if (end_day_dt := dt_util.parse_datetime(end_time_str)) is None: return self.json_message("Invalid end_time", HTTPStatus.BAD_REQUEST) end_day = end_day_dt
def _get_events( hass, start_day, end_day, entity_id=None, filters=None, entities_filter=None, entity_matches_only=False, ): """Get events for a period of time.""" entity_attr_cache = EntityAttributeCache(hass) context_lookup = {None: None} entity_id_lower = None apply_sql_entities_filter = True def yield_events(query): """Yield Events that are not filtered away.""" for row in query.yield_per(1000): event = LazyEventPartialState(row) context_lookup.setdefault(event.context_id, event) if _keep_event(hass, event, entities_filter): yield event if entity_id is not None: entity_id_lower = entity_id.lower() if not valid_entity_id(entity_id_lower): raise InvalidEntityFormatError( f"Invalid entity id encountered: {entity_id_lower}. " "Format should be <domain>.<object_id>") entities_filter = generate_filter([], [entity_id_lower], [], []) apply_sql_entities_filter = False with session_scope(hass=hass) as session: old_state = aliased(States, name="old_state") query = ( session.query( Events.event_type, Events.event_data, Events.time_fired, Events.context_id, Events.context_user_id, States.state, States.entity_id, States.domain, States.attributes, ).order_by(Events.time_fired).outerjoin( States, (Events.event_id == States.event_id)).outerjoin( old_state, (States.old_state_id == old_state.state_id)) # The below filter, removes state change events that do not have # and old_state, new_state, or the old and # new state. # .filter((Events.event_type != EVENT_STATE_CHANGED) | ((States.state_id.isnot(None)) & (old_state.state_id.isnot(None)) & (States.state.isnot(None)) & (States.state != old_state.state))) # # Prefilter out continuous domains that have # ATTR_UNIT_OF_MEASUREMENT as its much faster in sql. # .filter((Events.event_type != EVENT_STATE_CHANGED) | sqlalchemy.not_(States.domain.in_(CONTINUOUS_DOMAINS)) | sqlalchemy.not_( States.attributes.contains(UNIT_OF_MEASUREMENT_JSON))). filter( Events.event_type.in_(ALL_EVENT_TYPES + list(hass.data.get(DOMAIN, {})))).filter( (Events.time_fired > start_day) & (Events.time_fired < end_day))) if entity_id_lower is not None: if entity_matches_only: # When entity_matches_only is provided, contexts and events that do not # contain the entity_id are not included in the logbook response. entity_id_json = ENTITY_ID_JSON_TEMPLATE.format( entity_id_lower) query = query.filter( ((States.last_updated == States.last_changed) & (States.entity_id == entity_id_lower)) | (States.state_id.is_(None) & Events.event_data.contains(entity_id_json))) else: query = query.filter(( (States.last_updated == States.last_changed) & (States.entity_id == entity_id_lower)) | (States.state_id.is_(None))) else: query = query.filter((States.last_updated == States.last_changed) | (States.state_id.is_(None))) if apply_sql_entities_filter and filters: entity_filter = filters.entity_filter() if entity_filter is not None: query = query.filter(entity_filter | ( Events.event_type != EVENT_STATE_CHANGED)) return list( humanify(hass, yield_events(query), entity_attr_cache, context_lookup))