From 5635cdb77ca41d23b24b30021d25a9f17fd822e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Apr 2020 12:47:31 -0500 Subject: [PATCH 1/8] Fix setting up multiple UPSes with NUT in 0.108.x (#34427) --- homeassistant/components/nut/config_flow.py | 4 +++- tests/components/nut/test_config_flow.py | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nut/config_flow.py b/homeassistant/components/nut/config_flow.py index 04889bb3f3f..0721ea10f94 100644 --- a/homeassistant/components/nut/config_flow.py +++ b/homeassistant/components/nut/config_flow.py @@ -99,7 +99,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def _host_port_alias_already_configured(self, host, port, alias): """See if we already have a nut entry matching user input configured.""" existing_host_port_aliases = { - _format_host_port_alias(host, port, alias) + _format_host_port_alias( + entry.data[CONF_HOST], entry.data[CONF_PORT], entry.data.get(CONF_ALIAS) + ) for entry in self._async_current_entries() } return _format_host_port_alias(host, port, alias) in existing_host_port_aliases diff --git a/tests/components/nut/test_config_flow.py b/tests/components/nut/test_config_flow.py index 362f6c0b2ba..2ffc470ccf0 100644 --- a/tests/components/nut/test_config_flow.py +++ b/tests/components/nut/test_config_flow.py @@ -4,6 +4,8 @@ from asynctest import MagicMock, patch from homeassistant import config_entries, setup from homeassistant.components.nut.const import DOMAIN +from tests.common import MockConfigEntry + def _get_mock_pynutclient(list_vars=None): pynutclient = MagicMock() @@ -62,6 +64,12 @@ async def test_form_import(hass): """Test we get the form with import source.""" await setup.async_setup_component(hass, "persistent_notification", {}) + config_entry = MockConfigEntry( + domain=DOMAIN, + data={"host": "2.2.2.2", "port": 123, "resources": ["battery.charge"]}, + ) + config_entry.add_to_hass(hass) + mock_pynut = _get_mock_pynutclient(list_vars={"battery.voltage": "serial"}) with patch( @@ -92,7 +100,7 @@ async def test_form_import(hass): } await hass.async_block_till_done() assert len(mock_setup.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 2 async def test_form_cannot_connect(hass): From 41e4dc4336ae75095e5f6f86d3c48e923dd8abdd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 1 Apr 2020 15:25:04 -0700 Subject: [PATCH 2/8] Cloud do checks during setup (#33507) * Update cloud to do more tasks during async_setup * Upgrade hass_nabucasa to 0.33 --- homeassistant/components/cloud/__init__.py | 21 +++--------- homeassistant/components/cloud/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_binary_sensor.py | 34 ++++++++++---------- tests/components/cloud/test_init.py | 8 +---- 7 files changed, 27 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 0a0b9f0fe88..a40529b6fe4 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -10,7 +10,6 @@ from homeassistant.const import ( CONF_MODE, CONF_NAME, CONF_REGION, - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ) from homeassistant.core import callback @@ -191,11 +190,7 @@ async def async_setup(hass, config): client = CloudClient(hass, prefs, websession, alexa_conf, google_conf) cloud = hass.data[DOMAIN] = Cloud(client, **kwargs) - async def _startup(event): - """Startup event.""" - await cloud.start() - - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _startup) + await cloud.start() async def _shutdown(event): """Shutdown event.""" @@ -230,17 +225,11 @@ async def async_setup(hass, config): return loaded = True - hass.async_create_task( - hass.helpers.discovery.async_load_platform( - "binary_sensor", DOMAIN, {}, config - ) - ) - hass.async_create_task( - hass.helpers.discovery.async_load_platform("stt", DOMAIN, {}, config) - ) - hass.async_create_task( - hass.helpers.discovery.async_load_platform("tts", DOMAIN, {}, config) + await hass.helpers.discovery.async_load_platform( + "binary_sensor", DOMAIN, {}, config ) + await hass.helpers.discovery.async_load_platform("stt", DOMAIN, {}, config) + await hass.helpers.discovery.async_load_platform("tts", DOMAIN, {}, config) cloud.iot.register_on_connect(_on_connect) diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index cfbb221c164..b8c2bc277f0 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -2,7 +2,7 @@ "domain": "cloud", "name": "Home Assistant Cloud", "documentation": "https://www.home-assistant.io/integrations/cloud", - "requirements": ["hass-nabucasa==0.32.2"], + "requirements": ["hass-nabucasa==0.33.0"], "dependencies": ["http", "webhook", "alexa"], "after_dependencies": ["google_assistant"], "codeowners": ["@home-assistant/cloud"] diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 92564cd6781..434881ea79a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ ciso8601==2.1.3 cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 -hass-nabucasa==0.32.2 +hass-nabucasa==0.33.0 home-assistant-frontend==20200407.2 importlib-metadata==1.5.0 jinja2>=2.11.1 diff --git a/requirements_all.txt b/requirements_all.txt index ca6bd795b82..cd773652112 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -674,7 +674,7 @@ habitipy==0.2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.32.2 +hass-nabucasa==0.33.0 # homeassistant.components.mqtt hbmqtt==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c71b77b0008..ff64c959be8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -264,7 +264,7 @@ ha-ffmpeg==2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.32.2 +hass-nabucasa==0.33.0 # homeassistant.components.mqtt hbmqtt==0.9.5 diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py index 24b0563890b..c4ad22abb2f 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -1,24 +1,23 @@ """Tests for the cloud binary sensor.""" from unittest.mock import Mock +from asynctest import patch + from homeassistant.components.cloud.const import DISPATCHER_REMOTE_UPDATE from homeassistant.setup import async_setup_component async def test_remote_connection_sensor(hass): """Test the remote connection sensor.""" - from homeassistant.components.cloud import binary_sensor as bin_sensor - - bin_sensor.WAIT_UNTIL_CHANGE = 0 - assert await async_setup_component(hass, "cloud", {"cloud": {}}) await hass.async_block_till_done() assert hass.states.get("binary_sensor.remote_ui") is None # Fake connection/discovery - org_cloud = hass.data["cloud"] - await org_cloud.iot._on_connect[-1]() + await hass.helpers.discovery.async_load_platform( + "binary_sensor", "cloud", {}, {"cloud": {}} + ) # Mock test env cloud = hass.data["cloud"] = Mock() @@ -29,17 +28,18 @@ async def test_remote_connection_sensor(hass): assert state is not None assert state.state == "unavailable" - cloud.remote.is_connected = False - cloud.remote.certificate = object() - hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) - await hass.async_block_till_done() + with patch("homeassistant.components.cloud.binary_sensor.WAIT_UNTIL_CHANGE", 0): + cloud.remote.is_connected = False + cloud.remote.certificate = object() + hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + await hass.async_block_till_done() - state = hass.states.get("binary_sensor.remote_ui") - assert state.state == "off" + state = hass.states.get("binary_sensor.remote_ui") + assert state.state == "off" - cloud.remote.is_connected = True - hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) - await hass.async_block_till_done() + cloud.remote.is_connected = True + hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + await hass.async_block_till_done() - state = hass.states.get("binary_sensor.remote_ui") - assert state.state == "on" + state = hass.states.get("binary_sensor.remote_ui") + assert state.state == "on" diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 9dd8695b9b2..10a7bc38c05 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -6,7 +6,7 @@ import pytest from homeassistant.components import cloud from homeassistant.components.cloud.const import DOMAIN from homeassistant.components.cloud.prefs import STORAGE_KEY -from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP +from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import Context from homeassistant.exceptions import Unauthorized from homeassistant.setup import async_setup_component @@ -103,12 +103,6 @@ async def test_remote_services(hass, mock_cloud_fixture, hass_read_only_user): async def test_startup_shutdown_events(hass, mock_cloud_fixture): """Test if the cloud will start on startup event.""" - with patch("hass_nabucasa.Cloud.start", return_value=mock_coro()) as mock_start: - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() - - assert mock_start.called - with patch("hass_nabucasa.Cloud.stop", return_value=mock_coro()) as mock_stop: hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() From eedfca6623f8bf46215cb866ae76a92126fed9f7 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 12 Apr 2020 19:55:03 +0200 Subject: [PATCH 3/8] Fix modbus sync/async issues (#34043) * add pyserial to manifest pymodbus is very developer oriented and assumes every developer adapt the requierements.txt to his/hers needs. Our requirements.txt is different it contains all posibilities allowing user to later change configuration without having to install extra packages. As a consequence manifest.json needs to include the pyserial. * modbus: make truly async client creation Make hass call listen_once async. Integrate content of start_modbus into async_setup. Do not use the boiler plate create tcp client function from pymodbus as it is sync, and also does not work well with asyncio, instead call the init_ directly, since that is async. * both component/modbus and component/serial uses pyserial-async but with slighty different version requirements. Combined the 2. * Review 1 * Review 2 * Review @staticmethod is no good, because the function uses class variables. * Review Pytest is sometimes a bit cryptic, lets hope this does it. --- homeassistant/components/modbus/__init__.py | 99 +++++++++++-------- homeassistant/components/modbus/manifest.json | 3 +- requirements_all.txt | 1 + requirements_test_all.txt | 4 + 4 files changed, 62 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index 1e889043fae..ad0330b56a0 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -3,13 +3,21 @@ import asyncio import logging from async_timeout import timeout -from pymodbus.client.asynchronous import schedulers -from pymodbus.client.asynchronous.serial import AsyncModbusSerialClient as ClientSerial -from pymodbus.client.asynchronous.tcp import AsyncModbusTCPClient as ClientTCP -from pymodbus.client.asynchronous.udp import AsyncModbusUDPClient as ClientUDP +from pymodbus.client.asynchronous.asyncio import ( + AsyncioModbusSerialClient, + ModbusClientProtocol, + init_tcp_client, + init_udp_client, +) from pymodbus.exceptions import ModbusException +from pymodbus.factory import ClientDecoder from pymodbus.pdu import ExceptionResponse -from pymodbus.transaction import ModbusRtuFramer +from pymodbus.transaction import ( + ModbusAsciiFramer, + ModbusBinaryFramer, + ModbusRtuFramer, + ModbusSocketFramer, +) import voluptuous as vol from homeassistant.const import ( @@ -105,13 +113,6 @@ async def async_setup(hass, config): for client in hub_collect.values(): del client - def start_modbus(): - """Start Modbus service.""" - for client in hub_collect.values(): - client.setup() - - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus) - async def write_register(service): """Write Modbus registers.""" unit = int(float(service.data[ATTR_UNIT])) @@ -136,7 +137,11 @@ async def async_setup(hass, config): await hub_collect[client_name].write_coil(unit, address, state) # do not wait for EVENT_HOMEASSISTANT_START, activate pymodbus now - await hass.async_add_executor_job(start_modbus) + for client in hub_collect.values(): + await client.setup(hass) + + # register function to gracefully stop modbus + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus) # Register services for modbus hass.services.async_register( @@ -189,47 +194,55 @@ class ModbusHub: await asyncio.sleep(self._config_delay) self._config_delay = 0 - def setup(self): - """Set up pymodbus client.""" - # pylint: disable = E0633 - # Client* do deliver loop, client as result but - # pylint does not accept that fact + @staticmethod + def _framer(method): + if method == "ascii": + framer = ModbusAsciiFramer(ClientDecoder()) + elif method == "rtu": + framer = ModbusRtuFramer(ClientDecoder()) + elif method == "binary": + framer = ModbusBinaryFramer(ClientDecoder()) + elif method == "socket": + framer = ModbusSocketFramer(ClientDecoder()) + else: + framer = None + return framer + async def setup(self, hass): + """Set up pymodbus client.""" if self._config_type == "serial": - _, self._client = ClientSerial( - schedulers.ASYNC_IO, - method=self._config_method, - port=self._config_port, + # reconnect ?? + framer = self._framer(self._config_method) + + # just a class creation no IO or other slow items + self._client = AsyncioModbusSerialClient( + self._config_port, + protocol_class=ModbusClientProtocol, + framer=framer, + loop=self._loop, baudrate=self._config_baudrate, - stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, - loop=self._loop, + stopbits=self._config_stopbits, ) + await self._client.connect() elif self._config_type == "rtuovertcp": - _, self._client = ClientTCP( - schedulers.ASYNC_IO, - host=self._config_host, - port=self._config_port, - framer=ModbusRtuFramer, - timeout=self._config_timeout, - loop=self._loop, + # framer ModbusRtuFramer ?? + # timeout ?? + self._client = await init_tcp_client( + None, self._loop, self._config_host, self._config_port ) elif self._config_type == "tcp": - _, self._client = ClientTCP( - schedulers.ASYNC_IO, - host=self._config_host, - port=self._config_port, - timeout=self._config_timeout, - loop=self._loop, + # framer ?? + # timeout ?? + self._client = await init_tcp_client( + None, self._loop, self._config_host, self._config_port ) elif self._config_type == "udp": - _, self._client = ClientUDP( - schedulers.ASYNC_IO, - host=self._config_host, - port=self._config_port, - timeout=self._config_timeout, - loop=self._loop, + # framer ?? + # timeout ?? + self._client = await init_udp_client( + None, self._loop, self._config_host, self._config_port ) else: assert False diff --git a/homeassistant/components/modbus/manifest.json b/homeassistant/components/modbus/manifest.json index d1d2a9db550..ec3381577ad 100644 --- a/homeassistant/components/modbus/manifest.json +++ b/homeassistant/components/modbus/manifest.json @@ -2,7 +2,6 @@ "domain": "modbus", "name": "Modbus", "documentation": "https://www.home-assistant.io/integrations/modbus", - "requirements": ["pymodbus==2.3.0"], - "dependencies": [], + "requirements": ["pymodbus==2.3.0", "pyserial-asyncio==0.4"], "codeowners": ["@adamchengtkc", "@janiversen"] } diff --git a/requirements_all.txt b/requirements_all.txt index cd773652112..495e5b78aed 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1523,6 +1523,7 @@ pysdcp==1 # homeassistant.components.sensibo pysensibo==1.0.3 +# homeassistant.components.modbus # homeassistant.components.serial pyserial-asyncio==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ff64c959be8..4434852bccc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -586,6 +586,10 @@ pyps4-2ndscreen==1.0.7 # homeassistant.components.qwikswitch pyqwikswitch==0.93 +# homeassistant.components.modbus +# homeassistant.components.serial +pyserial-asyncio==0.4 + # homeassistant.components.signal_messenger pysignalclirestapi==0.2.4 From 945567150d5768919960620123966bffed1e2128 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Fri, 17 Apr 2020 09:55:57 +0200 Subject: [PATCH 4/8] Rollback modbus to version 0.107.7 keep new functionality (#34287) * Rollback modbus to version 0.107.7 Update manifest to not use async. Rollback entities to sync version. Keep newer modifications apart from async. Rollback __init__ to sync version but keep the new functionality. add async sub directory Adding the current (not working) version in a sub directory, to allow easy sharing with a few alfa testers. The async version are to be updated to use the serial/tcp already available instead of the flaky pymodbus version. pymodbus is still needed to encode/decode the messagess. Update test cases to reflect sync implementation, but keep the new functionality like e.g. conftest.py. * do not publish async version The async version will be made available in a forked repo, until it is ready to replace the production code. --- homeassistant/components/modbus/__init__.py | 217 +++++++----------- .../components/modbus/binary_sensor.py | 20 +- homeassistant/components/modbus/climate.py | 47 ++-- homeassistant/components/modbus/manifest.json | 2 +- homeassistant/components/modbus/sensor.py | 28 +-- homeassistant/components/modbus/switch.py | 76 +++--- requirements_all.txt | 1 - requirements_test_all.txt | 4 - tests/components/modbus/conftest.py | 12 +- 9 files changed, 179 insertions(+), 228 deletions(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index ad0330b56a0..34a396758cb 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -1,23 +1,9 @@ """Support for Modbus.""" -import asyncio import logging +import threading -from async_timeout import timeout -from pymodbus.client.asynchronous.asyncio import ( - AsyncioModbusSerialClient, - ModbusClientProtocol, - init_tcp_client, - init_udp_client, -) -from pymodbus.exceptions import ModbusException -from pymodbus.factory import ClientDecoder -from pymodbus.pdu import ExceptionResponse -from pymodbus.transaction import ( - ModbusAsciiFramer, - ModbusBinaryFramer, - ModbusRtuFramer, - ModbusSocketFramer, -) +from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient, ModbusUdpClient +from pymodbus.transaction import ModbusRtuFramer import voluptuous as vol from homeassistant.const import ( @@ -50,6 +36,7 @@ from .const import ( _LOGGER = logging.getLogger(__name__) + BASE_SCHEMA = vol.Schema({vol.Optional(CONF_NAME, default=DEFAULT_HUB): cv.string}) SERIAL_SCHEMA = BASE_SCHEMA.extend( @@ -101,57 +88,55 @@ SERVICE_WRITE_COIL_SCHEMA = vol.Schema( ) -async def async_setup(hass, config): +def setup(hass, config): """Set up Modbus component.""" hass.data[DOMAIN] = hub_collect = {} for client_config in config[DOMAIN]: - hub_collect[client_config[CONF_NAME]] = ModbusHub(client_config, hass.loop) + hub_collect[client_config[CONF_NAME]] = ModbusHub(client_config) def stop_modbus(event): """Stop Modbus service.""" for client in hub_collect.values(): - del client + client.close() - async def write_register(service): + def write_register(service): """Write Modbus registers.""" unit = int(float(service.data[ATTR_UNIT])) address = int(float(service.data[ATTR_ADDRESS])) value = service.data[ATTR_VALUE] client_name = service.data[ATTR_HUB] if isinstance(value, list): - await hub_collect[client_name].write_registers( + hub_collect[client_name].write_registers( unit, address, [int(float(i)) for i in value] ) else: - await hub_collect[client_name].write_register( - unit, address, int(float(value)) - ) + hub_collect[client_name].write_register(unit, address, int(float(value))) - async def write_coil(service): + def write_coil(service): """Write Modbus coil.""" unit = service.data[ATTR_UNIT] address = service.data[ATTR_ADDRESS] state = service.data[ATTR_STATE] client_name = service.data[ATTR_HUB] - await hub_collect[client_name].write_coil(unit, address, state) + hub_collect[client_name].write_coil(unit, address, state) # do not wait for EVENT_HOMEASSISTANT_START, activate pymodbus now for client in hub_collect.values(): - await client.setup(hass) + client.setup() # register function to gracefully stop modbus hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus) # Register services for modbus - hass.services.async_register( + hass.services.register( DOMAIN, SERVICE_WRITE_REGISTER, write_register, schema=SERVICE_WRITE_REGISTER_SCHEMA, ) - hass.services.async_register( - DOMAIN, SERVICE_WRITE_COIL, write_coil, schema=SERVICE_WRITE_COIL_SCHEMA, + hass.services.register( + DOMAIN, SERVICE_WRITE_COIL, write_coil, schema=SERVICE_WRITE_COIL_SCHEMA ) return True @@ -159,13 +144,12 @@ async def async_setup(hass, config): class ModbusHub: """Thread safe wrapper class for pymodbus.""" - def __init__(self, client_config, main_loop): + def __init__(self, client_config): """Initialize the Modbus hub.""" # generic configuration - self._loop = main_loop self._client = None - self._lock = asyncio.Lock() + self._lock = threading.Lock() self._config_name = client_config[CONF_NAME] self._config_type = client_config[CONF_TYPE] self._config_port = client_config[CONF_PORT] @@ -183,144 +167,101 @@ class ModbusHub: # network configuration self._config_host = client_config[CONF_HOST] self._config_delay = client_config[CONF_DELAY] + if self._config_delay > 0: + _LOGGER.warning( + "Parameter delay is accepted but not used in this version" + ) @property def name(self): """Return the name of this hub.""" return self._config_name - async def _connect_delay(self): - if self._config_delay > 0: - await asyncio.sleep(self._config_delay) - self._config_delay = 0 - - @staticmethod - def _framer(method): - if method == "ascii": - framer = ModbusAsciiFramer(ClientDecoder()) - elif method == "rtu": - framer = ModbusRtuFramer(ClientDecoder()) - elif method == "binary": - framer = ModbusBinaryFramer(ClientDecoder()) - elif method == "socket": - framer = ModbusSocketFramer(ClientDecoder()) - else: - framer = None - return framer - - async def setup(self, hass): + def setup(self): """Set up pymodbus client.""" if self._config_type == "serial": - # reconnect ?? - framer = self._framer(self._config_method) - - # just a class creation no IO or other slow items - self._client = AsyncioModbusSerialClient( - self._config_port, - protocol_class=ModbusClientProtocol, - framer=framer, - loop=self._loop, + self._client = ModbusSerialClient( + method=self._config_method, + port=self._config_port, baudrate=self._config_baudrate, + stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, - stopbits=self._config_stopbits, + timeout=self._config_timeout, ) - await self._client.connect() elif self._config_type == "rtuovertcp": - # framer ModbusRtuFramer ?? - # timeout ?? - self._client = await init_tcp_client( - None, self._loop, self._config_host, self._config_port + self._client = ModbusTcpClient( + host=self._config_host, + port=self._config_port, + framer=ModbusRtuFramer, + timeout=self._config_timeout, ) elif self._config_type == "tcp": - # framer ?? - # timeout ?? - self._client = await init_tcp_client( - None, self._loop, self._config_host, self._config_port + self._client = ModbusTcpClient( + host=self._config_host, + port=self._config_port, + timeout=self._config_timeout, ) elif self._config_type == "udp": - # framer ?? - # timeout ?? - self._client = await init_udp_client( - None, self._loop, self._config_host, self._config_port + self._client = ModbusUdpClient( + host=self._config_host, + port=self._config_port, + timeout=self._config_timeout, ) else: assert False - async def _read(self, unit, address, count, func): - """Read generic with error handling.""" - await self._connect_delay() - async with self._lock: - kwargs = {"unit": unit} if unit else {} - try: - async with timeout(self._config_timeout): - result = await func(address, count, **kwargs) - except asyncio.TimeoutError: - result = None + # Connect device + self.connect() - if isinstance(result, (ModbusException, ExceptionResponse)): - _LOGGER.error("Hub %s Exception (%s)", self._config_name, result) - return result + def close(self): + """Disconnect client.""" + with self._lock: + self._client.close() - async def _write(self, unit, address, value, func): - """Read generic with error handling.""" - await self._connect_delay() - async with self._lock: - kwargs = {"unit": unit} if unit else {} - try: - async with timeout(self._config_timeout): - func(address, value, **kwargs) - except asyncio.TimeoutError: - return + def connect(self): + """Connect client.""" + with self._lock: + self._client.connect() - async def read_coils(self, unit, address, count): + def read_coils(self, unit, address, count): """Read coils.""" - if self._client.protocol is None: - return None - return await self._read(unit, address, count, self._client.protocol.read_coils) + with self._lock: + kwargs = {"unit": unit} if unit else {} + return self._client.read_coils(address, count, **kwargs) - async def read_discrete_inputs(self, unit, address, count): + def read_discrete_inputs(self, unit, address, count): """Read discrete inputs.""" - if self._client.protocol is None: - return None - return await self._read( - unit, address, count, self._client.protocol.read_discrete_inputs - ) + with self._lock: + kwargs = {"unit": unit} if unit else {} + return self._client.read_discrete_inputs(address, count, **kwargs) - async def read_input_registers(self, unit, address, count): + def read_input_registers(self, unit, address, count): """Read input registers.""" - if self._client.protocol is None: - return None - return await self._read( - unit, address, count, self._client.protocol.read_input_registers - ) + with self._lock: + kwargs = {"unit": unit} if unit else {} + return self._client.read_input_registers(address, count, **kwargs) - async def read_holding_registers(self, unit, address, count): + def read_holding_registers(self, unit, address, count): """Read holding registers.""" - if self._client.protocol is None: - return None - return await self._read( - unit, address, count, self._client.protocol.read_holding_registers - ) + with self._lock: + kwargs = {"unit": unit} if unit else {} + return self._client.read_holding_registers(address, count, **kwargs) - async def write_coil(self, unit, address, value): + def write_coil(self, unit, address, value): """Write coil.""" - if self._client.protocol is None: - return None - return await self._write(unit, address, value, self._client.protocol.write_coil) + with self._lock: + kwargs = {"unit": unit} if unit else {} + self._client.write_coil(address, value, **kwargs) - async def write_register(self, unit, address, value): + def write_register(self, unit, address, value): """Write register.""" - if self._client.protocol is None: - return None - return await self._write( - unit, address, value, self._client.protocol.write_register - ) + with self._lock: + kwargs = {"unit": unit} if unit else {} + self._client.write_register(address, value, **kwargs) - async def write_registers(self, unit, address, values): + def write_registers(self, unit, address, values): """Write registers.""" - if self._client.protocol is None: - return None - return await self._write( - unit, address, values, self._client.protocol.write_registers - ) + with self._lock: + kwargs = {"unit": unit} if unit else {} + self._client.write_registers(address, values, **kwargs) diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index 9989b9d530a..5f80813d108 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -2,7 +2,7 @@ import logging from typing import Optional -from pymodbus.exceptions import ModbusException +from pymodbus.exceptions import ConnectionException, ModbusException from pymodbus.pdu import ExceptionResponse import voluptuous as vol @@ -54,7 +54,7 @@ PLATFORM_SCHEMA = vol.All( ) -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus binary sensors.""" sensors = [] for entry in config[CONF_INPUTS]: @@ -70,7 +70,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= ) ) - async_add_entities(sensors) + add_entities(sensors) class ModbusBinarySensor(BinarySensorDevice): @@ -107,15 +107,17 @@ class ModbusBinarySensor(BinarySensorDevice): """Return True if entity is available.""" return self._available - async def async_update(self): + def update(self): """Update the state of the sensor.""" - if self._input_type == CALL_TYPE_COIL: - result = await self._hub.read_coils(self._slave, self._address, 1) - else: - result = await self._hub.read_discrete_inputs(self._slave, self._address, 1) - if result is None: + try: + if self._input_type == CALL_TYPE_COIL: + result = self._hub.read_coils(self._slave, self._address, 1) + else: + result = self._hub.read_discrete_inputs(self._slave, self._address, 1) + except ConnectionException: self._available = False return + if isinstance(result, (ModbusException, ExceptionResponse)): self._available = False return diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index e5fbcf4d421..5cfd9c36967 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -3,7 +3,7 @@ import logging import struct from typing import Optional -from pymodbus.exceptions import ModbusException +from pymodbus.exceptions import ConnectionException, ModbusException from pymodbus.pdu import ExceptionResponse import voluptuous as vol @@ -72,7 +72,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus Thermostat Platform.""" name = config[CONF_NAME] modbus_slave = config[CONF_SLAVE] @@ -91,7 +91,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= hub_name = config[CONF_HUB] hub = hass.data[MODBUS_DOMAIN][hub_name] - async_add_entities( + add_entities( [ ModbusThermostat( hub, @@ -170,12 +170,12 @@ class ModbusThermostat(ClimateDevice): """Return the list of supported features.""" return SUPPORT_TARGET_TEMPERATURE - async def async_update(self): + def update(self): """Update Target & Current Temperature.""" - self._target_temperature = await self._read_register( + self._target_temperature = self._read_register( CALL_TYPE_REGISTER_HOLDING, self._target_temperature_register ) - self._current_temperature = await self._read_register( + self._current_temperature = self._read_register( self._current_temperature_register_type, self._current_temperature_register ) @@ -224,7 +224,7 @@ class ModbusThermostat(ClimateDevice): """Return the supported step of target temperature.""" return self._temp_step - async def set_temperature(self, **kwargs): + def set_temperature(self, **kwargs): """Set new target temperature.""" target_temperature = int( (kwargs.get(ATTR_TEMPERATURE) - self._offset) / self._scale @@ -233,26 +233,28 @@ class ModbusThermostat(ClimateDevice): return byte_string = struct.pack(self._structure, target_temperature) register_value = struct.unpack(">h", byte_string[0:2])[0] - await self._write_register(self._target_temperature_register, register_value) + self._write_register(self._target_temperature_register, register_value) @property def available(self) -> bool: """Return True if entity is available.""" return self._available - async def _read_register(self, register_type, register) -> Optional[float]: + def _read_register(self, register_type, register) -> Optional[float]: """Read register using the Modbus hub slave.""" - if register_type == CALL_TYPE_REGISTER_INPUT: - result = await self._hub.read_input_registers( - self._slave, register, self._count - ) - else: - result = await self._hub.read_holding_registers( - self._slave, register, self._count - ) - if result is None: + try: + if register_type == CALL_TYPE_REGISTER_INPUT: + result = self._hub.read_input_registers( + self._slave, register, self._count + ) + else: + result = self._hub.read_holding_registers( + self._slave, register, self._count + ) + except ConnectionException: self._available = False return + if isinstance(result, (ModbusException, ExceptionResponse)): self._available = False return @@ -269,7 +271,12 @@ class ModbusThermostat(ClimateDevice): return register_value - async def _write_register(self, register, value): + def _write_register(self, register, value): """Write holding register using the Modbus hub slave.""" - await self._hub.write_registers(self._slave, register, [value, 0]) + try: + self._hub.write_registers(self._slave, register, [value, 0]) + except ConnectionException: + self._available = False + return + self._available = True diff --git a/homeassistant/components/modbus/manifest.json b/homeassistant/components/modbus/manifest.json index ec3381577ad..a9155c7b628 100644 --- a/homeassistant/components/modbus/manifest.json +++ b/homeassistant/components/modbus/manifest.json @@ -2,6 +2,6 @@ "domain": "modbus", "name": "Modbus", "documentation": "https://www.home-assistant.io/integrations/modbus", - "requirements": ["pymodbus==2.3.0", "pyserial-asyncio==0.4"], + "requirements": ["pymodbus==2.3.0"], "codeowners": ["@adamchengtkc", "@janiversen"] } diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index 988d495eba5..8c475a114eb 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -3,7 +3,7 @@ import logging import struct from typing import Any, Optional, Union -from pymodbus.exceptions import ModbusException +from pymodbus.exceptions import ConnectionException, ModbusException from pymodbus.pdu import ExceptionResponse import voluptuous as vol @@ -89,7 +89,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Modbus sensors.""" sensors = [] data_types = {DATA_TYPE_INT: {1: "h", 2: "i", 4: "q"}} @@ -148,7 +148,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= if not sensors: return False - async_add_entities(sensors) + add_entities(sensors) class ModbusRegisterSensor(RestoreEntity): @@ -219,19 +219,21 @@ class ModbusRegisterSensor(RestoreEntity): """Return True if entity is available.""" return self._available - async def async_update(self): + def update(self): """Update the state of the sensor.""" - if self._register_type == CALL_TYPE_REGISTER_INPUT: - result = await self._hub.read_input_registers( - self._slave, self._register, self._count - ) - else: - result = await self._hub.read_holding_registers( - self._slave, self._register, self._count - ) - if result is None: + try: + if self._register_type == CALL_TYPE_REGISTER_INPUT: + result = self._hub.read_input_registers( + self._slave, self._register, self._count + ) + else: + result = self._hub.read_holding_registers( + self._slave, self._register, self._count + ) + except ConnectionException: self._available = False return + if isinstance(result, (ModbusException, ExceptionResponse)): self._available = False return diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index e4ec6a004fb..97a5d00a30f 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -2,7 +2,7 @@ import logging from typing import Optional -from pymodbus.exceptions import ModbusException +from pymodbus.exceptions import ConnectionException, ModbusException from pymodbus.pdu import ExceptionResponse import voluptuous as vol @@ -76,7 +76,7 @@ PLATFORM_SCHEMA = vol.All( ) -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Read configuration and create Modbus devices.""" switches = [] if CONF_COILS in config: @@ -109,7 +109,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= ) ) - async_add_entities(switches) + add_entities(switches) class ModbusCoilSwitch(ToggleEntity, RestoreEntity): @@ -146,24 +146,26 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity): """Return True if entity is available.""" return self._available - async def turn_on(self, **kwargs): + def turn_on(self, **kwargs): """Set switch on.""" - await self._write_coil(self._coil, True) + self._write_coil(self._coil, True) - async def turn_off(self, **kwargs): + def turn_off(self, **kwargs): """Set switch off.""" - await self._write_coil(self._coil, False) + self._write_coil(self._coil, False) - async def async_update(self): + def update(self): """Update the state of the switch.""" - self._is_on = await self._read_coil(self._coil) + self._is_on = self._read_coil(self._coil) - async def _read_coil(self, coil) -> Optional[bool]: + def _read_coil(self, coil) -> Optional[bool]: """Read coil using the Modbus hub slave.""" - result = await self._hub.read_coils(self._slave, coil, 1) - if result is None: + try: + result = self._hub.read_coils(self._slave, coil, 1) + except ConnectionException: self._available = False return + if isinstance(result, (ModbusException, ExceptionResponse)): self._available = False return @@ -173,9 +175,14 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity): return value - async def _write_coil(self, coil, value): + def _write_coil(self, coil, value): """Write coil using the Modbus hub slave.""" - await self._hub.write_coil(self._slave, coil, value) + try: + self._hub.write_coil(self._slave, coil, value) + except ConnectionException: + self._available = False + return + self._available = True @@ -221,21 +228,21 @@ class ModbusRegisterSwitch(ModbusCoilSwitch): self._is_on = None - async def turn_on(self, **kwargs): + def turn_on(self, **kwargs): """Set switch on.""" # Only holding register is writable if self._register_type == CALL_TYPE_REGISTER_HOLDING: - await self._write_register(self._command_on) + self._write_register(self._command_on) if not self._verify_state: self._is_on = True - async def turn_off(self, **kwargs): + def turn_off(self, **kwargs): """Set switch off.""" # Only holding register is writable if self._register_type == CALL_TYPE_REGISTER_HOLDING: - await self._write_register(self._command_off) + self._write_register(self._command_off) if not self._verify_state: self._is_on = False @@ -244,12 +251,12 @@ class ModbusRegisterSwitch(ModbusCoilSwitch): """Return True if entity is available.""" return self._available - async def async_update(self): + def update(self): """Update the state of the switch.""" if not self._verify_state: return - value = await self._read_register() + value = self._read_register() if value == self._state_on: self._is_on = True elif value == self._state_off: @@ -263,18 +270,18 @@ class ModbusRegisterSwitch(ModbusCoilSwitch): value, ) - async def _read_register(self) -> Optional[int]: - if self._register_type == CALL_TYPE_REGISTER_INPUT: - result = await self._hub.read_input_registers( - self._slave, self._register, 1 - ) - else: - result = await self._hub.read_holding_registers( - self._slave, self._register, 1 - ) - if result is None: + def _read_register(self) -> Optional[int]: + try: + if self._register_type == CALL_TYPE_REGISTER_INPUT: + result = self._hub.read_input_registers(self._slave, self._register, 1) + else: + result = self._hub.read_holding_registers( + self._slave, self._register, 1 + ) + except ConnectionException: self._available = False return + if isinstance(result, (ModbusException, ExceptionResponse)): self._available = False return @@ -284,7 +291,12 @@ class ModbusRegisterSwitch(ModbusCoilSwitch): return value - async def _write_register(self, value): + def _write_register(self, value): """Write holding register using the Modbus hub slave.""" - await self._hub.write_register(self._slave, self._register, value) + try: + self._hub.write_register(self._slave, self._register, value) + except ConnectionException: + self._available = False + return + self._available = True diff --git a/requirements_all.txt b/requirements_all.txt index 495e5b78aed..cd773652112 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1523,7 +1523,6 @@ pysdcp==1 # homeassistant.components.sensibo pysensibo==1.0.3 -# homeassistant.components.modbus # homeassistant.components.serial pyserial-asyncio==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4434852bccc..ff64c959be8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -586,10 +586,6 @@ pyps4-2ndscreen==1.0.7 # homeassistant.components.qwikswitch pyqwikswitch==0.93 -# homeassistant.components.modbus -# homeassistant.components.serial -pyserial-asyncio==0.4 - # homeassistant.components.signal_messenger pysignalclirestapi==0.2.4 diff --git a/tests/components/modbus/conftest.py b/tests/components/modbus/conftest.py index d9cd62313b4..814e59e5571 100644 --- a/tests/components/modbus/conftest.py +++ b/tests/components/modbus/conftest.py @@ -40,19 +40,11 @@ class ReadResult: self.registers = register_words -read_result = None - - async def run_test( hass, use_mock_hub, register_config, entity_domain, register_words, expected ): """Run test for given config and check that sensor outputs expected result.""" - async def simulate_read_registers(unit, address, count): - """Simulate modbus register read.""" - del unit, address, count # not used in simulation, but in real connection - return read_result - # Full sensor configuration sensor_name = "modbus_test_sensor" scan_interval = 5 @@ -69,9 +61,9 @@ async def run_test( # Setup inputs for the sensor read_result = ReadResult(register_words) if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT: - use_mock_hub.read_input_registers = simulate_read_registers + use_mock_hub.read_input_registers.return_value = read_result else: - use_mock_hub.read_holding_registers = simulate_read_registers + use_mock_hub.read_holding_registers.return_value = read_result # Initialize sensor now = dt_util.utcnow() From 59686274d79bd149dcbe030f80fa95a05bfaae95 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Apr 2020 10:55:04 -0500 Subject: [PATCH 5/8] Ensure nexia state file is in a writable location (#34325) * bump nexia to 0.9.2 --- homeassistant/components/nexia/__init__.py | 3 +++ homeassistant/components/nexia/config_flow.py | 3 +++ homeassistant/components/nexia/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nexia/__init__.py b/homeassistant/components/nexia/__init__.py index 5c317794c2a..a9c04933674 100644 --- a/homeassistant/components/nexia/__init__.py +++ b/homeassistant/components/nexia/__init__.py @@ -60,6 +60,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): username = conf[CONF_USERNAME] password = conf[CONF_PASSWORD] + state_file = hass.config.path(f"nexia_config_{username}.conf") + try: nexia_home = await hass.async_add_executor_job( partial( @@ -67,6 +69,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): username=username, password=password, device_name=hass.config.location_name, + state_file=state_file, ) ) except ConnectTimeout as ex: diff --git a/homeassistant/components/nexia/config_flow.py b/homeassistant/components/nexia/config_flow.py index 5844cb8da20..59f1b211da5 100644 --- a/homeassistant/components/nexia/config_flow.py +++ b/homeassistant/components/nexia/config_flow.py @@ -20,6 +20,8 @@ async def validate_input(hass: core.HomeAssistant, data): Data has the keys from DATA_SCHEMA with values provided by the user. """ + + state_file = hass.config.path(f"nexia_config_{data[CONF_USERNAME]}.conf") try: nexia_home = NexiaHome( username=data[CONF_USERNAME], @@ -27,6 +29,7 @@ async def validate_input(hass: core.HomeAssistant, data): auto_login=False, auto_update=False, device_name=hass.config.location_name, + state_file=state_file, ) await hass.async_add_executor_job(nexia_home.login) except ConnectTimeout as ex: diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index a58330ad227..f09d4d1a4d1 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia", - "requirements": ["nexia==0.9.1"], + "requirements": ["nexia==0.9.2"], "codeowners": ["@ryannazaretian", "@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true diff --git a/requirements_all.txt b/requirements_all.txt index cd773652112..606a5bc73b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -922,7 +922,7 @@ netdisco==2.6.0 neurio==0.3.1 # homeassistant.components.nexia -nexia==0.9.1 +nexia==0.9.2 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ff64c959be8..ccbdc92b90a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -357,7 +357,7 @@ nessclient==0.9.15 netdisco==2.6.0 # homeassistant.components.nexia -nexia==0.9.1 +nexia==0.9.2 # homeassistant.components.nsw_fuel_station nsw-fuel-api-client==1.0.10 From 16e1b3772c87ea48ea598d9bc73a89116c096f29 Mon Sep 17 00:00:00 2001 From: ochlocracy <5885236+ochlocracy@users.noreply.github.com> Date: Tue, 21 Apr 2020 13:46:56 -0400 Subject: [PATCH 6/8] Modify requirements for CameraStreamController in Alexa (#34470) --- homeassistant/components/alexa/entities.py | 11 +++++------ tests/components/alexa/test_smart_home.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index fce05c8dc86..d4676344a62 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -791,19 +791,18 @@ class CameraCapabilities(AlexaEntity): yield Alexa(self.hass) def _check_requirements(self): - """Check the hass URL for HTTPS scheme and port 443.""" + """Check the hass URL for HTTPS scheme.""" if "stream" not in self.hass.config.components: - _LOGGER.error( + _LOGGER.debug( "%s requires stream component for AlexaCameraStreamController", self.entity_id, ) return False url = urlparse(network.async_get_external_url(self.hass)) - if url.scheme != "https" or (url.port is not None and url.port != 443): - _LOGGER.error( - "%s requires HTTPS support on port 443 for AlexaCameraStreamController", - self.entity_id, + if url.scheme != "https": + _LOGGER.debug( + "%s requires HTTPS for AlexaCameraStreamController", self.entity_id ) return False diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index a0d40460373..293727b4294 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -3806,9 +3806,9 @@ async def test_camera_discovery_without_stream(hass): "url,result", [ ("http://nohttpswrongport.org:8123", 2), - ("https://httpswrongport.org:8123", 2), ("http://nohttpsport443.org:443", 2), ("tls://nohttpsport443.org:443", 2), + ("https://httpsnnonstandport.org:8123", 3), ("https://correctschemaandport.org:443", 3), ("https://correctschemaandport.org", 3), ], From 12e92d504dcf4f3b13c5f7bd2689072fc8508442 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 21 Apr 2020 17:55:09 +0200 Subject: [PATCH 7/8] Fix cloud binary_sensor & TTS/STT (#34505) --- homeassistant/components/cloud/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index a40529b6fe4..ad97284d857 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -190,8 +190,6 @@ async def async_setup(hass, config): client = CloudClient(hass, prefs, websession, alexa_conf, google_conf) cloud = hass.data[DOMAIN] = Cloud(client, **kwargs) - await cloud.start() - async def _shutdown(event): """Shutdown event.""" await cloud.stop() @@ -233,6 +231,7 @@ async def async_setup(hass, config): cloud.iot.register_on_connect(_on_connect) + await cloud.start() await http_api.async_setup(hass) account_link.async_setup(hass) From 61620dec3097f658fa67b6c35e27b435fddb08c7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 21 Apr 2020 16:27:13 -0700 Subject: [PATCH 8/8] Bumped version to 0.108.7 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 33e0c685d26..6eab813b67d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 108 -PATCH_VERSION = "6" +PATCH_VERSION = "7" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0)