Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
async def test_invalid_password(hass):
"""Test that an invalid password throws an error."""
conf = {
CONF_IP_ADDRESS: "192.168.1.100",
CONF_PASSWORD: "bad_password",
CONF_PORT: 8080,
CONF_SSL: True,
}
flow = config_flow.RainMachineFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.rainmachine.config_flow.login",
return_value=mock_coro(exception=RainMachineError),
):
result = await flow.async_step_user(user_input=conf)
assert result["errors"] == {CONF_PASSWORD: "invalid_credentials"}
websession,
port=config_entry.data[CONF_PORT],
ssl=config_entry.data[CONF_SSL],
)
rainmachine = RainMachine(
client,
config_entry.data.get(CONF_BINARY_SENSORS, {}).get(
CONF_MONITORED_CONDITIONS, list(BINARY_SENSORS)
),
config_entry.data.get(CONF_SENSORS, {}).get(
CONF_MONITORED_CONDITIONS, list(SENSORS)
),
config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
)
await rainmachine.async_update()
except RainMachineError as err:
_LOGGER.error("An error occurred: %s", err)
raise ConfigEntryNotReady
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = rainmachine
for component in ("binary_sensor", "sensor", "switch"):
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)
async def refresh(event_time):
"""Refresh RainMachine sensor data."""
_LOGGER.debug("Updating RainMachine sensor data")
await rainmachine.async_update()
async_dispatcher_send(hass, SENSOR_UPDATE_TOPIC)
)
):
tasks[RESTRICTIONS_CURRENT] = self.client.restrictions.current()
if (
any(
c in self.binary_sensor_conditions
for c in (TYPE_FREEZE_PROTECTION, TYPE_HOT_DAYS)
)
or TYPE_FREEZE_TEMP in self.sensor_conditions
):
tasks[RESTRICTIONS_UNIVERSAL] = self.client.restrictions.universal()
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
for operation, result in zip(tasks, results):
if isinstance(result, RainMachineError):
_LOGGER.error(
"There was an error while updating %s: %s", operation, result
)
continue
self.data[operation] = result
return await self._show_form()
if user_input[CONF_IP_ADDRESS] in configured_instances(self.hass):
return await self._show_form({CONF_IP_ADDRESS: "identifier_exists"})
websession = aiohttp_client.async_get_clientsession(self.hass)
try:
await login(
user_input[CONF_IP_ADDRESS],
user_input[CONF_PASSWORD],
websession,
port=user_input.get(CONF_PORT, DEFAULT_PORT),
ssl=True,
)
except RainMachineError:
return await self._show_form({CONF_PASSWORD: "invalid_credentials"})
# Since the config entry doesn't allow for configuration of SSL, make
# sure it's set:
if user_input.get(CONF_SSL) is None:
user_input[CONF_SSL] = DEFAULT_SSL
# Timedeltas are easily serializable, so store the seconds instead:
scan_interval = user_input.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
user_input[CONF_SCAN_INTERVAL] = scan_interval.seconds
# Unfortunately, RainMachine doesn't provide a way to refresh the
# access token without using the IP address and password, so we have to
# store it:
return self.async_create_entry(
title=user_input[CONF_IP_ADDRESS], data=user_input
Note that this call does not take into account interested entities when making
the API calls; we make the reasonable assumption that switches will always be
enabled.
"""
_LOGGER.debug("Updating program and zone data for RainMachine")
tasks = {
DATA_PROGRAMS: self.async_fetch_from_api(DATA_PROGRAMS),
DATA_ZONES: self.async_fetch_from_api(DATA_ZONES),
DATA_ZONES_DETAILS: self.async_fetch_from_api(DATA_ZONES_DETAILS),
}
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
for api_category, result in zip(tasks, results):
if isinstance(result, RainMachineError):
_LOGGER.error(
"There was an error while updating %s: %s", api_category, result
)
async_dispatcher_send(self.hass, PROGRAM_UPDATE_TOPIC)
async_dispatcher_send(self.hass, ZONE_UPDATE_TOPIC)
async def async_setup_entry(hass, config_entry):
"""Set up RainMachine as config entry."""
_verify_domain_control = verify_domain_control(hass, DOMAIN)
websession = aiohttp_client.async_get_clientsession(hass)
client = Client(websession)
try:
await client.load_local(
config_entry.data[CONF_IP_ADDRESS],
config_entry.data[CONF_PASSWORD],
port=config_entry.data[CONF_PORT],
ssl=config_entry.data[CONF_SSL],
)
except RainMachineError as err:
_LOGGER.error("An error occurred: %s", err)
raise ConfigEntryNotReady
else:
# regenmaschine can load multiple controllers at once, but we only grab the one
# we loaded above:
controller = next(iter(client.controllers.values()))
rainmachine = RainMachine(
hass,
controller,
config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
config_entry.data[CONF_SCAN_INTERVAL],
)
# Update the data object, which at this point (prior to any sensors registering
# "interest" in the API), will focus on grabbing the latest program and zone data:
def setup(hass, config):
"""Set up the RainMachine component."""
from regenmaschine import Authenticator, Client
from regenmaschine.exceptions import HTTPError
from requests.exceptions import ConnectTimeout
conf = config[DOMAIN]
ip_address = conf[CONF_IP_ADDRESS]
password = conf[CONF_PASSWORD]
port = conf[CONF_PORT]
ssl = conf[CONF_SSL]
_LOGGER.debug('Setting up RainMachine client')
try:
auth = Authenticator.create_local(
ip_address, password, port=port, https=ssl)
client = Client(auth)
hass.data[DATA_RAINMACHINE] = RainMachine(client)
except (HTTPError, ConnectTimeout, UnboundLocalError) as exc_info:
_LOGGER.error('An error occurred: %s', str(exc_info))
hass.components.persistent_notification.create(
'Error: {0}<br>'
'You will need to restart hass after fixing.'
''.format(exc_info),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
_LOGGER.debug('Setting up switch platform')
switch_config = conf.get(CONF_SWITCHES, {})
discovery.load_platform(hass, 'switch', DOMAIN, switch_config, config)
from regenmaschine import Authenticator, Client
from regenmaschine.exceptions import HTTPError
from requests.exceptions import ConnectTimeout
conf = config[DOMAIN]
ip_address = conf[CONF_IP_ADDRESS]
password = conf[CONF_PASSWORD]
port = conf[CONF_PORT]
ssl = conf[CONF_SSL]
_LOGGER.debug('Setting up RainMachine client')
try:
auth = Authenticator.create_local(
ip_address, password, port=port, https=ssl)
client = Client(auth)
hass.data[DATA_RAINMACHINE] = RainMachine(client)
except (HTTPError, ConnectTimeout, UnboundLocalError) as exc_info:
_LOGGER.error('An error occurred: %s', str(exc_info))
hass.components.persistent_notification.create(
'Error: {0}<br>'
'You will need to restart hass after fixing.'
''.format(exc_info),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
_LOGGER.debug('Setting up switch platform')
switch_config = conf.get(CONF_SWITCHES, {})
discovery.load_platform(hass, 'switch', DOMAIN, switch_config, config)
_LOGGER.debug('Setup complete')
async def async_setup_entry(hass, config_entry):
"""Set up RainMachine as config entry."""
_verify_domain_control = verify_domain_control(hass, DOMAIN)
websession = aiohttp_client.async_get_clientsession(hass)
client = Client(websession)
try:
await client.load_local(
config_entry.data[CONF_IP_ADDRESS],
config_entry.data[CONF_PASSWORD],
port=config_entry.data[CONF_PORT],
ssl=config_entry.data[CONF_SSL],
)
except RainMachineError as err:
_LOGGER.error("An error occurred: %s", err)
raise ConfigEntryNotReady
else:
# regenmaschine can load multiple controllers at once, but we only grab the one
# we loaded above:
controller = next(iter(client.controllers.values()))
ATTR_PRECIP_RATE: self._properties_json.get("waterSense").get(
"precipitationRate"
),
ATTR_RESTRICTIONS: self._obj.get("restriction"),
ATTR_SLOPE: SLOPE_TYPE_MAP.get(self._properties_json.get("slope")),
ATTR_SOIL_TYPE: SOIL_TYPE_MAP.get(self._properties_json.get("sun")),
ATTR_SPRINKLER_TYPE: SPRINKLER_TYPE_MAP.get(
self._properties_json.get("group_id")
),
ATTR_SUN_EXPOSURE: SUN_EXPOSURE_MAP.get(
self._properties_json.get("sun")
),
ATTR_VEGETATION_TYPE: VEGETATION_MAP.get(self._obj.get("type")),
}
)
except RequestError as err:
_LOGGER.error(
'Unable to update info for zone "%s": %s', self.unique_id, str(err)
)