diff --git a/homeassistant/components/climate/mysensors.py b/homeassistant/components/climate/mysensors.py index 2545094ceec..9fab56c61ac 100644 --- a/homeassistant/components/climate/mysensors.py +++ b/homeassistant/components/climate/mysensors.py @@ -115,7 +115,7 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice): """List of available fan modes.""" return ['Auto', 'Min', 'Normal', 'Max'] - def set_temperature(self, **kwargs): + async def async_set_temperature(self, **kwargs): """Set new target temperature.""" set_req = self.gateway.const.SetReq temp = kwargs.get(ATTR_TEMPERATURE) @@ -143,9 +143,9 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice): if self.gateway.optimistic: # Optimistically assume that device has changed state self._values[value_type] = value - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def set_fan_mode(self, fan_mode): + async def async_set_fan_mode(self, fan_mode): """Set new target temperature.""" set_req = self.gateway.const.SetReq self.gateway.set_child_value( @@ -153,9 +153,9 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice): if self.gateway.optimistic: # Optimistically assume that device has changed state self._values[set_req.V_HVAC_SPEED] = fan_mode - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def set_operation_mode(self, operation_mode): + async def async_set_operation_mode(self, operation_mode): """Set new target temperature.""" self.gateway.set_child_value( self.node_id, self.child_id, self.value_type, @@ -163,7 +163,7 @@ class MySensorsHVAC(mysensors.MySensorsEntity, ClimateDevice): if self.gateway.optimistic: # Optimistically assume that device has changed state self._values[self.value_type] = operation_mode - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() async def async_update(self): """Update the controller with the latest value from a sensor.""" diff --git a/homeassistant/components/cover/mysensors.py b/homeassistant/components/cover/mysensors.py index 669a7ce6723..3f8eb054710 100644 --- a/homeassistant/components/cover/mysensors.py +++ b/homeassistant/components/cover/mysensors.py @@ -42,7 +42,7 @@ class MySensorsCover(mysensors.MySensorsEntity, CoverDevice): set_req = self.gateway.const.SetReq return self._values.get(set_req.V_DIMMER) - def open_cover(self, **kwargs): + async def async_open_cover(self, **kwargs): """Move the cover up.""" set_req = self.gateway.const.SetReq self.gateway.set_child_value( @@ -53,9 +53,9 @@ class MySensorsCover(mysensors.MySensorsEntity, CoverDevice): self._values[set_req.V_DIMMER] = 100 else: self._values[set_req.V_LIGHT] = STATE_ON - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def close_cover(self, **kwargs): + async def async_close_cover(self, **kwargs): """Move the cover down.""" set_req = self.gateway.const.SetReq self.gateway.set_child_value( @@ -66,9 +66,9 @@ class MySensorsCover(mysensors.MySensorsEntity, CoverDevice): self._values[set_req.V_DIMMER] = 0 else: self._values[set_req.V_LIGHT] = STATE_OFF - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def set_cover_position(self, **kwargs): + async def async_set_cover_position(self, **kwargs): """Move the cover to a specific position.""" position = kwargs.get(ATTR_POSITION) set_req = self.gateway.const.SetReq @@ -77,9 +77,9 @@ class MySensorsCover(mysensors.MySensorsEntity, CoverDevice): if self.gateway.optimistic: # Optimistically assume that cover has changed state. self._values[set_req.V_DIMMER] = position - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def stop_cover(self, **kwargs): + async def async_stop_cover(self, **kwargs): """Stop the device.""" set_req = self.gateway.const.SetReq self.gateway.set_child_value( diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index 6e41e0f5693..55387288d7f 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -130,7 +130,7 @@ class MySensorsLight(mysensors.MySensorsEntity, Light): self._white = white self._values[self.value_type] = hex_color - def turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs): """Turn the device off.""" value_type = self.gateway.const.SetReq.V_LIGHT self.gateway.set_child_value( @@ -139,7 +139,7 @@ class MySensorsLight(mysensors.MySensorsEntity, Light): # optimistically assume that light has changed state self._state = False self._values[value_type] = STATE_OFF - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() def _async_update_light(self): """Update the controller with values from light child.""" @@ -171,12 +171,12 @@ class MySensorsLightDimmer(MySensorsLight): """Flag supported features.""" return SUPPORT_BRIGHTNESS - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the device on.""" self._turn_on_light() self._turn_on_dimmer(**kwargs) if self.gateway.optimistic: - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() async def async_update(self): """Update the controller with the latest value from a sensor.""" @@ -196,13 +196,13 @@ class MySensorsLightRGB(MySensorsLight): return SUPPORT_BRIGHTNESS | SUPPORT_COLOR return SUPPORT_COLOR - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the device on.""" self._turn_on_light() self._turn_on_dimmer(**kwargs) self._turn_on_rgb_and_w('%02x%02x%02x', **kwargs) if self.gateway.optimistic: - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() async def async_update(self): """Update the controller with the latest value from a sensor.""" @@ -225,10 +225,10 @@ class MySensorsLightRGBW(MySensorsLightRGB): return SUPPORT_BRIGHTNESS | SUPPORT_MYSENSORS_RGBW return SUPPORT_MYSENSORS_RGBW - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the device on.""" self._turn_on_light() self._turn_on_dimmer(**kwargs) self._turn_on_rgb_and_w('%02x%02x%02x%02x', **kwargs) if self.gateway.optimistic: - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 9b394457973..f5ad59095dc 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -4,6 +4,7 @@ Connect to a MySensors gateway via pymysensors API. For more details about this component, please refer to the documentation at https://home-assistant.io/components/mysensors/ """ +import asyncio from collections import defaultdict import logging import os @@ -16,17 +17,17 @@ import voluptuous as vol from homeassistant.components.mqtt import ( valid_publish_topic, valid_subscribe_topic) from homeassistant.const import ( - ATTR_BATTERY_LEVEL, CONF_NAME, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, + ATTR_BATTERY_LEVEL, CONF_NAME, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON) from homeassistant.core import callback from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, dispatcher_send) + async_dispatcher_connect, async_dispatcher_send) from homeassistant.helpers.entity import Entity -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component -REQUIREMENTS = ['pymysensors==0.11.1'] +REQUIREMENTS = ['pymysensors==0.14.0'] _LOGGER = logging.getLogger(__name__) @@ -280,67 +281,62 @@ MYSENSORS_CONST_SCHEMA = { } -def setup(hass, config): +async def async_setup(hass, config): """Set up the MySensors component.""" import mysensors.mysensors as mysensors version = config[DOMAIN].get(CONF_VERSION) persistence = config[DOMAIN].get(CONF_PERSISTENCE) - def setup_gateway(device, persistence_file, baud_rate, tcp_port, in_prefix, - out_prefix): + async def setup_gateway( + device, persistence_file, baud_rate, tcp_port, in_prefix, + out_prefix): """Return gateway after setup of the gateway.""" if device == MQTT_COMPONENT: - if not setup_component(hass, MQTT_COMPONENT, config): - return + if not await async_setup_component(hass, MQTT_COMPONENT, config): + return None mqtt = hass.components.mqtt retain = config[DOMAIN].get(CONF_RETAIN) def pub_callback(topic, payload, qos, retain): """Call MQTT publish function.""" - mqtt.publish(topic, payload, qos, retain) + mqtt.async_publish(topic, payload, qos, retain) def sub_callback(topic, sub_cb, qos): """Call MQTT subscribe function.""" - mqtt.subscribe(topic, sub_cb, qos) - gateway = mysensors.MQTTGateway( - pub_callback, sub_callback, + @callback + def internal_callback(*args): + """Call callback.""" + sub_cb(*args) + + hass.async_add_job( + mqtt.async_subscribe(topic, internal_callback, qos)) + + gateway = mysensors.AsyncMQTTGateway( + pub_callback, sub_callback, in_prefix=in_prefix, + out_prefix=out_prefix, retain=retain, loop=hass.loop, event_callback=None, persistence=persistence, persistence_file=persistence_file, - protocol_version=version, in_prefix=in_prefix, - out_prefix=out_prefix, retain=retain) + protocol_version=version) else: try: - is_serial_port(device) - gateway = mysensors.SerialGateway( - device, event_callback=None, persistence=persistence, + await hass.async_add_job(is_serial_port, device) + gateway = mysensors.AsyncSerialGateway( + device, baud=baud_rate, loop=hass.loop, + event_callback=None, persistence=persistence, persistence_file=persistence_file, - protocol_version=version, baud=baud_rate) + protocol_version=version) except vol.Invalid: - try: - socket.getaddrinfo(device, None) - # valid ip address - gateway = mysensors.TCPGateway( - device, event_callback=None, persistence=persistence, - persistence_file=persistence_file, - protocol_version=version, port=tcp_port) - except OSError: - # invalid ip address - return + gateway = mysensors.AsyncTCPGateway( + device, port=tcp_port, loop=hass.loop, event_callback=None, + persistence=persistence, persistence_file=persistence_file, + protocol_version=version) gateway.metric = hass.config.units.is_metric gateway.optimistic = config[DOMAIN].get(CONF_OPTIMISTIC) gateway.device = device gateway.event_callback = gw_callback_factory(hass) - - def gw_start(event): - """Trigger to start of the gateway and any persistence.""" - if persistence: - discover_persistent_devices(hass, gateway) - gateway.start() - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, - lambda event: gateway.stop()) - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, gw_start) + if persistence: + await gateway.start_persistence() return gateway @@ -357,7 +353,7 @@ def setup(hass, config): tcp_port = gway.get(CONF_TCP_PORT) in_prefix = gway.get(CONF_TOPIC_IN_PREFIX, '') out_prefix = gway.get(CONF_TOPIC_OUT_PREFIX, '') - ready_gateway = setup_gateway( + ready_gateway = await setup_gateway( device, persistence_file, baud_rate, tcp_port, in_prefix, out_prefix) if ready_gateway is not None: @@ -371,9 +367,36 @@ def setup(hass, config): hass.data[MYSENSORS_GATEWAYS] = gateways + hass.async_add_job(finish_setup(hass, gateways)) + return True +async def finish_setup(hass, gateways): + """Load any persistent devices and platforms and start gateway.""" + discover_tasks = [] + start_tasks = [] + for gateway in gateways.values(): + discover_tasks.append(discover_persistent_devices(hass, gateway)) + start_tasks.append(gw_start(hass, gateway)) + if discover_tasks: + # Make sure all devices and platforms are loaded before gateway start. + await asyncio.wait(discover_tasks, loop=hass.loop) + if start_tasks: + await asyncio.wait(start_tasks, loop=hass.loop) + + +async def gw_start(hass, gateway): + """Start the gateway.""" + @callback + def gw_stop(event): + """Trigger to stop the gateway.""" + hass.async_add_job(gateway.stop()) + + await gateway.start() + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gw_stop) + + def validate_child(gateway, node_id, child): """Validate that a child has the correct values according to schema. @@ -431,14 +454,18 @@ def validate_child(gateway, node_id, child): return validated +@callback def discover_mysensors_platform(hass, platform, new_devices): """Discover a MySensors platform.""" - discovery.load_platform( - hass, platform, DOMAIN, {ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN}) + task = hass.async_add_job(discovery.async_load_platform( + hass, platform, DOMAIN, + {ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN})) + return task -def discover_persistent_devices(hass, gateway): +async def discover_persistent_devices(hass, gateway): """Discover platforms for devices loaded via persistence file.""" + tasks = [] new_devices = defaultdict(list) for node_id in gateway.sensors: node = gateway.sensors[node_id] @@ -447,7 +474,9 @@ def discover_persistent_devices(hass, gateway): for platform, dev_ids in validated.items(): new_devices[platform].extend(dev_ids) for platform, dev_ids in new_devices.items(): - discover_mysensors_platform(hass, platform, dev_ids) + tasks.append(discover_mysensors_platform(hass, platform, dev_ids)) + if tasks: + await asyncio.wait(tasks, loop=hass.loop) def get_mysensors_devices(hass, domain): @@ -459,6 +488,7 @@ def get_mysensors_devices(hass, domain): def gw_callback_factory(hass): """Return a new callback for the gateway.""" + @callback def mysensors_callback(msg): """Handle messages from a MySensors gateway.""" start = timer() @@ -489,7 +519,7 @@ def gw_callback_factory(hass): # Only one signal per device is needed. # A device can have multiple platforms, ie multiple schemas. # FOR LATER: Add timer to not signal if another update comes in. - dispatcher_send(hass, signal) + async_dispatcher_send(hass, signal) end = timer() if end - start > 0.1: _LOGGER.debug( diff --git a/homeassistant/components/notify/mysensors.py b/homeassistant/components/notify/mysensors.py index 257b5995446..1374779c5f0 100644 --- a/homeassistant/components/notify/mysensors.py +++ b/homeassistant/components/notify/mysensors.py @@ -42,7 +42,7 @@ class MySensorsNotificationService(BaseNotificationService): """Initialize the service.""" self.devices = mysensors.get_mysensors_devices(hass, DOMAIN) - def send_message(self, message="", **kwargs): + async def async_send_message(self, message="", **kwargs): """Send a message to a user.""" target_devices = kwargs.get(ATTR_TARGET) devices = [device for device in self.devices.values() diff --git a/homeassistant/components/switch/mysensors.py b/homeassistant/components/switch/mysensors.py index c0f45cad861..a91ca6d11e7 100644 --- a/homeassistant/components/switch/mysensors.py +++ b/homeassistant/components/switch/mysensors.py @@ -42,7 +42,7 @@ async def async_setup_platform( hass, DOMAIN, discovery_info, device_class_map, async_add_devices=async_add_devices) - def send_ir_code_service(service): + async def async_send_ir_code_service(service): """Set IR code as device state attribute.""" entity_ids = service.data.get(ATTR_ENTITY_ID) ir_code = service.data.get(ATTR_IR_CODE) @@ -58,10 +58,10 @@ async def async_setup_platform( kwargs = {ATTR_IR_CODE: ir_code} for device in _devices: - device.turn_on(**kwargs) + await device.async_turn_on(**kwargs) hass.services.async_register( - DOMAIN, SERVICE_SEND_IR_CODE, send_ir_code_service, + DOMAIN, SERVICE_SEND_IR_CODE, async_send_ir_code_service, schema=SEND_IR_CODE_SERVICE_SCHEMA) @@ -84,23 +84,23 @@ class MySensorsSwitch(mysensors.MySensorsEntity, SwitchDevice): """Return True if switch is on.""" return self._values.get(self.value_type) == STATE_ON - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the switch on.""" self.gateway.set_child_value( self.node_id, self.child_id, self.value_type, 1) if self.gateway.optimistic: # optimistically assume that switch has changed state self._values[self.value_type] = STATE_ON - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs): """Turn the switch off.""" self.gateway.set_child_value( self.node_id, self.child_id, self.value_type, 0) if self.gateway.optimistic: # optimistically assume that switch has changed state self._values[self.value_type] = STATE_OFF - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() class MySensorsIRSwitch(MySensorsSwitch): @@ -117,7 +117,7 @@ class MySensorsIRSwitch(MySensorsSwitch): set_req = self.gateway.const.SetReq return self._values.get(set_req.V_LIGHT) == STATE_ON - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the IR switch on.""" set_req = self.gateway.const.SetReq if ATTR_IR_CODE in kwargs: @@ -130,11 +130,11 @@ class MySensorsIRSwitch(MySensorsSwitch): # optimistically assume that switch has changed state self._values[self.value_type] = self._ir_code self._values[set_req.V_LIGHT] = STATE_ON - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() # turn off switch after switch was turned on - self.turn_off() + await self.async_turn_off() - def turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs): """Turn the IR switch off.""" set_req = self.gateway.const.SetReq self.gateway.set_child_value( @@ -142,7 +142,7 @@ class MySensorsIRSwitch(MySensorsSwitch): if self.gateway.optimistic: # optimistically assume that switch has changed state self._values[set_req.V_LIGHT] = STATE_OFF - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() async def async_update(self): """Update the controller with the latest value from a sensor.""" diff --git a/requirements_all.txt b/requirements_all.txt index cbc5cce0590..bb822934a1a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -869,7 +869,7 @@ pymusiccast==0.1.6 pymyq==0.0.8 # homeassistant.components.mysensors -pymysensors==0.11.1 +pymysensors==0.14.0 # homeassistant.components.lock.nello pynello==1.5.1