Merge pull request #34517 from home-assistant/rc

pull/54114/head 0.108.7
Paulus Schoutsen 2020-04-21 17:26:36 -07:00 committed by GitHub
commit 9b757e4c22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 222 additions and 257 deletions

View File

@ -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

View File

@ -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,12 +190,6 @@ 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)
async def _shutdown(event):
"""Shutdown event."""
await cloud.stop()
@ -230,20 +223,15 @@ 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)
await cloud.start()
await http_api.async_setup(hass)
account_link.async_setup(hass)

View File

@ -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"]

View File

@ -1,14 +1,8 @@
"""Support for Modbus."""
import asyncio
import logging
import threading
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.exceptions import ModbusException
from pymodbus.pdu import ExceptionResponse
from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient, ModbusUdpClient
from pymodbus.transaction import ModbusRtuFramer
import voluptuous as vol
@ -42,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(
@ -93,60 +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()
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):
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
await hass.async_add_executor_job(start_modbus)
for client in hub_collect.values():
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
@ -154,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]
@ -178,136 +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
def setup(self):
"""Set up pymodbus client."""
# pylint: disable = E0633
# Client* do deliver loop, client as result but
# pylint does not accept that fact
if self._config_type == "serial":
_, self._client = ClientSerial(
schedulers.ASYNC_IO,
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,
loop=self._loop,
timeout=self._config_timeout,
)
elif self._config_type == "rtuovertcp":
_, self._client = ClientTCP(
schedulers.ASYNC_IO,
self._client = ModbusTcpClient(
host=self._config_host,
port=self._config_port,
framer=ModbusRtuFramer,
timeout=self._config_timeout,
loop=self._loop,
)
elif self._config_type == "tcp":
_, self._client = ClientTCP(
schedulers.ASYNC_IO,
self._client = ModbusTcpClient(
host=self._config_host,
port=self._config_port,
timeout=self._config_timeout,
loop=self._loop,
)
elif self._config_type == "udp":
_, self._client = ClientUDP(
schedulers.ASYNC_IO,
self._client = ModbusUdpClient(
host=self._config_host,
port=self._config_port,
timeout=self._config_timeout,
loop=self._loop,
)
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)

View File

@ -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

View File

@ -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

View File

@ -3,6 +3,5 @@
"name": "Modbus",
"documentation": "https://www.home-assistant.io/integrations/modbus",
"requirements": ["pymodbus==2.3.0"],
"dependencies": [],
"codeowners": ["@adamchengtkc", "@janiversen"]
}

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
@ -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

View File

@ -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
@ -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

View File

@ -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),
],

View File

@ -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"

View File

@ -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()

View File

@ -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()

View File

@ -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):