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)
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] )
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}
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}
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))
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))
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 )
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()] }