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)
Пример #3
0
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
Пример #4
0
    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)
Пример #5
0
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,
                }