Ejemplo n.º 1
0
    def set_characteristics(self, chars_query, client_addr):
        """Called from ``HAPServerHandler`` when iOS configures the characteristics.

        :param chars_query: A configuration query. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "value": False, # Value to set
                 "ev": True # (Un)subscribe for events from this characteristics.
              }]
           }

        :type chars_query: dict
        """
        # TODO: Add support for chars that do no support notifications.
        for cq in chars_query[HAP_REPR_CHARS]:
            aid, iid = cq[HAP_REPR_AID], cq[HAP_REPR_IID]
            char = self.accessory.get_characteristic(aid, iid)

            if HAP_PERMISSION_NOTIFY in cq:
                char_topic = get_topic(aid, iid)
                logger.debug('Subscribed client %s to topic %s', client_addr,
                             char_topic)
                self.subscribe_client_topic(client_addr, char_topic,
                                            cq[HAP_PERMISSION_NOTIFY])

            if HAP_REPR_VALUE in cq:
                # TODO: status needs to be based on success of set_value
                char.client_update_value(cq[HAP_REPR_VALUE], client_addr)
Ejemplo n.º 2
0
    def set_characteristics(self, chars_query, client_addr):
        """Called from ``HAPServerHandler`` when iOS configures the characteristics.

        :param chars_query: A configuration query. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "value": False, # Value to set
                 "ev": True # (Un)subscribe for events from this characteristics.
              }]
           }

        :type chars_query: dict
        """
        # TODO: Add support for chars that do no support notifications.
        service_callbacks = {}
        for cq in chars_query[HAP_REPR_CHARS]:
            aid, iid = cq[HAP_REPR_AID], cq[HAP_REPR_IID]
            char = self.accessory.get_characteristic(aid, iid)

            if HAP_PERMISSION_NOTIFY in cq:
                char_topic = get_topic(aid, iid)
                logger.debug(
                    "Subscribed client %s to topic %s", client_addr, char_topic
                )
                self.subscribe_client_topic(
                    client_addr, char_topic, cq[HAP_PERMISSION_NOTIFY]
                )

            if HAP_REPR_VALUE in cq:
                # TODO: status needs to be based on success of set_value
                char.client_update_value(cq[HAP_REPR_VALUE], client_addr)
                # For some services we want to send all the char value
                # changes at once.  This resolves an issue where we send
                # ON and then BRIGHTNESS and the light would go to 100%
                # and then dim to the brightness because each callback
                # would only send one char at a time.
                service = char.service

                if service and service.setter_callback:
                    service_name = service.display_name
                    service_callbacks.setdefault(aid, {})
                    service_callbacks[aid].setdefault(
                        service_name, [service.setter_callback, {}]
                    )
                    service_callbacks[aid][service_name][SERVICE_CALLBACK_DATA][
                        char.display_name
                    ] = cq[HAP_REPR_VALUE]

        for aid in service_callbacks:
            for service_name in service_callbacks[aid]:
                service_callbacks[aid][service_name][SERVICE_CALLBACK](
                    service_callbacks[aid][service_name][SERVICE_CALLBACK_DATA]
                )
Ejemplo n.º 3
0
    def set_characteristics(self, chars_query, client_addr):
        """Called from ``HAPServerHandler`` when iOS configures the characteristics.

        :param chars_query: A configuration query. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "value": False, # Value to set
                 "ev": True # (Un)subscribe for events from this characteristics.
              }]
           }

        :type chars_query: dict

        :return: Response status for each characteristic. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "status": 0,
              }]
           }

        :rtype: dict
        """
        chars_query = chars_query[HAP_REPR_CHARS]
        chars_response = []
        for cq in chars_query:
            aid, iid = cq[HAP_REPR_AID], cq[HAP_REPR_IID]
            char = self.accessory.get_characteristic(aid, iid)

            if HAP_PERMISSION_NOTIFY in cq:
                char_topic = get_topic(aid, iid)
                self.subscribe_client_topic(client_addr, char_topic,
                                            cq[HAP_PERMISSION_NOTIFY])

            response = {
                HAP_REPR_AID: aid,
                HAP_REPR_IID: iid,
                HAP_REPR_STATUS: CHAR_STAT_OK,
            }
            if HAP_REPR_VALUE in cq:
                # TODO: status needs to be based on success of set_value
                char.client_update_value(cq[HAP_REPR_VALUE])
                if "r" in cq:
                    response[HAP_REPR_VALUE] = char.get_value()

            chars_response.append(response)
        return {HAP_REPR_CHARS: chars_response}
Ejemplo n.º 4
0
    def set_characteristics(self, chars_query, client_addr):
        """Called from ``HAPServerHandler`` when iOS configures the characteristics.

        :param chars_query: A configuration query. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "value": False, # Value to set
                 "ev": True # (Un)subscribe for events from this charactertics.
              }]
           }

        :type chars_query: dict

        :return: Response status for each characteristic. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "status": 0,
              }]
           }

        :rtype: dict
        """
        chars_query = chars_query["characteristics"]
        chars_response = []
        for cq in chars_query:
            aid, iid = cq["aid"], cq["iid"]
            char = self.accessory.get_characteristic(aid, iid)

            if "ev" in cq:
                char_topic = get_topic(aid, iid)
                self.subscribe_client_topic(client_addr, char_topic, cq["ev"])

            response = {
                "aid": aid,
                "iid": iid,
                "status": HAP_CONSTANTS.CHAR_STAT_OK,
            }
            if "value" in cq:
                # TODO: status needs to be based on success of set_value
                char.set_value(cq["value"], should_notify=True)
                if "r" in cq:
                    response["value"] = char.value

            chars_response.append(response)
        return {"characteristics": chars_response}
Ejemplo n.º 5
0
    def publish(self, data, sender_client_addr=None):
        """Publishes an event to the client.

        The publishing occurs only if the current client is subscribed to the topic for
        the aid and iid contained in the data.

        :param data: The data to publish. It must at least contain the keys "aid" and
            "iid".
        :type data: dict
        """
        topic = get_topic(data[HAP_REPR_AID], data[HAP_REPR_IID])
        if topic not in self.topics:
            return

        data = {HAP_REPR_CHARS: [data]}
        bytedata = json.dumps(data).encode()
        self.event_queue.put((topic, bytedata, sender_client_addr))
Ejemplo n.º 6
0
    def publish(self, data):
        """Publishes an event to the client.

        The publishing occurs only if the current client is subscribed to the topic for
        the aid and iid contained in the data.

        @param data: The data to publish. It must at least contain the keys "aid" and
            "iid".
        @type data: dict
        """
        topic = get_topic(data["aid"], data["iid"])
        if topic not in self.topics:
            return

        data = {"characteristics": [data]}
        bytedata = json.dumps(data).encode()
        self.event_queue.put((topic, bytedata))
Ejemplo n.º 7
0
    def publish(self, data, sender_client_addr=None):
        """Publishes an event to the client.

        The publishing occurs only if the current client is subscribed to the topic for
        the aid and iid contained in the data.

        :param data: The data to publish. It must at least contain the keys "aid" and
            "iid".
        :type data: dict
        """
        topic = get_topic(data[HAP_REPR_AID], data[HAP_REPR_IID])
        if topic not in self.topics:
            return

        if threading.current_thread() == self.tid:
            self.async_send_event(topic, data, sender_client_addr)
            return

        self.loop.call_soon_threadsafe(
            self.async_send_event, topic, data, sender_client_addr
        )
Ejemplo n.º 8
0
    def set_characteristics(self, chars_query, client_addr):
        """Called from ``HAPServerHandler`` when iOS configures the characteristics.

        :param chars_query: A configuration query. For example:

        .. code-block:: python

           {
              "characteristics": [{
                 "aid": 1,
                 "iid": 2,
                 "value": False, # Value to set
                 "ev": True # (Un)subscribe for events from this characteristics.
              }]
           }

        :type chars_query: dict
        """
        # TODO: Add support for chars that do no support notifications.
        accessory_callbacks = {}
        setter_results = {}
        had_error = False

        for cq in chars_query[HAP_REPR_CHARS]:
            aid, iid = cq[HAP_REPR_AID], cq[HAP_REPR_IID]
            setter_results.setdefault(aid, {})
            char = self.accessory.get_characteristic(aid, iid)

            if HAP_PERMISSION_NOTIFY in cq:
                char_topic = get_topic(aid, iid)
                logger.debug("Subscribed client %s to topic %s", client_addr,
                             char_topic)
                self.async_subscribe_client_topic(client_addr, char_topic,
                                                  cq[HAP_PERMISSION_NOTIFY])

            if HAP_REPR_VALUE not in cq:
                continue

            value = cq[HAP_REPR_VALUE]

            try:
                char.client_update_value(value, client_addr)
            except Exception:  # pylint: disable=broad-except
                logger.exception(
                    "%s: Error while setting characteristic %s to %s",
                    client_addr,
                    char.display_name,
                    value,
                )
                setter_results[aid][
                    iid] = HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE
                had_error = True
            else:
                setter_results[aid][iid] = HAP_SERVER_STATUS.SUCCESS

            # For some services we want to send all the char value
            # changes at once.  This resolves an issue where we send
            # ON and then BRIGHTNESS and the light would go to 100%
            # and then dim to the brightness because each callback
            # would only send one char at a time.
            if not char.service or not char.service.setter_callback:
                continue

            services = accessory_callbacks.setdefault(aid, {})

            if char.service.display_name not in services:
                services[char.service.display_name] = {
                    SERVICE_CALLBACK: char.service.setter_callback,
                    SERVICE_CHARS: {},
                    SERVICE_IIDS: [],
                }

            service_data = services[char.service.display_name]
            service_data[SERVICE_CHARS][char.display_name] = value
            service_data[SERVICE_IIDS].append(iid)

        for aid, services in accessory_callbacks.items():
            for service_name, service_data in services.items():
                try:
                    service_data[SERVICE_CALLBACK](service_data[SERVICE_CHARS])
                except Exception:  # pylint: disable=broad-except
                    logger.exception(
                        "%s: Error while setting characteristics to %s for the %s service",
                        service_data[SERVICE_CHARS],
                        client_addr,
                        service_name,
                    )
                    set_result = HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE
                    had_error = True
                else:
                    set_result = HAP_SERVER_STATUS.SUCCESS

                for iid in service_data[SERVICE_IIDS]:
                    setter_results[aid][iid] = set_result

        if not had_error:
            return None

        return {
            HAP_REPR_CHARS: [{
                HAP_REPR_AID: aid,
                HAP_REPR_IID: iid,
                HAP_REPR_STATUS: status,
            } for aid, iid_status in setter_results.items()
                             for iid, status in iid_status.items()]
        }