Skip to content

Commit

Permalink
Switch to dispatcher for internal communication
Browse files Browse the repository at this point in the history
  • Loading branch information
cgtobi committed Aug 6, 2020
1 parent e208d8b commit 070974a
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 150 deletions.
65 changes: 29 additions & 36 deletions homeassistant/components/netatmo/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect

from .const import (
ATTR_PERSON,
Expand All @@ -21,6 +22,8 @@
DATA_HANDLER,
DATA_PERSONS,
DOMAIN,
EVENT_TYPE_OFF,
EVENT_TYPE_ON,
MANUFACTURER,
MODELS,
SERVICE_SETPERSONAWAY,
Expand All @@ -34,20 +37,6 @@

DEFAULT_QUALITY = "high"

SCHEMA_SERVICE_SETPERSONSHOME = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CAMERA_DOMAIN),
vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string]),
}
)

SCHEMA_SERVICE_SETPERSONAWAY = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CAMERA_DOMAIN),
vol.Optional(ATTR_PERSON): cv.string,
}
)


async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the Netatmo camera platform."""
Expand Down Expand Up @@ -109,21 +98,22 @@ async def get_entities():
if data_handler.data[CAMERA_DATA_CLASS_NAME] is not None:
platform.async_register_entity_service(
SERVICE_SETPERSONSHOME,
SCHEMA_SERVICE_SETPERSONSHOME,
"_service_setpersonshome",
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CAMERA_DOMAIN),
vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string]),
},
"_service_set_persons_home",
)
platform.async_register_entity_service(
SERVICE_SETPERSONAWAY,
SCHEMA_SERVICE_SETPERSONAWAY,
"_service_setpersonaway",
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CAMERA_DOMAIN),
vol.Optional(ATTR_PERSON): cv.string,
},
"_service_set_person_away",
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Netatmo camera platform."""
return


class NetatmoCamera(NetatmoBase, Camera):
"""Representation of a Netatmo camera."""

Expand Down Expand Up @@ -156,16 +146,19 @@ async def async_added_to_hass(self) -> None:
"""Entity created."""
await super().async_added_to_hass()

self._listeners.append(
self.hass.bus.async_listen("netatmo_event", self.handle_event)
)
for event_type in (EVENT_TYPE_OFF, EVENT_TYPE_ON):
self._listeners.append(
async_dispatcher_connect(
self.hass,
f"signal-{DOMAIN}-webhook-{event_type}",
self.handle_event,
)
)

async def handle_event(self, event):
@callback
def handle_event(self, event):
"""Handle webhook events."""
data = event.data["data"]

if not data.get("event_type"):
return
data = event["data"]

if not data.get("camera_id"):
return
Expand Down Expand Up @@ -278,7 +271,7 @@ def async_update_callback(self):
self._is_local = camera.get("is_local")
self.is_streaming = bool(self._status == "on")

def _service_setpersonshome(self, **kwargs):
def _service_set_persons_home(self, **kwargs):
"""Service to change current home schedule."""
persons = kwargs.get(ATTR_PERSONS)
person_ids = []
Expand All @@ -288,9 +281,9 @@ def _service_setpersonshome(self, **kwargs):
person_ids.append(pid)

self._data.set_persons_home(person_ids=person_ids, home_id=self._home_id)
_LOGGER.info("Set %s as at home", persons)
_LOGGER.debug("Set %s as at home", persons)

def _service_setpersonaway(self, **kwargs):
def _service_set_person_away(self, **kwargs):
"""Service to mark a person as away or set the home as empty."""
person = kwargs.get(ATTR_PERSON)
person_id = None
Expand All @@ -303,10 +296,10 @@ def _service_setpersonaway(self, **kwargs):
self._data.set_persons_away(
person_id=person_id, home_id=self._home_id,
)
_LOGGER.info("Set %s as away", person)
_LOGGER.debug("Set %s as away", person)

else:
self._data.set_persons_away(
person_id=person_id, home_id=self._home_id,
)
_LOGGER.info("Set home as empty")
_LOGGER.debug("Set home as empty")
99 changes: 52 additions & 47 deletions homeassistant/components/netatmo/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect

from .const import (
ATTR_HEATING_POWER_REQUEST,
Expand All @@ -35,6 +36,9 @@
DATA_HOMES,
DATA_SCHEDULES,
DOMAIN,
EVENT_TYPE_CANCEL_SET_POINT,
EVENT_TYPE_SET_POINT,
EVENT_TYPE_THERM_MODE,
MANUFACTURER,
SERVICE_SETSCHEDULE,
SIGNAL_NAME,
Expand Down Expand Up @@ -95,13 +99,6 @@
NA_THERM = "NATherm1"
NA_VALVE = "NRV"

SCHEMA_SERVICE_SETSCHEDULE = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CLIMATE_DOMAIN),
vol.Required(ATTR_SCHEDULE_NAME): cv.string,
}
)


async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the Netatmo energy platform."""
Expand Down Expand Up @@ -156,15 +153,15 @@ async def get_entities():

if home_data is not None:
platform.async_register_entity_service(
SERVICE_SETSCHEDULE, SCHEMA_SERVICE_SETSCHEDULE, "_service_setschedule",
SERVICE_SETSCHEDULE,
{
vol.Required(ATTR_ENTITY_ID): cv.entity_domain(CLIMATE_DOMAIN),
vol.Required(ATTR_SCHEDULE_NAME): cv.string,
},
"_service_set_schedule",
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Netatmo energy sensors."""
return


class NetatmoThermostat(NetatmoBase, ClimateEntity):
"""Representation a Netatmo thermostat."""

Expand Down Expand Up @@ -229,23 +226,29 @@ async def async_added_to_hass(self) -> None:
"""Entity created."""
await super().async_added_to_hass()

self._listeners.append(
self.hass.bus.async_listen("netatmo_event", self.handle_event)
)
for event_type in (
EVENT_TYPE_SET_POINT,
EVENT_TYPE_THERM_MODE,
EVENT_TYPE_CANCEL_SET_POINT,
):
self._listeners.append(
async_dispatcher_connect(
self.hass,
f"signal-{DOMAIN}-webhook-{event_type}",
self.handle_event,
)
)

async def handle_event(self, event):
"""Handle webhook events."""
data = event.data["data"]

if not data.get("event_type"):
return
data = event["data"]

if not data.get("home"):
return

home = data["home"]
if self._home_id == home["id"] and data["event_type"] == "therm_mode":
self._preset = NETATMO_MAP_PRESET[home["therm_mode"]]
if self._home_id == home["id"] and data["event_type"] == EVENT_TYPE_THERM_MODE:
self._preset = NETATMO_MAP_PRESET[home[EVENT_TYPE_THERM_MODE]]
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
if self._preset == PRESET_FROST_GUARD:
self._target_temperature = self._hg_temperature
Expand All @@ -260,7 +263,7 @@ async def handle_event(self, event):
return

for room in home["rooms"]:
if data["event_type"] == "set_point":
if data["event_type"] == EVENT_TYPE_SET_POINT:
if self._id == room["id"]:
if room["therm_setpoint_mode"] == "off":
self._hvac_mode = HVAC_MODE_OFF
Expand All @@ -269,7 +272,7 @@ async def handle_event(self, event):
self.async_write_ha_state()
break

elif data["event_type"] == "cancel_set_point":
elif data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT:
if self._id == room["id"]:
self.async_update_callback()
self.async_write_ha_state()
Expand Down Expand Up @@ -411,36 +414,38 @@ def available(self) -> bool:
def async_update_callback(self):
"""Update the entity's state."""
self._home_status = self.data_handler.data[self._home_status_class]
self._room_status = self._home_status.rooms[self._id]
self._room_data = self._data.rooms[self._home_id][self._id]
self._room_status = self._home_status.rooms.get(self._id)
self._room_data = self._data.rooms.get(self._home_id, {}).get(self._id)

if not self._room_status or not self._room_data:
if self._connected:
_LOGGER.info(
"The thermostat in room %s seems to be out of reach.",
self._device_name,
)

self._connected = False
return

roomstatus = {"roomID": self._room_status["id"]}
roomstatus = {"roomID": self._room_status.get("id", {})}
if self._room_status.get("reachable"):
roomstatus.update(self._build_room_status())

self._away_temperature = self._data.get_away_temp(self._home_id)
self._hg_temperature = self._data.get_hg_temp(self._home_id)
self._setpoint_duration = self._data.setpoint_duration[self._home_id]

try:
if self._model is None:
self._model = roomstatus["module_type"]
self._current_temperature = roomstatus["current_temperature"]
self._target_temperature = roomstatus["target_temperature"]
self._preset = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
self._battery_level = roomstatus.get("battery_level")
self._connected = True

except KeyError as err:
if self._connected:
_LOGGER.debug(
"The thermostat in room %s seems to be out of reach. (%s)",
self._device_name,
err,
)
if "current_temperature" not in roomstatus:
return

self._connected = False
if self._model is None:
self._model = roomstatus["module_type"]
self._current_temperature = roomstatus["current_temperature"]
self._target_temperature = roomstatus["target_temperature"]
self._preset = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
self._battery_level = roomstatus.get("battery_level")
self._connected = True

self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]

Expand Down Expand Up @@ -503,7 +508,7 @@ def _build_room_status(self):

return {}

def _service_setschedule(self, **kwargs):
def _service_set_schedule(self, **kwargs):
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
schedule_id = None
for sid, name in self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].items():
Expand All @@ -515,7 +520,7 @@ def _service_setschedule(self, **kwargs):
return

self._data.switch_home_schedule(home_id=self._home_id, schedule_id=schedule_id)
_LOGGER.info(
_LOGGER.debug(
"Setting %s schedule to %s (%s)",
self._home_id,
kwargs.get(ATTR_SCHEDULE_NAME),
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/netatmo/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def async_step_user(self, user_input=None):
"""Handle a flow start."""
await self.async_set_unique_id(DOMAIN)

if self.hass.config_entries.async_entries(DOMAIN):
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")

return await super().async_step_user(user_input)
Expand Down Expand Up @@ -108,7 +108,7 @@ async def async_step_public_weather_areas(self, user_input=None):
user_input={CONF_NEW_AREA: new_client}
)

return self._update_options()
return self._create_new_options_entry()

weather_areas = list(self.options[CONF_WEATHER_AREAS])

Expand Down Expand Up @@ -183,7 +183,7 @@ async def async_step_public_weather(self, user_input=None):

return self.async_show_form(step_id="public_weather", data_schema=data_schema)

def _update_options(self):
def _create_options_entry(self):
"""Update config entry options."""
return self.async_create_entry(
title="Netatmo Public Weather", data=self.options
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/netatmo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,10 @@
SERVICE_SETSCHEDULE = "set_schedule"
SERVICE_SETPERSONSHOME = "set_persons_home"
SERVICE_SETPERSONAWAY = "set_person_away"

EVENT_TYPE_CANCEL_SET_POINT = "cancel_set_point"
EVENT_TYPE_LIGHT_MODE = "light_mode"
EVENT_TYPE_OFF = "off"
EVENT_TYPE_ON = "on"
EVENT_TYPE_SET_POINT = "set_point"
EVENT_TYPE_THERM_MODE = "therm_mode"
Loading

0 comments on commit 070974a

Please sign in to comment.