Ejemplo n.º 1
0
def _decrypt_payload_helper(
    key: str | None,
    ciphertext: str,
    get_key_bytes: Callable[[str, int], str | bytes],
    key_encoder,
) -> dict[str, str] | None:
    """Decrypt encrypted payload."""
    try:
        keylen, decrypt = setup_decrypt(key_encoder)
    except OSError:
        _LOGGER.warning(
            "Ignoring encrypted payload because libsodium not installed")
        return None

    if key is None:
        _LOGGER.warning(
            "Ignoring encrypted payload because no decryption key known")
        return None

    key_bytes = get_key_bytes(key, keylen)

    msg_bytes = decrypt(ciphertext, key_bytes)
    message = json_loads(msg_bytes)
    _LOGGER.debug("Successfully decrypted mobile_app payload")
    return message
Ejemplo n.º 2
0
 def to_native(self, validate_entity_id: bool = True) -> State | None:
     """Convert to an HA state object."""
     context = Context(
         id=self.context_id,
         user_id=self.context_user_id,
         parent_id=self.context_parent_id,
     )
     try:
         attrs = json_loads(self.attributes) if self.attributes else {}
     except JSON_DECODE_EXCEPTIONS:
         # When json_loads fails
         _LOGGER.exception("Error converting row to state: %s", self)
         return None
     if self.last_changed is None or self.last_changed == self.last_updated:
         last_changed = last_updated = process_timestamp(self.last_updated)
     else:
         last_updated = process_timestamp(self.last_updated)
         last_changed = process_timestamp(self.last_changed)
     return State(
         self.entity_id,
         self.state,
         # Join the state_attributes table on attributes_id to get the attributes
         # for newer states
         attrs,
         last_changed,
         last_updated,
         context=context,
         validate_entity_id=validate_entity_id,
     )
Ejemplo n.º 3
0
        def state_received(msg):
            """Handle new MQTT messages."""
            values = json_loads(msg.payload)

            if values["state"] == "ON":
                self._state = True
            elif values["state"] == "OFF":
                self._state = False
            elif values["state"] is None:
                self._state = None

            if self._supported_features and SUPPORT_COLOR and "color" in values:
                if values["color"] is None:
                    self._hs = None
                else:
                    self._update_color(values)

            if self._config[CONF_COLOR_MODE] and "color_mode" in values:
                self._update_color(values)

            if self._supported_features and SUPPORT_BRIGHTNESS:
                try:
                    self._brightness = int(
                        values["brightness"]
                        / float(self._config[CONF_BRIGHTNESS_SCALE])
                        * 255
                    )
                except KeyError:
                    pass
                except (TypeError, ValueError):
                    _LOGGER.warning("Invalid brightness value received")

            if (
                self._supported_features
                and SUPPORT_COLOR_TEMP
                and not self._config[CONF_COLOR_MODE]
            ):
                try:
                    if values["color_temp"] is None:
                        self._color_temp = None
                    else:
                        self._color_temp = int(values["color_temp"])
                except KeyError:
                    pass
                except ValueError:
                    _LOGGER.warning("Invalid color temp value received")

            if self._supported_features and LightEntityFeature.EFFECT:
                with suppress(KeyError):
                    self._effect = values["effect"]

            if self._supported_features and SUPPORT_WHITE_VALUE:
                try:
                    self._white_value = int(values["white_value"])
                except KeyError:
                    pass
                except ValueError:
                    _LOGGER.warning("Invalid white value received")

            self.async_write_ha_state()
Ejemplo n.º 4
0
 def to_native(self) -> dict[str, Any]:
     """Convert to an HA state object."""
     try:
         return cast(dict[str, Any], json_loads(self.shared_data))
     except JSON_DECODE_EXCEPTIONS:
         _LOGGER.exception("Error converting row to event data: %s", self)
         return {}
Ejemplo n.º 5
0
    async def post(self, request, domain, service):
        """Call a service.

        Returns a list of changed states.
        """
        hass: ha.HomeAssistant = request.app["hass"]
        body = await request.text()
        try:
            data = json_loads(body) if body else None
        except ValueError:
            return self.json_message("Data should be valid JSON.",
                                     HTTPStatus.BAD_REQUEST)

        context = self.context(request)

        try:
            await hass.services.async_call(domain,
                                           service,
                                           data,
                                           blocking=True,
                                           context=context)
        except (vol.Invalid, ServiceNotFound) as ex:
            raise HTTPBadRequest() from ex

        changed_states = []

        for state in hass.states.async_all():
            if state.context is context:
                changed_states.append(state)

        return self.json(changed_states)
Ejemplo n.º 6
0
    async def post(self, request, event_type):
        """Fire events."""
        if not request["hass_user"].is_admin:
            raise Unauthorized()
        body = await request.text()
        try:
            event_data = json_loads(body) if body else None
        except ValueError:
            return self.json_message("Event data should be valid JSON.",
                                     HTTPStatus.BAD_REQUEST)

        if event_data is not None and not isinstance(event_data, dict):
            return self.json_message("Event data should be a JSON object",
                                     HTTPStatus.BAD_REQUEST)

        # Special case handling for event STATE_CHANGED
        # We will try to convert state dicts back to State objects
        if event_type == ha.EVENT_STATE_CHANGED and event_data:
            for key in ("old_state", "new_state"):
                state = ha.State.from_dict(event_data.get(key))

                if state:
                    event_data[key] = state

        request.app["hass"].bus.async_fire(event_type, event_data,
                                           ha.EventOrigin.remote,
                                           self.context(request))

        return self.json_message(f"Event {event_type} fired.")
Ejemplo n.º 7
0
 def to_native(self) -> dict[str, Any]:
     """Convert to an HA state object."""
     try:
         return cast(dict[str, Any], json_loads(self.shared_attrs))
     except JSON_DECODE_EXCEPTIONS:
         # When json_loads fails
         _LOGGER.exception("Error converting row to state attributes: %s",
                           self)
         return {}
Ejemplo n.º 8
0
 def state_message_received(msg):
     """Handle state MQTT message."""
     payload = json_loads(msg.payload)
     if STATE in payload and (payload[STATE] in POSSIBLE_STATES
                              or payload[STATE] is None):
         self._state = (POSSIBLE_STATES[payload[STATE]]
                        if payload[STATE] else None)
         del payload[STATE]
     self._state_attrs.update(payload)
     self.async_write_ha_state()
Ejemplo n.º 9
0
Archivo: siren.py Proyecto: jbouwh/core
        def state_message_received(msg):
            """Handle new MQTT state messages."""
            payload = self._value_template(msg.payload)
            if not payload or payload == PAYLOAD_EMPTY_JSON:
                _LOGGER.debug(
                    "Ignoring empty payload '%s' after rendering for topic %s",
                    payload,
                    msg.topic,
                )
                return
            json_payload = {}
            if payload in [self._state_on, self._state_off, PAYLOAD_NONE]:
                json_payload = {STATE: payload}
            else:
                try:
                    json_payload = json_loads(payload)
                    _LOGGER.debug(
                        "JSON payload detected after processing payload '%s' on topic %s",
                        json_payload,
                        msg.topic,
                    )
                except JSON_DECODE_EXCEPTIONS:
                    _LOGGER.warning(
                        "No valid (JSON) payload detected after processing payload '%s' on topic %s",
                        json_payload,
                        msg.topic,
                    )
                    return
            if STATE in json_payload:
                if json_payload[STATE] == self._state_on:
                    self._attr_is_on = True
                if json_payload[STATE] == self._state_off:
                    self._attr_is_on = False
                if json_payload[STATE] == PAYLOAD_NONE:
                    self._attr_is_on = None
                del json_payload[STATE]

            if json_payload:
                # process attributes
                try:
                    vol.All(TURN_ON_SCHEMA)(json_payload)
                except vol.MultipleInvalid as invalid_siren_parameters:
                    _LOGGER.warning(
                        "Unable to update siren state attributes from payload '%s': %s",
                        json_payload,
                        invalid_siren_parameters,
                    )
                    return
            self._update(process_turn_on_params(self, json_payload))
            self.async_write_ha_state()
Ejemplo n.º 10
0
        def position_message_received(msg):
            """Handle new MQTT position messages."""
            payload = self._get_position_template(msg.payload)

            if not payload:
                _LOGGER.debug("Ignoring empty position message from '%s'", msg.topic)
                return

            try:
                payload = json_loads(payload)
            except JSON_DECODE_EXCEPTIONS:
                pass

            if isinstance(payload, dict):
                if "position" not in payload:
                    _LOGGER.warning(
                        "Template (position_template) returned JSON without position attribute"
                    )
                    return
                if "tilt_position" in payload:
                    if not self._config.get(CONF_TILT_STATE_OPTIMISTIC):
                        # reset forced set tilt optimistic
                        self._tilt_optimistic = False
                    self.tilt_payload_received(payload["tilt_position"])
                payload = payload["position"]

            try:
                percentage_payload = self.find_percentage_in_range(
                    float(payload), COVER_PAYLOAD
                )
            except ValueError:
                _LOGGER.warning("Payload '%s' is not numeric", payload)
                return

            self._position = percentage_payload
            if self._config.get(CONF_STATE_TOPIC) is None:
                self._state = (
                    STATE_CLOSED
                    if percentage_payload == DEFAULT_POSITION_CLOSED
                    else STATE_OPEN
                )

            self.async_write_ha_state()
Ejemplo n.º 11
0
 def to_native(self, validate_entity_id: bool = True) -> Event | None:
     """Convert to a native HA Event."""
     context = Context(
         id=self.context_id,
         user_id=self.context_user_id,
         parent_id=self.context_parent_id,
     )
     try:
         return Event(
             self.event_type,
             json_loads(self.event_data) if self.event_data else {},
             EventOrigin(self.origin)
             if self.origin else EVENT_ORIGIN_ORDER[self.origin_idx],
             process_timestamp(self.time_fired),
             context=context,
         )
     except JSON_DECODE_EXCEPTIONS:
         # When json_loads fails
         _LOGGER.exception("Error converting to event: %s", self)
         return None
Ejemplo n.º 12
0
 def attributes_message_received(msg: ReceiveMessage) -> None:
     try:
         payload = attr_tpl(msg.payload)
         json_dict = json_loads(payload) if isinstance(payload,
                                                       str) else None
         if isinstance(json_dict, dict):
             filtered_dict = {
                 k: v
                 for k, v in json_dict.items()
                 if k not in MQTT_ATTRIBUTES_BLOCKED
                 and k not in self._attributes_extra_blocked
             }
             self._attributes = filtered_dict
             self.async_write_ha_state()
         else:
             _LOGGER.warning("JSON result was not a dictionary")
             self._attributes = None
     except ValueError:
         _LOGGER.warning("Erroneous JSON: %s", payload)
         self._attributes = None
Ejemplo n.º 13
0
    def mqtt_automation_listener(mqttmsg):
        """Listen for MQTT messages."""
        payload = mqttmsg.payload

        if value_template is not None:
            payload = value_template.async_render_with_possible_json_value(
                payload,
                error_value=None,
            )

        if wanted_payload is None or wanted_payload == payload:
            data = {
                **trigger_data,
                "platform": "mqtt",
                "topic": mqttmsg.topic,
                "payload": mqttmsg.payload,
                "qos": mqttmsg.qos,
                "description": f"mqtt topic {mqttmsg.topic}",
            }

            with suppress(ValueError):
                data["payload_json"] = json_loads(mqttmsg.payload)

            hass.async_run_hass_job(job, {"trigger": data})
Ejemplo n.º 14
0
async def async_start(  # noqa: C901
        hass: HomeAssistant, discovery_topic, config_entry=None) -> None:
    """Start MQTT Discovery."""
    mqtt_integrations = {}

    async def async_discovery_message_received(msg):
        """Process the received message."""
        hass.data[LAST_DISCOVERY] = time.time()
        payload = msg.payload
        topic = msg.topic
        topic_trimmed = topic.replace(f"{discovery_topic}/", "", 1)

        if not (match := TOPIC_MATCHER.match(topic_trimmed)):
            if topic_trimmed.endswith("config"):
                _LOGGER.warning(
                    "Received message on illegal discovery topic '%s'. The topic contains "
                    "not allowed characters. For more information see "
                    "https://www.home-assistant.io/docs/mqtt/discovery/#discovery-topic",
                    topic,
                )
            return

        component, node_id, object_id = match.groups()

        if component not in SUPPORTED_COMPONENTS:
            _LOGGER.warning("Integration %s is not supported", component)
            return

        if payload:
            try:
                payload = json_loads(payload)
            except ValueError:
                _LOGGER.warning("Unable to parse JSON %s: '%s'", object_id,
                                payload)
                return

        payload = MQTTConfig(payload)

        for key in list(payload):
            abbreviated_key = key
            key = ABBREVIATIONS.get(key, key)
            payload[key] = payload.pop(abbreviated_key)

        if CONF_DEVICE in payload:
            device = payload[CONF_DEVICE]
            for key in list(device):
                abbreviated_key = key
                key = DEVICE_ABBREVIATIONS.get(key, key)
                device[key] = device.pop(abbreviated_key)

        if TOPIC_BASE in payload:
            base = payload.pop(TOPIC_BASE)
            for key, value in payload.items():
                if isinstance(value, str) and value:
                    if value[0] == TOPIC_BASE and key.endswith("topic"):
                        payload[key] = f"{base}{value[1:]}"
                    if value[-1] == TOPIC_BASE and key.endswith("topic"):
                        payload[key] = f"{value[:-1]}{base}"
            if payload.get(CONF_AVAILABILITY):
                for availability_conf in cv.ensure_list(
                        payload[CONF_AVAILABILITY]):
                    if not isinstance(availability_conf, dict):
                        continue
                    if topic := availability_conf.get(CONF_TOPIC):
                        if topic[0] == TOPIC_BASE:
                            availability_conf[
                                CONF_TOPIC] = f"{base}{topic[1:]}"
                        if topic[-1] == TOPIC_BASE:
                            availability_conf[
                                CONF_TOPIC] = f"{topic[:-1]}{base}"
Ejemplo n.º 15
0
    def _update_from_rest_data(self):
        """Update state from the rest data."""
        value = self.rest.data
        _LOGGER.debug("Data fetched from resource: %s", value)
        if self.rest.headers is not None:
            # If the http request failed, headers will be None
            content_type = self.rest.headers.get("content-type")

            if content_type and (
                    content_type.startswith("text/xml")
                    or content_type.startswith("application/xml")
                    or content_type.startswith("application/xhtml+xml")
                    or content_type.startswith("application/rss+xml")):
                try:
                    value = json_dumps(xmltodict.parse(value))
                    _LOGGER.debug("JSON converted from XML: %s", value)
                except ExpatError:
                    _LOGGER.warning(
                        "REST xml result could not be parsed and converted to JSON"
                    )
                    _LOGGER.debug("Erroneous XML: %s", value)

        if self._json_attrs:
            self._attributes = {}
            if value:
                try:
                    json_dict = json_loads(value)
                    if self._json_attrs_path is not None:
                        json_dict = jsonpath(json_dict, self._json_attrs_path)
                    # jsonpath will always store the result in json_dict[0]
                    # so the next line happens to work exactly as needed to
                    # find the result
                    if isinstance(json_dict, list):
                        json_dict = json_dict[0]
                    if isinstance(json_dict, dict):
                        attrs = {
                            k: json_dict[k]
                            for k in self._json_attrs if k in json_dict
                        }
                        self._attributes = attrs
                    else:
                        _LOGGER.warning(
                            "JSON result was not a dictionary"
                            " or list with 0th element a dictionary")
                except ValueError:
                    _LOGGER.warning("REST result could not be parsed as JSON")
                    _LOGGER.debug("Erroneous JSON: %s", value)

            else:
                _LOGGER.warning("Empty reply found when expecting JSON data")

        if value is not None and self._value_template is not None:
            value = self._value_template.async_render_with_possible_json_value(
                value, None)

        if value is None or self.device_class not in (
                SensorDeviceClass.DATE,
                SensorDeviceClass.TIMESTAMP,
        ):
            self._state = value
            return

        self._state = async_parse_date_datetime(value, self.entity_id,
                                                self.device_class)
Ejemplo n.º 16
0
        return (other.__class__ in [self.__class__, State]
                and self.entity_id == other.entity_id
                and self.state == other.state
                and self.attributes == other.attributes)


def decode_attributes_from_row(
        row: Row, attr_cache: dict[str, dict[str, Any]]) -> dict[str, Any]:
    """Decode attributes from a database row."""
    source: str = row.shared_attrs or row.attributes
    if (attributes := attr_cache.get(source)) is not None:
        return attributes
    if not source or source == EMPTY_JSON_OBJECT:
        return {}
    try:
        attr_cache[source] = attributes = json_loads(source)
    except ValueError:
        _LOGGER.exception("Error converting row to state attributes: %s",
                          source)
        attr_cache[source] = attributes = {}
    return attributes


def row_to_compressed_state(
    row: Row,
    attr_cache: dict[str, dict[str, Any]],
    start_time: datetime | None = None,
) -> dict[str, Any]:
    """Convert a database row to a compressed state."""
    comp_state = {
        COMPRESSED_STATE_STATE: row.state,