Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ homeassistant/components/usgs_earthquakes_feed/* @exxamalte
homeassistant/components/utility_meter/* @dgomes
homeassistant/components/velbus/* @Cereal2nd @brefra
homeassistant/components/velux/* @Julius2342
homeassistant/components/vera/* @vangorra
homeassistant/components/versasense/* @flamm3blemuff1n
homeassistant/components/version/* @fabaff
homeassistant/components/vesync/* @markperdue @webdjoe
Expand Down
121 changes: 76 additions & 45 deletions homeassistant/components/vera/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Support for Vera devices."""
import asyncio
from collections import defaultdict
import logging

import pyvera as veraApi
from requests.exceptions import RequestException
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ARMED,
ATTR_BATTERY_LEVEL,
Expand All @@ -15,26 +18,23 @@
CONF_LIGHTS,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import convert, slugify
from homeassistant.util.dt import utc_from_timestamp

_LOGGER = logging.getLogger(__name__)

DOMAIN = "vera"

VERA_CONTROLLER = "vera_controller"

CONF_CONTROLLER = "vera_controller_url"

VERA_ID_FORMAT = "{}_{}"

ATTR_CURRENT_POWER_W = "current_power_w"
ATTR_CURRENT_ENERGY_KWH = "current_energy_kwh"
from .common import ControllerData, get_configured_platforms
from .config_flow import new_options
from .const import (
ATTR_CURRENT_ENERGY_KWH,
ATTR_CURRENT_POWER_W,
CONF_CONTROLLER,
DOMAIN,
VERA_ID_FORMAT,
)

VERA_DEVICES = "vera_devices"
VERA_SCENES = "vera_scenes"
_LOGGER = logging.getLogger(__name__)

VERA_ID_LIST_SCHEMA = vol.Schema([int])

Expand All @@ -51,42 +51,53 @@
extra=vol.ALLOW_EXTRA,
)

VERA_COMPONENTS = [
"binary_sensor",
"sensor",
"light",
"switch",
"lock",
"climate",
"cover",
"scene",
]

async def async_setup(hass: HomeAssistant, base_config: dict) -> bool:
"""Set up for Vera controllers."""
config = base_config.get(DOMAIN)

def setup(hass, base_config):
"""Set up for Vera devices."""
if not config:
return True

def stop_subscription(event):
"""Shutdown Vera subscriptions and subscription thread on exit."""
_LOGGER.info("Shutting down subscriptions")
hass.data[VERA_CONTROLLER].stop()
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=config,
)
)

config = base_config.get(DOMAIN)
return True

# Get Vera specific configuration.
base_url = config.get(CONF_CONTROLLER)
light_ids = config.get(CONF_LIGHTS)
exclude_ids = config.get(CONF_EXCLUDE)

async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Do setup of vera."""
# Use options entered during initial config flow or provided from configuration.yml
if config_entry.data.get(CONF_LIGHTS) or config_entry.data.get(CONF_EXCLUDE):
hass.config_entries.async_update_entry(
entry=config_entry,
data=config_entry.data,
options=new_options(
config_entry.data.get(CONF_LIGHTS, []),
config_entry.data.get(CONF_EXCLUDE, []),
),
)

base_url = config_entry.data[CONF_CONTROLLER]
light_ids = config_entry.options.get(CONF_LIGHTS, [])
Comment thread
vangorra marked this conversation as resolved.
exclude_ids = config_entry.options.get(CONF_EXCLUDE, [])

# Initialize the Vera controller.
controller, _ = veraApi.init_controller(base_url)
hass.data[VERA_CONTROLLER] = controller
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_subscription)
controller = veraApi.VeraController(base_url)
controller.start()

hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP,
lambda event: hass.async_add_executor_job(controller.stop),
)

try:
all_devices = controller.get_devices()
all_devices = await hass.async_add_executor_job(controller.get_devices)

all_scenes = controller.get_scenes()
all_scenes = await hass.async_add_executor_job(controller.get_scenes)
except RequestException:
# There was a network related error connecting to the Vera controller.
_LOGGER.exception("Error communicating with Vera API")
Expand All @@ -102,15 +113,35 @@ def stop_subscription(event):
continue

vera_devices[device_type].append(device)
hass.data[VERA_DEVICES] = vera_devices

vera_scenes = []
for scene in all_scenes:
vera_scenes.append(scene)
hass.data[VERA_SCENES] = vera_scenes

for component in VERA_COMPONENTS:
discovery.load_platform(hass, component, DOMAIN, {}, base_config)
controller_data = ControllerData(
controller=controller, devices=vera_devices, scenes=vera_scenes
)

hass.data[DOMAIN] = controller_data

# Forward the config data to the necessary platforms.
for platform in get_configured_platforms(controller_data):
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)

return True


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload Withings config entry."""
controller_data = hass.data[DOMAIN]

tasks = [
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in get_configured_platforms(controller_data)
]
await asyncio.gather(*tasks)

return True

Expand Down
31 changes: 22 additions & 9 deletions homeassistant/components/vera/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
"""Support for Vera binary sensors."""
import logging
from typing import Callable, List

from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT, BinarySensorDevice
from homeassistant.components.binary_sensor import (
DOMAIN as PLATFORM_DOMAIN,
ENTITY_ID_FORMAT,
BinarySensorDevice,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity

from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice
from . import VeraDevice
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Perform the setup for Vera controller devices."""
add_entities(
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up the sensor config entry."""
controller_data = hass.data[DOMAIN]
async_add_entities(
[
VeraBinarySensor(device, hass.data[VERA_CONTROLLER])
for device in hass.data[VERA_DEVICES]["binary_sensor"]
],
True,
VeraBinarySensor(device, controller_data.controller)
for device in controller_data.devices.get(PLATFORM_DOMAIN)
]
)


Expand Down
31 changes: 22 additions & 9 deletions homeassistant/components/vera/climate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
"""Support for Vera thermostats."""
import logging
from typing import Callable, List

from homeassistant.components.climate import ENTITY_ID_FORMAT, ClimateDevice
from homeassistant.components.climate import (
DOMAIN as PLATFORM_DOMAIN,
ENTITY_ID_FORMAT,
ClimateDevice,
)
from homeassistant.components.climate.const import (
FAN_AUTO,
FAN_ON,
Expand All @@ -12,10 +17,14 @@
SUPPORT_FAN_MODE,
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.util import convert

from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice
from . import VeraDevice
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

Expand All @@ -25,14 +34,18 @@
SUPPORT_HVAC = [HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF]


def setup_platform(hass, config, add_entities_callback, discovery_info=None):
"""Set up of Vera thermostats."""
add_entities_callback(
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up the sensor config entry."""
controller_data = hass.data[DOMAIN]
async_add_entities(
[
VeraThermostat(device, hass.data[VERA_CONTROLLER])
for device in hass.data[VERA_DEVICES]["climate"]
],
True,
VeraThermostat(device, controller_data.controller)
for device in controller_data.devices.get(PLATFORM_DOMAIN)
]
)


Expand Down
29 changes: 29 additions & 0 deletions homeassistant/components/vera/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Common vera code."""
Comment thread
MartinHjelmare marked this conversation as resolved.
import logging
from typing import DefaultDict, List, NamedTuple, Set

import pyvera as pv

from homeassistant.components.scene import DOMAIN as SCENE_DOMAIN

_LOGGER = logging.getLogger(__name__)


class ControllerData(NamedTuple):
"""Controller data."""

controller: pv.VeraController
devices: DefaultDict[str, List[pv.VeraDevice]]
scenes: List[pv.VeraScene]


def get_configured_platforms(controller_data: ControllerData) -> Set[str]:
"""Get configured platforms for a controller."""
platforms = []
for platform in controller_data.devices:
platforms.append(platform)

if controller_data.scenes:
platforms.append(SCENE_DOMAIN)

return set(platforms)
Loading