def from_dict(cls, json_dict: Dict) -> "StoredState": """Initialize a stored state from a dict.""" last_seen = json_dict["last_seen"] if isinstance(last_seen, str): last_seen = dt_util.parse_datetime(last_seen) return cls(State.from_dict(json_dict["state"]), last_seen)
def async_restore_entity_removed(self, entity_id: str) -> None: """Unregister this entity from saving state.""" # When an entity is being removed from opp, store its last state. This # allows us to support state restoration if the entity is removed, then # re-added while opp is still running. state = self.opp.states.get(entity_id) # To fully mimic all the attribute data types when loaded from storage, # we're going to serialize it to JSON and then re-load it. if state is not None: state = State.from_dict(_encode_complex(state.as_dict())) if state is not None: self.last_states[entity_id] = StoredState(state, dt_util.utcnow()) self.entity_ids.remove(entity_id)
def mock_restore_cache(opp, states): """Mock the DATA_RESTORE_CACHE.""" key = restore_state.DATA_RESTORE_STATE_TASK data = restore_state.RestoreStateData(opp) now = date_util.utcnow() last_states = {} for state in states: restored_state = state.as_dict() restored_state["attributes"] = json.loads( json.dumps(restored_state["attributes"], cls=JSONEncoder)) last_states[state.entity_id] = restore_state.StoredState( State.from_dict(restored_state), now) data.last_states = last_states _LOGGER.debug("Restore cache: %s", data.last_states) assert len( data.last_states) == len(states), f"Duplicate entity_id? {states}" opp.data[key] = data
def _event_receiver(msg): """Receive events published by and fire them on this opp instance.""" event = json.loads(msg.payload) event_type = event.get("event_type") event_data = event.get("event_data") # Don't fire OPENPEERPOWER_* events on this instance if event_type in BLOCKED_EVENTS: return # Special case handling for event STATE_CHANGED # We will try to convert state dicts back to State objects # Copied over from the _handle_api_post_events_event method # of the api component. if event_type == EVENT_STATE_CHANGED and event_data: for key in ("old_state", "new_state"): state = State.from_dict(event_data.get(key)) if state: event_data[key] = state opp.bus.async_fire(event_type, event_data=event_data, origin=EventOrigin.remote)
def humanify(opp, events): """Generate a converted list of events into Entry objects. Will try to group events if possible: - if 2+ sensor updates in GROUP_BY_MINUTES, show last - if Open Peer Power stop and start happen in same minute call it restarted """ domain_prefixes = tuple(f"{dom}." for dom in CONTINUOUS_DOMAINS) # Group events in batches of GROUP_BY_MINUTES for _, g_events in groupby( events, lambda event: event.time_fired.minute // GROUP_BY_MINUTES): events_batch = list(g_events) # Keep track of last sensor states last_sensor_event = {} # Group HA start/stop events # Maps minute of event to 1: stop, 2: stop + start start_stop_events = {} # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: entity_id = event.data.get("entity_id") if entity_id.startswith(domain_prefixes): last_sensor_event[entity_id] = event elif event.event_type == EVENT_OPENPEERPOWER_STOP: if event.time_fired.minute in start_stop_events: continue start_stop_events[event.time_fired.minute] = 1 elif event.event_type == EVENT_OPENPEERPOWER_START: if event.time_fired.minute not in start_stop_events: continue start_stop_events[event.time_fired.minute] = 2 # Yield entries for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: to_state = State.from_dict(event.data.get("new_state")) domain = to_state.domain # Skip all but the last sensor state if (domain in CONTINUOUS_DOMAINS and event != last_sensor_event[to_state.entity_id]): continue # Don't show continuous sensor value changes in the logbook if domain in CONTINUOUS_DOMAINS and to_state.attributes.get( "unit_of_measurement"): continue yield { "when": event.time_fired, "name": to_state.name, "message": _entry_message_from_state(domain, to_state), "domain": domain, "entity_id": to_state.entity_id, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_OPENPEERPOWER_START: if start_stop_events.get(event.time_fired.minute) == 2: continue yield { "when": event.time_fired, "name": "Open Peer Power", "message": "started", "domain": HA_DOMAIN, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_OPENPEERPOWER_STOP: if start_stop_events.get(event.time_fired.minute) == 2: action = "restarted" else: action = "stopped" yield { "when": event.time_fired, "name": "Open Peer Power", "message": action, "domain": HA_DOMAIN, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_LOGBOOK_ENTRY: domain = event.data.get(ATTR_DOMAIN) entity_id = event.data.get(ATTR_ENTITY_ID) if domain is None and entity_id is not None: try: domain = split_entity_id(str(entity_id))[0] except IndexError: pass yield { "when": event.time_fired, "name": event.data.get(ATTR_NAME), "message": event.data.get(ATTR_MESSAGE), "domain": domain, "entity_id": entity_id, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_ALEXA_SMART_HOME: data = event.data entity_id = data["request"].get("entity_id") if entity_id: state = opp.states.get(entity_id) name = state.name if state else entity_id message = "send command {}/{} for {}".format( data["request"]["namespace"], data["request"]["name"], name) else: message = "send command {}/{}".format( data["request"]["namespace"], data["request"]["name"]) yield { "when": event.time_fired, "name": "Amazon Alexa", "message": message, "domain": "alexa", "entity_id": entity_id, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_HOMEKIT_CHANGED: data = event.data entity_id = data.get(ATTR_ENTITY_ID) value = data.get(ATTR_VALUE) value_msg = f" to {value}" if value else "" message = "send command {}{} for {}".format( data[ATTR_SERVICE], value_msg, data[ATTR_DISPLAY_NAME]) yield { "when": event.time_fired, "name": "HomeKit", "message": message, "domain": DOMAIN_HOMEKIT, "entity_id": entity_id, "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_AUTOMATION_TRIGGERED: yield { "when": event.time_fired, "name": event.data.get(ATTR_NAME), "message": "has been triggered", "domain": "automation", "entity_id": event.data.get(ATTR_ENTITY_ID), "context_id": event.context.id, "context_user_id": event.context.user_id, } elif event.event_type == EVENT_SCRIPT_STARTED: yield { "when": event.time_fired, "name": event.data.get(ATTR_NAME), "message": "started", "domain": "script", "entity_id": event.data.get(ATTR_ENTITY_ID), "context_id": event.context.id, "context_user_id": event.context.user_id, }