def handle_data(self, command, msg): """Handle changes to the mailbox.""" from asterisk_mbox.commands import ( CMD_MESSAGE_LIST, CMD_MESSAGE_CDR_AVAILABLE, CMD_MESSAGE_CDR) if command == CMD_MESSAGE_LIST: _LOGGER.debug("AsteriskVM sent updated message list: Len %d", len(msg)) old_messages = self.messages self.messages = sorted( msg, key=lambda item: item['info']['origtime'], reverse=True) if not isinstance(old_messages, list): async_dispatcher_send( self.hass, SIGNAL_DISCOVER_PLATFORM, DOMAIN) async_dispatcher_send(self.hass, SIGNAL_MESSAGE_UPDATE, self.messages) elif command == CMD_MESSAGE_CDR: _LOGGER.debug("AsteriskVM sent updated CDR list: Len %d", len(msg.get('entries', []))) self.cdr = msg['entries'] async_dispatcher_send(self.hass, SIGNAL_CDR_UPDATE, self.cdr) elif command == CMD_MESSAGE_CDR_AVAILABLE: if not isinstance(self.cdr, list): _LOGGER.debug("AsteriskVM adding CDR platform") self.cdr = [] async_dispatcher_send(self.hass, SIGNAL_DISCOVER_PLATFORM, "asterisk_cdr") async_dispatcher_send(self.hass, SIGNAL_CDR_REQUEST) else: _LOGGER.debug("AsteriskVM sent unknown message '%d' len: %d", command, len(msg))
def async_fire_mqtt_message(hass, topic, payload, qos=0): """Fire the MQTT message.""" if isinstance(payload, str): payload = payload.encode('utf-8') dispatcher.async_dispatcher_send( hass, mqtt.SIGNAL_MQTT_MESSAGE_RECEIVED, topic, payload, qos)
async def async_set_climate(self, toggle): """Set climate control mode via Nissan servers.""" climate_result = None if toggle: _LOGGER.debug("Requesting climate turn on for %s", self.leaf.vin) set_function = self.leaf.start_climate_control result_function = self.leaf.get_start_climate_control_result else: _LOGGER.debug("Requesting climate turn off for %s", self.leaf.vin) set_function = self.leaf.stop_climate_control result_function = self.leaf.get_stop_climate_control_result request = await self.hass.async_add_executor_job(set_function) for attempt in range(MAX_RESPONSE_ATTEMPTS): if attempt > 0: _LOGGER.debug("Climate data not in yet (%s) (%s). " "Waiting (%s) seconds", self.leaf.vin, attempt, PYCARWINGS2_SLEEP) await asyncio.sleep(PYCARWINGS2_SLEEP) climate_result = await self.hass.async_add_executor_job( result_function, request) if climate_result is not None: break if climate_result is not None: _LOGGER.debug("Climate result: %s", climate_result.__dict__) async_dispatcher_send(self.hass, SIGNAL_UPDATE_LEAF) return climate_result.is_hvac_running == toggle _LOGGER.debug("Climate result not returned by Nissan servers") return False
async def set_control_setpoint(call): """Set the control setpoint on the OpenTherm Gateway.""" gw_var = gw_vars.DATA_CONTROL_SETPOINT value = await gateway.set_control_setpoint(call.data[ATTR_TEMPERATURE]) status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] status.update({gw_var: value}) async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
async def async_handle_temp_update(hass, context, msg): """Handle a temperature sensor state update.""" _LOGGER.debug("[temp handler] context: %s msg: %s", context, msg) entity_id, temp = context.get(DEVICE_CLASS_TEMPERATURE), msg.get('temp') if entity_id: async_dispatcher_send( hass, SIGNAL_SENSOR_UPDATE.format(entity_id), temp)
async def _sync(self): """Update local list of devices.""" if not await self._hass.async_add_executor_job( self._client.update) and self._is_available: self._is_available = False _LOGGER.warning("Device is unavailable") return async def new_device(device_id, component): """Load new device.""" config_entries_key = '{}.{}'.format(component, DOMAIN) async with self._hass.data[DATA_CONFIG_ENTRY_LOCK]: if config_entries_key not in self._hass.data[ CONFIG_ENTRY_IS_SETUP]: await self._hass.config_entries.async_forward_entry_setup( self._config_entry, component) self._hass.data[CONFIG_ENTRY_IS_SETUP].add( config_entries_key) async_dispatcher_send( self._hass, POINT_DISCOVERY_NEW.format(component, DOMAIN), device_id) self._is_available = True for home_id in self._client.homes: if home_id not in self._known_homes: await new_device(home_id, 'alarm_control_panel') self._known_homes.add(home_id) for device in self._client.devices: if device.device_id not in self._known_devices: for component in ('sensor', 'binary_sensor'): await new_device(device.device_id, component) self._known_devices.add(device.device_id) async_dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY)
async def _async_handle_conversation_event(self, event): from hangups import ChatMessageEvent if isinstance(event, ChatMessageEvent): dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_MESSAGE_RECEIVED, event.conversation_id, event.user_id, event)
async def async_handle_humi_update(hass, context, msg): """Handle a humidity sensor state update.""" _LOGGER.debug("[humi handler] context: %s msg: %s", context, msg) entity_id, humi = context.get(DEVICE_CLASS_HUMIDITY), msg.get('humi') if entity_id: async_dispatcher_send( hass, SIGNAL_SENSOR_UPDATE.format(entity_id), humi)
def on_data(data): """Define a handler to fire when the data is received.""" mac_address = data['macAddress'] if data != self.stations[mac_address][ATTR_LAST_DATA]: _LOGGER.debug('New data received: %s', data) self.stations[mac_address][ATTR_LAST_DATA] = data async_dispatcher_send(self._hass, TOPIC_UPDATE)
async def set_outside_temp(call): """Provide the outside temperature to the OpenTherm Gateway.""" gw_var = gw_vars.DATA_OUTSIDE_TEMP value = await gateway.set_outside_temp(call.data[ATTR_TEMPERATURE]) status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] status.update({gw_var: value}) async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
def async_update_data(now): """Update data from IP camera in SCAN_INTERVAL.""" yield from cam.update() async_dispatcher_send(hass, SIGNAL_UPDATE_DATA, host) async_track_point_in_utc_time( hass, async_update_data, utcnow() + interval)
async def handle_webhook(hass, webhook_id, request): """Handle incoming webhook with GPSLogger request.""" try: data = WEBHOOK_SCHEMA(dict(await request.post())) except vol.MultipleInvalid as error: return web.Response( text=error.error_message, status=HTTP_UNPROCESSABLE_ENTITY ) attrs = { ATTR_SPEED: data.get(ATTR_SPEED), ATTR_DIRECTION: data.get(ATTR_DIRECTION), ATTR_ALTITUDE: data.get(ATTR_ALTITUDE), ATTR_PROVIDER: data.get(ATTR_PROVIDER), ATTR_ACTIVITY: data.get(ATTR_ACTIVITY) } device = data[ATTR_DEVICE] async_dispatcher_send( hass, TRACKER_UPDATE, device, (data[ATTR_LATITUDE], data[ATTR_LONGITUDE]), data[ATTR_BATTERY], data[ATTR_ACCURACY], attrs) return web.Response( text='Setting location for {}'.format(device), status=HTTP_OK )
def async_update_heat_data(now): """Update heat data from eight in HEAT_SCAN_INTERVAL.""" yield from eight.update_device_data() async_dispatcher_send(hass, SIGNAL_UPDATE_HEAT) async_track_point_in_utc_time( hass, async_update_heat_data, utcnow() + HEAT_SCAN_INTERVAL)
def async_update_user_data(now): """Update user data from eight in USER_SCAN_INTERVAL.""" yield from eight.update_user_data() async_dispatcher_send(hass, SIGNAL_UPDATE_USER) async_track_point_in_utc_time( hass, async_update_user_data, utcnow() + USER_SCAN_INTERVAL)
def axis_device_discovered(service, discovery_info): """Called when axis devices has been found.""" host = discovery_info[CONF_HOST] name = discovery_info['hostname'] serialnumber = discovery_info['properties']['macaddress'] if serialnumber not in AXIS_DEVICES: config_file = _read_config(hass) if serialnumber in config_file: # Device config saved to file try: device_config = DEVICE_SCHEMA(config_file[serialnumber]) device_config[CONF_HOST] = host except vol.Invalid as err: _LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err) return False if not setup_device(hass, config, device_config): _LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME]) else: # New device, create configuration request for UI request_configuration(hass, config, name, host, serialnumber) else: # Device already registered, but on a different IP device = AXIS_DEVICES[serialnumber] device.url = host async_dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host)
async def handle_remove_tracking(call): """Call when a user removes an Aftership tracking from HASS.""" slug = call.data[CONF_SLUG] tracking_number = call.data[CONF_TRACKING_NUMBER] await aftership.remove_package_tracking(slug, tracking_number) async_dispatcher_send(hass, UPDATE_TOPIC)
def test_run_camera_setup(hass, test_client): """Test that it fetches the given dispatcher data.""" yield from async_setup_component(hass, 'camera', { 'camera': { 'platform': 'dispatcher', 'name': 'dispatcher', 'signal': 'test_camera', }}) client = yield from test_client(hass.http.app) async_dispatcher_send(hass, 'test_camera', b'test') yield from hass.async_block_till_done() resp = yield from client.get('/api/camera_proxy/camera.dispatcher') assert resp.status == 200 body = yield from resp.text() assert body == 'test' async_dispatcher_send(hass, 'test_camera', b'test2') yield from hass.async_block_till_done() resp = yield from client.get('/api/camera_proxy/camera.dispatcher') assert resp.status == 200 body = yield from resp.text() assert body == 'test2'
async def set_setback_temp(call): """Set the OpenTherm Gateway SetBack temperature.""" gw_var = gw_vars.OTGW_SB_TEMP value = await gateway.set_setback_temp(call.data[ATTR_TEMPERATURE]) status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] status.update({gw_var: value}) async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
def dispatch_level_change(self, command, level): """Dispatch level change.""" async_dispatcher_send( self._zha_device.hass, "{}_{}".format(self.unique_id, command), level )
def attribute_updated(self, attrid, value): """Handle attribute updates on this cluster.""" if attrid == self._value_attribute: async_dispatcher_send( self._zha_device.hass, "{}_{}".format(self.unique_id, SIGNAL_ATTR_UPDATED), value )
async def handle_add_tracking(call): """Call when a user adds a new Aftership tracking from HASS.""" title = call.data.get(CONF_TITLE) slug = call.data.get(CONF_SLUG) tracking_number = call.data[CONF_TRACKING_NUMBER] await aftership.add_package_tracking(tracking_number, title, slug) async_dispatcher_send(hass, UPDATE_TOPIC)
async def async_update_sabnzbd(now): """Refresh SABnzbd queue data.""" from pysabnzbd import SabnzbdApiException try: await sab_api.refresh_data() async_dispatcher_send(hass, SIGNAL_SABNZBD_UPDATED, None) except SabnzbdApiException as err: _LOGGER.error(err)
async def _on_disconnect(self): """Handle disconnecting.""" if self._connected: _LOGGER.debug('Connection lost! Reconnect...') await self.async_connect() else: dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_DISCONNECTED)
async def async_new_address_callback(hass, entry): """Handle signals of device getting new address. This is a static method because a class method (bound method), can not be used with weak references. """ device = hass.data[DOMAIN][entry.data[CONF_MAC]] device.api.config.host = device.host async_dispatcher_send(hass, device.event_new_address)
async def test_add_new_group(hass): """Test successful creation of group entities.""" await setup_gateway(hass, {}) group = Mock() group.name = 'name' group.register_async_callback = Mock() async_dispatcher_send(hass, 'deconz_new_group', [group]) await hass.async_block_till_done() assert "light.name" in hass.data[deconz.DOMAIN].deconz_ids
async def test_do_not_add_deconz_groups(hass): """Test that clip sensors can be ignored.""" await setup_gateway(hass, {}, allow_deconz_groups=False) group = Mock() group.name = 'name' group.register_async_callback = Mock() async_dispatcher_send(hass, 'deconz_new_group', [group]) await hass.async_block_till_done() assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
async def async_update(self): """Retrieve latest state.""" result = await self.get_attribute_value('fan_mode', from_cache=True) async_dispatcher_send( self._zha_device.hass, "{}_{}".format(self.unique_id, SIGNAL_ATTR_UPDATED), result )
async def set_gpio_mode(call): """Set the OpenTherm Gateway GPIO modes.""" gpio_id = call.data[ATTR_ID] gpio_mode = call.data[ATTR_MODE] mode = await gateway.set_gpio_mode(gpio_id, gpio_mode) gpio_var = getattr(gw_vars, 'OTGW_GPIO_{}'.format(gpio_id)) status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] status.update({gpio_var: mode}) async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
async def set_led_mode(call): """Set the OpenTherm Gateway LED modes.""" led_id = call.data[ATTR_ID] led_mode = call.data[ATTR_MODE] mode = await gateway.set_led_mode(led_id, led_mode) led_var = getattr(gw_vars, 'OTGW_LED_{}'.format(led_id)) status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] status.update({led_var: mode}) async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
async def async_device_message_received(topic, payload, qos): """Process the received message.""" match = TOPIC_MATCHER.match(topic) if not match: return _prefix_topic, component, node_id, object_id = match.groups() if component not in SUPPORTED_COMPONENTS: _LOGGER.warning("Component %s is not supported", component) return # If present, the node_id will be included in the discovered object id discovery_id = '_'.join((node_id, object_id)) if node_id else object_id if ALREADY_DISCOVERED not in hass.data: hass.data[ALREADY_DISCOVERED] = {} discovery_hash = (component, discovery_id) if discovery_hash in hass.data[ALREADY_DISCOVERED]: _LOGGER.info( "Component has already been discovered: %s %s, sending update", component, discovery_id) async_dispatcher_send( hass, MQTT_DISCOVERY_UPDATED.format(discovery_hash), payload) elif payload: # Add component try: payload = json.loads(payload) except ValueError: _LOGGER.warning("Unable to parse JSON %s: '%s'", object_id, payload) return payload = dict(payload) platform = payload.get(CONF_PLATFORM, 'mqtt') if platform not in ALLOWED_PLATFORMS.get(component, []): _LOGGER.warning("Platform %s (component %s) is not allowed", platform, component) return payload[CONF_PLATFORM] = platform if CONF_STATE_TOPIC not in payload: payload[CONF_STATE_TOPIC] = '{}/{}/{}{}/state'.format( discovery_topic, component, '%s/' % node_id if node_id else '', object_id) hass.data[ALREADY_DISCOVERED][discovery_hash] = None payload[ATTR_DISCOVERY_HASH] = discovery_hash _LOGGER.info("Found new component: %s %s", component, discovery_id) await async_load_platform( hass, component, platform, payload, hass_config)
async def test_bandwidth_sensors(hass, aioclient_mock, mock_unifi_websocket): """Verify that bandwidth sensors are working as expected.""" wired_client = { "hostname": "Wired client", "is_wired": True, "mac": "00:00:00:00:00:01", "oui": "Producer", "wired-rx_bytes": 1234000000, "wired-tx_bytes": 5678000000, } wireless_client = { "is_wired": False, "mac": "00:00:00:00:00:02", "name": "Wireless client", "oui": "Producer", "rx_bytes": 2345000000, "tx_bytes": 6789000000, } options = { CONF_ALLOW_BANDWIDTH_SENSORS: True, CONF_ALLOW_UPTIME_SENSORS: False, CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False, } config_entry = await setup_unifi_integration( hass, aioclient_mock, options=options, clients_response=[wired_client, wireless_client], ) assert len(hass.states.async_all()) == 5 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4 assert hass.states.get("sensor.wired_client_rx").state == "1234.0" assert hass.states.get("sensor.wired_client_tx").state == "5678.0" assert hass.states.get("sensor.wireless_client_rx").state == "2345.0" assert hass.states.get("sensor.wireless_client_tx").state == "6789.0" ent_reg = er.async_get(hass) assert (ent_reg.async_get("sensor.wired_client_rx").entity_category == ENTITY_CATEGORY_DIAGNOSTIC) # Verify state update wireless_client["rx_bytes"] = 3456000000 wireless_client["tx_bytes"] = 7891000000 mock_unifi_websocket(data={ "meta": { "message": MESSAGE_CLIENT }, "data": [wireless_client], }) await hass.async_block_till_done() assert hass.states.get("sensor.wireless_client_rx").state == "3456.0" assert hass.states.get("sensor.wireless_client_tx").state == "7891.0" # Disable option options[CONF_ALLOW_BANDWIDTH_SENSORS] = False hass.config_entries.async_update_entry(config_entry, options=options.copy()) await hass.async_block_till_done() assert len(hass.states.async_all()) == 1 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0 assert hass.states.get("sensor.wireless_client_rx") is None assert hass.states.get("sensor.wireless_client_tx") is None assert hass.states.get("sensor.wired_client_rx") is None assert hass.states.get("sensor.wired_client_tx") is None # Enable option options[CONF_ALLOW_BANDWIDTH_SENSORS] = True hass.config_entries.async_update_entry(config_entry, options=options.copy()) await hass.async_block_till_done() assert len(hass.states.async_all()) == 5 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4 assert hass.states.get("sensor.wireless_client_rx") assert hass.states.get("sensor.wireless_client_tx") assert hass.states.get("sensor.wired_client_rx") assert hass.states.get("sensor.wired_client_tx") # Try to add the sensors again, using a signal clients_connected = {wired_client["mac"], wireless_client["mac"]} devices_connected = set() controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] async_dispatcher_send( hass, controller.signal_update, clients_connected, devices_connected, ) await hass.async_block_till_done() assert len(hass.states.async_all()) == 5 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
def zones_update_callback(status): """Update zone objects as per notification from the alarm.""" _LOGGER.debug("Zones callback, status: %s", status) async_dispatcher_send(hass, SIGNAL_ZONES_UPDATED, status[ZONES])
def alarm_status_update_callback(): """Send status update received from alarm to Home Assistant.""" _LOGGER.debug("Sending request to update panel state") async_dispatcher_send(hass, SIGNAL_PANEL_MESSAGE)
def controller_reconnected(self, ctrl: Controller) -> None: """On reconnect to controller.""" async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_RECONNECTED, ctrl)
async def async_reset_meters(self): """Reset all sensors of this meter.""" _LOGGER.debug("reset meter %s", self.entity_id) async_dispatcher_send(self.hass, SIGNAL_RESET_METER, self.entity_id)
def async_event_callback(self, action, event_id): """Call to configure events when initialized on event stream.""" if action == "add": async_dispatcher_send(self.hass, self.event_new_sensor, event_id)
def _async_push_update(state: PilotParser) -> None: """Receive a push update.""" _LOGGER.debug("%s: Got push update: %s", bulb.mac, state.pilotResult) coordinator.async_set_updated_data(None) if state.get_source() == PIR_SOURCE: async_dispatcher_send(hass, SIGNAL_WIZ_PIR.format(bulb.mac))
def reconnected(): """Schedule reconnect after connection has been lost.""" _LOGGER.warning('HLK-SW16 %s connected', device) async_dispatcher_send(hass, SIGNAL_AVAILABILITY.format(device), True)
def alarm_data_updated_callback(data): """Handle non-alarm based info updates.""" _LOGGER.debug("Envisalink sent new alarm info. Updating alarms...") async_dispatcher_send(hass, SIGNAL_KEYPAD_UPDATE, data)
async def async_process_queue(self, task_idx = 0): """walk through the list of tasks and execute the ones that are ready""" if self.queue_busy or not self.is_available(): return self.queue_busy = True # verify conditions conditions_passed = ( all(validate_condition(self.hass, item) for item in self._conditions) if self._condition_type == const.CONDITION_TYPE_AND else any(validate_condition(self.hass, item) for item in self._conditions) ) if len(self._conditions) else True if not conditions_passed and len(self._queue): _LOGGER.debug("[{}]: Conditions have failed, skipping execution of actions".format(self.id)) if self._track_conditions: # postpone tasks self.queue_busy = False return else: # abort all items in queue while len(self._queue): self._queue.pop() while task_idx < len(self._queue): action = self._queue[task_idx] if action[CONF_SERVICE] == ACTION_WAIT: @callback async def async_timer_finished(_now): self._timer = None self.queue_busy = False await self.async_process_queue(task_idx + 1) self._timer = async_call_later( self.hass, action[CONF_SERVICE_DATA][CONF_DELAY], async_timer_finished ) _LOGGER.debug("[{}]: Postponing next action for {} seconds".format(self.id, action[CONF_SERVICE_DATA][CONF_DELAY])) return if ATTR_ENTITY_ID in action: _LOGGER.debug("[{}]: Executing service {} on entity {}".format( self.id, action[CONF_SERVICE], action[ATTR_ENTITY_ID] )) else: _LOGGER.debug("[{}]: Executing service {}".format(self.id, action[CONF_SERVICE])) await async_call_from_config( self.hass, action, ) task_idx = task_idx+1 self.queue_busy = False if not self._track_conditions or not len(self._conditions): while len(self._queue): self._queue.pop() async_dispatcher_send(self.hass, "action_queue_finished", self.id) else: _LOGGER.debug("[{}]: Done for now, Waiting for conditions to change".format(self.id))
async def async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -> None: """Handle signals of config entry being updated.""" async_dispatcher_send(hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}")
async def async_device_message_received(msg): """Process the received message.""" payload = msg.payload topic = msg.topic topic_trimmed = topic.replace(f"{discovery_topic}/", "", 1) match = TOPIC_MATCHER.match(topic_trimmed) if not match: 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.keys()): 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.keys()): 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 present, the node_id will be included in the discovered object id discovery_id = " ".join((node_id, object_id)) if node_id else object_id discovery_hash = (component, discovery_id) if payload: # Attach MQTT topic to the payload, used for debug prints setattr(payload, "__configuration_source__", f"MQTT (topic: '{topic}')") discovery_data = { ATTR_DISCOVERY_HASH: discovery_hash, ATTR_DISCOVERY_PAYLOAD: payload, ATTR_DISCOVERY_TOPIC: topic, } setattr(payload, "discovery_data", discovery_data) payload[CONF_PLATFORM] = "mqtt" if ALREADY_DISCOVERED not in hass.data: hass.data[ALREADY_DISCOVERED] = {} if discovery_hash in hass.data[ALREADY_DISCOVERED]: # Dispatch update _LOGGER.info( "Component has already been discovered: %s %s, sending update", component, discovery_id, ) async_dispatcher_send( hass, MQTT_DISCOVERY_UPDATED.format(discovery_hash), payload ) elif payload: # Add component _LOGGER.info("Found new component: %s %s", component, discovery_id) hass.data[ALREADY_DISCOVERED][discovery_hash] = None config_entries_key = f"{component}.mqtt" async with hass.data[DATA_CONFIG_ENTRY_LOCK]: if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: if component == "device_automation": # Local import to avoid circular dependencies # pylint: disable=import-outside-toplevel from . import device_automation await device_automation.async_setup_entry(hass, config_entry) else: await hass.config_entries.async_forward_entry_setup( config_entry, component ) hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) async_dispatcher_send( hass, MQTT_DISCOVERY_NEW.format(component, "mqtt"), payload )
async def async_refresh_entity(service): """Refresh values that specific entity depends on.""" entity_id = service.data.get(ATTR_ENTITY_ID) async_dispatcher_send(hass, SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id))
async def ws_handler(message_obj): """Handle websocket messages. This allows push notifications from Alexa to update last_called and media state. """ command = (message_obj.json_payload["command"] if isinstance(message_obj.json_payload, dict) and "command" in message_obj.json_payload else None) json_payload = (message_obj.json_payload["payload"] if isinstance(message_obj.json_payload, dict) and "payload" in message_obj.json_payload else None) existing_serials = _existing_serials(hass, login_obj) seen_commands = hass.data[DATA_ALEXAMEDIA]["accounts"][email][ "websocket_commands"] if command and json_payload: _LOGGER.debug( "%s: Received websocket command: %s : %s", hide_email(email), command, hide_serial(json_payload), ) serial = None command_time = time.time() if command not in seen_commands: _LOGGER.debug("Adding %s to seen_commands: %s", command, seen_commands) seen_commands[command] = command_time if ("dopplerId" in json_payload and "deviceSerialNumber" in json_payload["dopplerId"]): serial = json_payload["dopplerId"]["deviceSerialNumber"] elif ("key" in json_payload and "entryId" in json_payload["key"] and json_payload["key"]["entryId"].find("#") != -1): serial = (json_payload["key"]["entryId"]).split("#")[2] json_payload["key"]["serialNumber"] = serial else: serial = None if command == "PUSH_ACTIVITY": # Last_Alexa Updated last_called = { "serialNumber": serial, "timestamp": json_payload["timestamp"], } if serial and serial in existing_serials: await update_last_called(login_obj, last_called) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"push_activity": json_payload}, ) elif command in ( "PUSH_AUDIO_PLAYER_STATE", "PUSH_MEDIA_CHANGE", "PUSH_MEDIA_PROGRESS_CHANGE", ): # Player update/ Push_media from tune_in if serial and serial in existing_serials: _LOGGER.debug("Updating media_player: %s", hide_serial(json_payload)) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"player_state": json_payload}, ) elif command == "PUSH_VOLUME_CHANGE": # Player volume update if serial and serial in existing_serials: _LOGGER.debug("Updating media_player volume: %s", hide_serial(json_payload)) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"player_state": json_payload}, ) elif command in ( "PUSH_DOPPLER_CONNECTION_CHANGE", "PUSH_EQUALIZER_STATE_CHANGE", ): # Player availability update if serial and serial in existing_serials: _LOGGER.debug( "Updating media_player availability %s", hide_serial(json_payload), ) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"player_state": json_payload}, ) elif command == "PUSH_BLUETOOTH_STATE_CHANGE": # Player bluetooth update bt_event = json_payload["bluetoothEvent"] bt_success = json_payload["bluetoothEventSuccess"] if (serial and serial in existing_serials and bt_success and bt_event and bt_event in ["DEVICE_CONNECTED", "DEVICE_DISCONNECTED"]): _LOGGER.debug("Updating media_player bluetooth %s", hide_serial(json_payload)) bluetooth_state = await update_bluetooth_state( login_obj, serial) # _LOGGER.debug("bluetooth_state %s", # hide_serial(bluetooth_state)) if bluetooth_state: async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"bluetooth_change": bluetooth_state}, ) elif command == "PUSH_MEDIA_QUEUE_CHANGE": # Player availability update if serial and serial in existing_serials: _LOGGER.debug("Updating media_player queue %s", hide_serial(json_payload)) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"queue_state": json_payload}, ) elif command == "PUSH_NOTIFICATION_CHANGE": # Player update await process_notifications(login_obj) if serial and serial in existing_serials: _LOGGER.debug( "Updating mediaplayer notifications: %s", hide_serial(json_payload), ) async_dispatcher_send( hass, f"{DOMAIN}_{hide_email(email)}"[0:32], {"notification_update": json_payload}, ) elif command in [ "PUSH_DELETE_DOPPLER_ACTIVITIES", # delete Alexa history "PUSH_LIST_ITEM_CHANGE", # update shopping list "PUSH_CONTENT_FOCUS_CHANGE", # likely prime related refocus "PUSH_DEVICE_SETUP_STATE_CHANGE", # likely device changes mid setup ]: pass else: _LOGGER.warning( "Unhandled command: %s with data %s. Please report at %s", command, hide_serial(json_payload), ISSUE_URL, ) if serial in existing_serials: history = hass.data[DATA_ALEXAMEDIA]["accounts"][email][ "websocket_activity"]["serials"].get(serial) if history is None or ( history and command_time - history[len(history) - 1][1] > 2): history = [(command, command_time)] else: history.append([command, command_time]) hass.data[DATA_ALEXAMEDIA]["accounts"][email][ "websocket_activity"]["serials"][serial] = history events = [] for old_command, old_command_time in history: if (old_command in { "PUSH_VOLUME_CHANGE", "PUSH_EQUALIZER_STATE_CHANGE" } and command_time - old_command_time < 0.25): events.append((old_command, round(command_time - old_command_time, 2))) elif old_command in {"PUSH_AUDIO_PLAYER_STATE"}: # There is a potential false positive generated during this event events = [] if len(events) >= 4: _LOGGER.debug( "%s: Detected potential DND websocket change with %s events %s", hide_serial(serial), len(events), events, ) await update_dnd_state(login_obj) if (serial and serial not in existing_serials and serial not in (hass.data[DATA_ALEXAMEDIA]["accounts"] [email]["excluded"].keys())): _LOGGER.debug("Discovered new media_player %s", serial) (hass.data[DATA_ALEXAMEDIA]["accounts"][email]["new_devices"] ) = True coordinator = hass.data[DATA_ALEXAMEDIA]["accounts"][ email].get("coordinator") if coordinator: await coordinator.async_request_refresh()
async def test_uptime_sensors( hass, aioclient_mock, mock_unifi_websocket, initial_uptime, event_uptime, new_uptime, ): """Verify that uptime sensors are working as expected.""" uptime_client = { "mac": "00:00:00:00:00:01", "name": "client1", "oui": "Producer", "uptime": initial_uptime, } options = { CONF_ALLOW_BANDWIDTH_SENSORS: False, CONF_ALLOW_UPTIME_SENSORS: True, CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False, } now = datetime(2021, 1, 1, 1, 1, 0, tzinfo=dt_util.UTC) with patch("homeassistant.util.dt.now", return_value=now): config_entry = await setup_unifi_integration( hass, aioclient_mock, options=options, clients_response=[uptime_client], ) assert len(hass.states.async_all()) == 2 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 assert hass.states.get( "sensor.client1_uptime").state == "2021-01-01T01:00:00+00:00" ent_reg = er.async_get(hass) assert (ent_reg.async_get("sensor.client1_uptime").entity_category == ENTITY_CATEGORY_DIAGNOSTIC) # Verify normal new event doesn't change uptime # 4 seconds has passed uptime_client["uptime"] = event_uptime now = datetime(2021, 1, 1, 1, 1, 4, tzinfo=dt_util.UTC) with patch("homeassistant.util.dt.now", return_value=now): mock_unifi_websocket(data={ "meta": { "message": MESSAGE_CLIENT }, "data": [uptime_client], }) await hass.async_block_till_done() assert hass.states.get( "sensor.client1_uptime").state == "2021-01-01T01:00:00+00:00" # Verify new event change uptime # 1 month has passed uptime_client["uptime"] = new_uptime now = datetime(2021, 2, 1, 1, 1, 0, tzinfo=dt_util.UTC) with patch("homeassistant.util.dt.now", return_value=now): mock_unifi_websocket(data={ "meta": { "message": MESSAGE_CLIENT }, "data": [uptime_client], }) await hass.async_block_till_done() assert hass.states.get( "sensor.client1_uptime").state == "2021-02-01T01:00:00+00:00" # Disable option options[CONF_ALLOW_UPTIME_SENSORS] = False hass.config_entries.async_update_entry(config_entry, options=options.copy()) await hass.async_block_till_done() assert len(hass.states.async_all()) == 1 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0 assert hass.states.get("sensor.client1_uptime") is None # Enable option options[CONF_ALLOW_UPTIME_SENSORS] = True with patch("homeassistant.util.dt.now", return_value=now): hass.config_entries.async_update_entry(config_entry, options=options.copy()) await hass.async_block_till_done() assert len(hass.states.async_all()) == 2 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 assert hass.states.get("sensor.client1_uptime") # Try to add the sensors again, using a signal clients_connected = {uptime_client["mac"]} devices_connected = set() controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] async_dispatcher_send( hass, controller.signal_update, clients_connected, devices_connected, ) await hass.async_block_till_done() assert len(hass.states.async_all()) == 2 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
async def wrapper(self, *args, **kwargs): """Call decorated function and send update signal to all entities.""" await func(self, *args, **kwargs) async_dispatcher_send(self.hass, DOMAIN)
async def test_switches(hass, aioclient_mock): """Test the update_items function with some clients.""" config_entry = await setup_unifi_integration( hass, aioclient_mock, options={ CONF_BLOCK_CLIENT: [BLOCKED["mac"], UNBLOCKED["mac"]], CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False, }, clients_response=[CLIENT_1, CLIENT_4], devices_response=[DEVICE_1], clients_all_response=[BLOCKED, UNBLOCKED, CLIENT_1], dpigroup_response=DPI_GROUPS, dpiapp_response=DPI_APPS, ) controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4 switch_1 = hass.states.get("switch.poe_client_1") assert switch_1 is not None assert switch_1.state == "on" assert switch_1.attributes["power"] == "2.56" assert switch_1.attributes[SWITCH_DOMAIN] == "00:00:00:00:01:01" assert switch_1.attributes["port"] == 1 assert switch_1.attributes["poe_mode"] == "auto" switch_4 = hass.states.get("switch.poe_client_4") assert switch_4 is None blocked = hass.states.get("switch.block_client_1") assert blocked is not None assert blocked.state == "off" unblocked = hass.states.get("switch.block_client_2") assert unblocked is not None assert unblocked.state == "on" dpi_switch = hass.states.get("switch.block_media_streaming") assert dpi_switch is not None assert dpi_switch.state == "on" assert dpi_switch.attributes["icon"] == "mdi:network" ent_reg = er.async_get(hass) for entry_id in ( "switch.poe_client_1", "switch.block_client_1", "switch.block_media_streaming", ): assert ent_reg.async_get( entry_id).entity_category is EntityCategory.CONFIG # Block and unblock client aioclient_mock.post( f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", ) await hass.services.async_call(SWITCH_DOMAIN, "turn_off", {"entity_id": "switch.block_client_1"}, blocking=True) assert aioclient_mock.call_count == 11 assert aioclient_mock.mock_calls[10][2] == { "mac": "00:00:00:00:01:01", "cmd": "block-sta", } await hass.services.async_call(SWITCH_DOMAIN, "turn_on", {"entity_id": "switch.block_client_1"}, blocking=True) assert aioclient_mock.call_count == 12 assert aioclient_mock.mock_calls[11][2] == { "mac": "00:00:00:00:01:01", "cmd": "unblock-sta", } # Enable and disable DPI aioclient_mock.put( f"https://{controller.host}:1234/api/s/{controller.site}/rest/dpiapp/5f976f62e3c58f018ec7e17d", ) await hass.services.async_call( SWITCH_DOMAIN, "turn_off", {"entity_id": "switch.block_media_streaming"}, blocking=True, ) assert aioclient_mock.call_count == 13 assert aioclient_mock.mock_calls[12][2] == {"enabled": False} await hass.services.async_call( SWITCH_DOMAIN, "turn_on", {"entity_id": "switch.block_media_streaming"}, blocking=True, ) assert aioclient_mock.call_count == 14 assert aioclient_mock.mock_calls[13][2] == {"enabled": True} # Make sure no duplicates arise on generic signal update async_dispatcher_send(hass, controller.signal_update) await hass.async_block_till_done() assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4
def send_removed_signal(): async_dispatcher_send(self.hass, SIGNAL_GROUP_ENTITY_REMOVED, self._group_id)
def _dispatch(signal, entity_ids, *args): for entity_id in entity_ids: async_dispatcher_send(hass, '{}_{}'.format(signal, entity_id), *args)
def controller_disconnected(self, ctrl: Controller, ex: Exception) -> None: """On disconnect from controller.""" async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_DISCONNECTED, ctrl, ex)
def partition_updated_callback(data): """Handle partition changes thrown by evl (including alarms).""" _LOGGER.debug("The envisalink sent a partition update event") async_dispatcher_send(hass, SIGNAL_PARTITION_UPDATE, data)
async def async_signal_options_update(hass: HomeAssistantType, config_entry: ConfigEntry) -> None: """Handle config entry options update.""" async_dispatcher_send(hass, UPDATE_OPTIONS_SIGNAL, config_entry)
def zones_updated_callback(data): """Handle zone timer updates.""" _LOGGER.debug("Envisalink sent a zone update event. Updating zones...") async_dispatcher_send(hass, SIGNAL_ZONE_UPDATE, data)
def dispatcher_message(self, identifier: str, data: Any = None) -> None: """Match cloud notification to dispatcher.""" if identifier.startswith("remote_"): async_dispatcher_send(self._hass, DISPATCHER_REMOTE_UPDATE, data)
logger=_LOGGER, name="Fjaraskupan Updater", update_interval=timedelta(seconds=120), update_method=async_update_data, ) coordinator.async_set_updated_data(device.state) device_info = DeviceInfo( identifiers={(DOMAIN, ble_device.address)}, manufacturer="Fjäråskupan", name="Fjäråskupan", ) device_state = DeviceState(device, coordinator, device_info) state.devices[ble_device.address] = device_state async_dispatcher_send(hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state) scanner.register_detection_callback(detection_callback) await scanner.start() hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True @callback def async_setup_entry_platform( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, constructor: Callable[[DeviceState], list[Entity]],
def add_device() -> None: async_dispatcher_send( hass, f"{DOMAIN}_{entry.entry_id}_add_{SENSOR_DOMAIN}", device)
def controller_update(self, ctrl: Controller) -> None: """System update message is received from the controller.""" async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_UPDATE, ctrl)
def outputs_update_callback(status): """Update zone objects as per notification from the alarm.""" _LOGGER.debug("Outputs updated callback , status: %s", status) async_dispatcher_send(hass, SIGNAL_OUTPUTS_UPDATED, status["outputs"])
def controller_discovered(self, ctrl: Controller) -> None: """Handle new controller discoverery.""" async_dispatcher_send(self.hass, DISPATCH_CONTROLLER_DISCOVERED, ctrl)
def _remove_events(self, events): """Remove old geo location events.""" _LOGGER.debug("Going to remove %s", events) for event in events: async_dispatcher_send( self._hass, SIGNAL_DELETE_ENTITY.format(event._strike_id))