mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Switch Netatmo integration to dispatcher for internal communication (#38590)
* Switch to dispatcher for internal communication * Fix method call * Update homeassistant/components/netatmo/camera.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/netatmo/camera.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/netatmo/climate.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/netatmo/climate.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Rename variables Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
3546a82cfb
commit
6930aebea2
@ -5,14 +5,10 @@ import pyatmo
|
|||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import SUPPORT_STREAM, Camera
|
||||||
DOMAIN as CAMERA_DOMAIN,
|
|
||||||
SUPPORT_STREAM,
|
|
||||||
Camera,
|
|
||||||
)
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_PERSON,
|
ATTR_PERSON,
|
||||||
@ -21,10 +17,12 @@ from .const import (
|
|||||||
DATA_HANDLER,
|
DATA_HANDLER,
|
||||||
DATA_PERSONS,
|
DATA_PERSONS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
EVENT_TYPE_OFF,
|
||||||
|
EVENT_TYPE_ON,
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
MODELS,
|
MODELS,
|
||||||
SERVICE_SETPERSONAWAY,
|
SERVICE_SET_PERSON_AWAY,
|
||||||
SERVICE_SETPERSONSHOME,
|
SERVICE_SET_PERSONS_HOME,
|
||||||
SIGNAL_NAME,
|
SIGNAL_NAME,
|
||||||
)
|
)
|
||||||
from .data_handler import CAMERA_DATA_CLASS_NAME
|
from .data_handler import CAMERA_DATA_CLASS_NAME
|
||||||
@ -34,20 +32,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
DEFAULT_QUALITY = "high"
|
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):
|
async def async_setup_entry(hass, entry, async_add_entities):
|
||||||
"""Set up the Netatmo camera platform."""
|
"""Set up the Netatmo camera platform."""
|
||||||
@ -108,22 +92,17 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
|
|
||||||
if data_handler.data[CAMERA_DATA_CLASS_NAME] is not None:
|
if data_handler.data[CAMERA_DATA_CLASS_NAME] is not None:
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_SETPERSONSHOME,
|
SERVICE_SET_PERSONS_HOME,
|
||||||
SCHEMA_SERVICE_SETPERSONSHOME,
|
{vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string])},
|
||||||
"_service_setpersonshome",
|
"_service_set_persons_home",
|
||||||
)
|
)
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_SETPERSONAWAY,
|
SERVICE_SET_PERSON_AWAY,
|
||||||
SCHEMA_SERVICE_SETPERSONAWAY,
|
{vol.Optional(ATTR_PERSON): cv.string},
|
||||||
"_service_setpersonaway",
|
"_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):
|
class NetatmoCamera(NetatmoBase, Camera):
|
||||||
"""Representation of a Netatmo camera."""
|
"""Representation of a Netatmo camera."""
|
||||||
|
|
||||||
@ -156,16 +135,19 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||||||
"""Entity created."""
|
"""Entity created."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
self._listeners.append(
|
for event_type in (EVENT_TYPE_OFF, EVENT_TYPE_ON):
|
||||||
self.hass.bus.async_listen("netatmo_event", self.handle_event)
|
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."""
|
"""Handle webhook events."""
|
||||||
data = event.data["data"]
|
data = event["data"]
|
||||||
|
|
||||||
if not data.get("event_type"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not data.get("camera_id"):
|
if not data.get("camera_id"):
|
||||||
return
|
return
|
||||||
@ -278,7 +260,7 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||||||
self._is_local = camera.get("is_local")
|
self._is_local = camera.get("is_local")
|
||||||
self.is_streaming = bool(self._status == "on")
|
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."""
|
"""Service to change current home schedule."""
|
||||||
persons = kwargs.get(ATTR_PERSONS)
|
persons = kwargs.get(ATTR_PERSONS)
|
||||||
person_ids = []
|
person_ids = []
|
||||||
@ -288,9 +270,9 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||||||
person_ids.append(pid)
|
person_ids.append(pid)
|
||||||
|
|
||||||
self._data.set_persons_home(person_ids=person_ids, home_id=self._home_id)
|
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."""
|
"""Service to mark a person as away or set the home as empty."""
|
||||||
person = kwargs.get(ATTR_PERSON)
|
person = kwargs.get(ATTR_PERSON)
|
||||||
person_id = None
|
person_id = None
|
||||||
@ -303,10 +285,10 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||||||
self._data.set_persons_away(
|
self._data.set_persons_away(
|
||||||
person_id=person_id, home_id=self._home_id,
|
person_id=person_id, home_id=self._home_id,
|
||||||
)
|
)
|
||||||
_LOGGER.info("Set %s as away", person)
|
_LOGGER.debug("Set %s as away", person)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._data.set_persons_away(
|
self._data.set_persons_away(
|
||||||
person_id=person_id, home_id=self._home_id,
|
person_id=person_id, home_id=self._home_id,
|
||||||
)
|
)
|
||||||
_LOGGER.info("Set home as empty")
|
_LOGGER.debug("Set home as empty")
|
||||||
|
@ -4,7 +4,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, ClimateEntity
|
from homeassistant.components.climate import ClimateEntity
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
CURRENT_HVAC_HEAT,
|
CURRENT_HVAC_HEAT,
|
||||||
CURRENT_HVAC_IDLE,
|
CURRENT_HVAC_IDLE,
|
||||||
@ -19,7 +19,6 @@ from homeassistant.components.climate.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_BATTERY_LEVEL,
|
ATTR_BATTERY_LEVEL,
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
PRECISION_HALVES,
|
PRECISION_HALVES,
|
||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
@ -27,6 +26,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_HEATING_POWER_REQUEST,
|
ATTR_HEATING_POWER_REQUEST,
|
||||||
@ -35,8 +35,11 @@ from .const import (
|
|||||||
DATA_HOMES,
|
DATA_HOMES,
|
||||||
DATA_SCHEDULES,
|
DATA_SCHEDULES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
EVENT_TYPE_CANCEL_SET_POINT,
|
||||||
|
EVENT_TYPE_SET_POINT,
|
||||||
|
EVENT_TYPE_THERM_MODE,
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
SERVICE_SETSCHEDULE,
|
SERVICE_SET_SCHEDULE,
|
||||||
SIGNAL_NAME,
|
SIGNAL_NAME,
|
||||||
)
|
)
|
||||||
from .data_handler import HOMEDATA_DATA_CLASS_NAME, HOMESTATUS_DATA_CLASS_NAME
|
from .data_handler import HOMEDATA_DATA_CLASS_NAME, HOMESTATUS_DATA_CLASS_NAME
|
||||||
@ -95,13 +98,6 @@ DEFAULT_MAX_TEMP = 30
|
|||||||
NA_THERM = "NATherm1"
|
NA_THERM = "NATherm1"
|
||||||
NA_VALVE = "NRV"
|
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):
|
async def async_setup_entry(hass, entry, async_add_entities):
|
||||||
"""Set up the Netatmo energy platform."""
|
"""Set up the Netatmo energy platform."""
|
||||||
@ -156,15 +152,12 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
|
|
||||||
if home_data is not None:
|
if home_data is not None:
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_SETSCHEDULE, SCHEMA_SERVICE_SETSCHEDULE, "_service_setschedule",
|
SERVICE_SET_SCHEDULE,
|
||||||
|
{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):
|
class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
||||||
"""Representation a Netatmo thermostat."""
|
"""Representation a Netatmo thermostat."""
|
||||||
|
|
||||||
@ -229,23 +222,29 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
"""Entity created."""
|
"""Entity created."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
self._listeners.append(
|
for event_type in (
|
||||||
self.hass.bus.async_listen("netatmo_event", self.handle_event)
|
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):
|
async def handle_event(self, event):
|
||||||
"""Handle webhook events."""
|
"""Handle webhook events."""
|
||||||
data = event.data["data"]
|
data = event["data"]
|
||||||
|
|
||||||
if not data.get("event_type"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not data.get("home"):
|
if not data.get("home"):
|
||||||
return
|
return
|
||||||
|
|
||||||
home = data["home"]
|
home = data["home"]
|
||||||
if self._home_id == home["id"] and data["event_type"] == "therm_mode":
|
if self._home_id == home["id"] and data["event_type"] == EVENT_TYPE_THERM_MODE:
|
||||||
self._preset = NETATMO_MAP_PRESET[home["therm_mode"]]
|
self._preset = NETATMO_MAP_PRESET[home[EVENT_TYPE_THERM_MODE]]
|
||||||
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
||||||
if self._preset == PRESET_FROST_GUARD:
|
if self._preset == PRESET_FROST_GUARD:
|
||||||
self._target_temperature = self._hg_temperature
|
self._target_temperature = self._hg_temperature
|
||||||
@ -260,7 +259,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for room in home["rooms"]:
|
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 self._id == room["id"]:
|
||||||
if room["therm_setpoint_mode"] == "off":
|
if room["therm_setpoint_mode"] == "off":
|
||||||
self._hvac_mode = HVAC_MODE_OFF
|
self._hvac_mode = HVAC_MODE_OFF
|
||||||
@ -269,7 +268,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
break
|
break
|
||||||
|
|
||||||
elif data["event_type"] == "cancel_set_point":
|
elif data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT:
|
||||||
if self._id == room["id"]:
|
if self._id == room["id"]:
|
||||||
self.async_update_callback()
|
self.async_update_callback()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -411,10 +410,20 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
def async_update_callback(self):
|
def async_update_callback(self):
|
||||||
"""Update the entity's state."""
|
"""Update the entity's state."""
|
||||||
self._home_status = self.data_handler.data[self._home_status_class]
|
self._home_status = self.data_handler.data[self._home_status_class]
|
||||||
self._room_status = self._home_status.rooms[self._id]
|
self._room_status = self._home_status.rooms.get(self._id)
|
||||||
self._room_data = self._data.rooms[self._home_id][self._id]
|
self._room_data = self._data.rooms.get(self._home_id, {}).get(self._id)
|
||||||
|
|
||||||
roomstatus = {"roomID": self._room_status["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.get("id", {})}
|
||||||
if self._room_status.get("reachable"):
|
if self._room_status.get("reachable"):
|
||||||
roomstatus.update(self._build_room_status())
|
roomstatus.update(self._build_room_status())
|
||||||
|
|
||||||
@ -422,25 +431,17 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
self._hg_temperature = self._data.get_hg_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]
|
self._setpoint_duration = self._data.setpoint_duration[self._home_id]
|
||||||
|
|
||||||
try:
|
if "current_temperature" not in roomstatus:
|
||||||
if self._model is None:
|
return
|
||||||
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._model is None:
|
||||||
if self._connected:
|
self._model = roomstatus["module_type"]
|
||||||
_LOGGER.debug(
|
self._current_temperature = roomstatus["current_temperature"]
|
||||||
"The thermostat in room %s seems to be out of reach. (%s)",
|
self._target_temperature = roomstatus["target_temperature"]
|
||||||
self._device_name,
|
self._preset = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
|
||||||
err,
|
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
||||||
)
|
self._battery_level = roomstatus.get("battery_level")
|
||||||
|
self._connected = True
|
||||||
self._connected = False
|
|
||||||
|
|
||||||
self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
||||||
|
|
||||||
@ -503,7 +504,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _service_setschedule(self, **kwargs):
|
def _service_set_schedule(self, **kwargs):
|
||||||
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
|
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
|
||||||
schedule_id = None
|
schedule_id = None
|
||||||
for sid, name in self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].items():
|
for sid, name in self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].items():
|
||||||
@ -515,7 +516,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._data.switch_home_schedule(home_id=self._home_id, schedule_id=schedule_id)
|
self._data.switch_home_schedule(home_id=self._home_id, schedule_id=schedule_id)
|
||||||
_LOGGER.info(
|
_LOGGER.debug(
|
||||||
"Setting %s schedule to %s (%s)",
|
"Setting %s schedule to %s (%s)",
|
||||||
self._home_id,
|
self._home_id,
|
||||||
kwargs.get(ATTR_SCHEDULE_NAME),
|
kwargs.get(ATTR_SCHEDULE_NAME),
|
||||||
|
@ -69,7 +69,7 @@ class NetatmoFlowHandler(
|
|||||||
"""Handle a flow start."""
|
"""Handle a flow start."""
|
||||||
await self.async_set_unique_id(DOMAIN)
|
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 self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
return await super().async_step_user(user_input)
|
return await super().async_step_user(user_input)
|
||||||
@ -108,7 +108,7 @@ class NetatmoOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
user_input={CONF_NEW_AREA: new_client}
|
user_input={CONF_NEW_AREA: new_client}
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._update_options()
|
return self._create_options_entry()
|
||||||
|
|
||||||
weather_areas = list(self.options[CONF_WEATHER_AREAS])
|
weather_areas = list(self.options[CONF_WEATHER_AREAS])
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ class NetatmoOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
|
|
||||||
return self.async_show_form(step_id="public_weather", data_schema=data_schema)
|
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."""
|
"""Update config entry options."""
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title="Netatmo Public Weather", data=self.options
|
title="Netatmo Public Weather", data=self.options
|
||||||
|
@ -73,6 +73,13 @@ ATTR_SCHEDULE_NAME = "schedule_name"
|
|||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
||||||
MIN_TIME_BETWEEN_EVENT_UPDATES = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_EVENT_UPDATES = timedelta(seconds=5)
|
||||||
|
|
||||||
SERVICE_SETSCHEDULE = "set_schedule"
|
SERVICE_SET_SCHEDULE = "set_schedule"
|
||||||
SERVICE_SETPERSONSHOME = "set_persons_home"
|
SERVICE_SET_PERSONS_HOME = "set_persons_home"
|
||||||
SERVICE_SETPERSONAWAY = "set_person_away"
|
SERVICE_SET_PERSON_AWAY = "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"
|
||||||
|
@ -11,6 +11,7 @@ import pyatmo
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
|
||||||
from .const import AUTH, DOMAIN, MANUFACTURER
|
from .const import AUTH, DOMAIN, MANUFACTURER
|
||||||
@ -69,7 +70,9 @@ class NetatmoDataHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.listeners.append(
|
self.listeners.append(
|
||||||
self.hass.bus.async_listen("netatmo_event", self.handle_event)
|
async_dispatcher_connect(
|
||||||
|
self.hass, f"signal-{DOMAIN}-webhook-None", self.handle_event,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_update(self, event_time):
|
async def async_update(self, event_time):
|
||||||
@ -99,11 +102,11 @@ class NetatmoDataHandler:
|
|||||||
|
|
||||||
async def handle_event(self, event):
|
async def handle_event(self, event):
|
||||||
"""Handle webhook events."""
|
"""Handle webhook events."""
|
||||||
if event.data["data"]["push_type"] == "webhook_activation":
|
if event["data"]["push_type"] == "webhook_activation":
|
||||||
_LOGGER.info("%s webhook successfully registered", MANUFACTURER)
|
_LOGGER.info("%s webhook successfully registered", MANUFACTURER)
|
||||||
self._webhook = True
|
self._webhook = True
|
||||||
|
|
||||||
elif event.data["data"]["push_type"] == "NACamera-connection":
|
elif event["data"]["push_type"] == "NACamera-connection":
|
||||||
_LOGGER.debug("%s camera reconnected", MANUFACTURER)
|
_LOGGER.debug("%s camera reconnected", MANUFACTURER)
|
||||||
self._data_classes[CAMERA_DATA_CLASS_NAME][NEXT_SCAN] = time()
|
self._data_classes[CAMERA_DATA_CLASS_NAME][NEXT_SCAN] = time()
|
||||||
|
|
||||||
@ -126,27 +129,27 @@ class NetatmoDataHandler:
|
|||||||
self, data_class_name, data_class_entry, update_callback, **kwargs
|
self, data_class_name, data_class_entry, update_callback, **kwargs
|
||||||
):
|
):
|
||||||
"""Register data class."""
|
"""Register data class."""
|
||||||
if data_class_entry not in self._data_classes:
|
if data_class_entry in self._data_classes:
|
||||||
self._data_classes[data_class_entry] = {
|
|
||||||
"class": DATA_CLASSES[data_class_name],
|
|
||||||
"name": data_class_entry,
|
|
||||||
"interval": DEFAULT_INTERVALS[data_class_name],
|
|
||||||
NEXT_SCAN: time() + DEFAULT_INTERVALS[data_class_name],
|
|
||||||
"kwargs": kwargs,
|
|
||||||
"subscriptions": [update_callback],
|
|
||||||
}
|
|
||||||
|
|
||||||
await self.async_fetch_data(
|
|
||||||
DATA_CLASSES[data_class_name], data_class_entry, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
self._queue.append(self._data_classes[data_class_entry])
|
|
||||||
_LOGGER.debug("Data class %s added", data_class_entry)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._data_classes[data_class_entry]["subscriptions"].append(
|
self._data_classes[data_class_entry]["subscriptions"].append(
|
||||||
update_callback
|
update_callback
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._data_classes[data_class_entry] = {
|
||||||
|
"class": DATA_CLASSES[data_class_name],
|
||||||
|
"name": data_class_entry,
|
||||||
|
"interval": DEFAULT_INTERVALS[data_class_name],
|
||||||
|
NEXT_SCAN: time() + DEFAULT_INTERVALS[data_class_name],
|
||||||
|
"kwargs": kwargs,
|
||||||
|
"subscriptions": [update_callback],
|
||||||
|
}
|
||||||
|
|
||||||
|
await self.async_fetch_data(
|
||||||
|
DATA_CLASSES[data_class_name], data_class_entry, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
self._queue.append(self._data_classes[data_class_entry])
|
||||||
|
_LOGGER.debug("Data class %s added", data_class_entry)
|
||||||
|
|
||||||
async def unregister_data_class(self, data_class_entry, update_callback):
|
async def unregister_data_class(self, data_class_entry, update_callback):
|
||||||
"""Unregister data class."""
|
"""Unregister data class."""
|
||||||
|
@ -6,8 +6,15 @@ import pyatmo
|
|||||||
from homeassistant.components.light import LightEntity
|
from homeassistant.components.light import LightEntity
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from .const import DATA_HANDLER, DOMAIN, MANUFACTURER, SIGNAL_NAME
|
from .const import (
|
||||||
|
DATA_HANDLER,
|
||||||
|
DOMAIN,
|
||||||
|
EVENT_TYPE_LIGHT_MODE,
|
||||||
|
MANUFACTURER,
|
||||||
|
SIGNAL_NAME,
|
||||||
|
)
|
||||||
from .data_handler import CAMERA_DATA_CLASS_NAME, NetatmoDataHandler
|
from .data_handler import CAMERA_DATA_CLASS_NAME, NetatmoDataHandler
|
||||||
from .netatmo_entity_base import NetatmoBase
|
from .netatmo_entity_base import NetatmoBase
|
||||||
|
|
||||||
@ -31,42 +38,36 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
)
|
)
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
|
all_cameras = []
|
||||||
|
|
||||||
|
if CAMERA_DATA_CLASS_NAME not in data_handler.data:
|
||||||
|
raise PlatformNotReady
|
||||||
|
|
||||||
try:
|
try:
|
||||||
all_cameras = []
|
|
||||||
for home in data_handler.data[CAMERA_DATA_CLASS_NAME].cameras.values():
|
for home in data_handler.data[CAMERA_DATA_CLASS_NAME].cameras.values():
|
||||||
for camera in home.values():
|
for camera in home.values():
|
||||||
all_cameras.append(camera)
|
all_cameras.append(camera)
|
||||||
|
|
||||||
for camera in all_cameras:
|
|
||||||
if camera["type"] == "NOC":
|
|
||||||
if not data_handler.webhook:
|
|
||||||
raise PlatformNotReady
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Adding camera light %s %s", camera["id"], camera["name"]
|
|
||||||
)
|
|
||||||
entities.append(
|
|
||||||
NetatmoLight(
|
|
||||||
data_handler,
|
|
||||||
camera["id"],
|
|
||||||
camera["type"],
|
|
||||||
camera["home_id"],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
except pyatmo.NoDevice:
|
except pyatmo.NoDevice:
|
||||||
_LOGGER.debug("No cameras found")
|
_LOGGER.debug("No cameras found")
|
||||||
|
|
||||||
|
for camera in all_cameras:
|
||||||
|
if camera["type"] == "NOC":
|
||||||
|
if not data_handler.webhook:
|
||||||
|
raise PlatformNotReady
|
||||||
|
|
||||||
|
_LOGGER.debug("Adding camera light %s %s", camera["id"], camera["name"])
|
||||||
|
entities.append(
|
||||||
|
NetatmoLight(
|
||||||
|
data_handler, camera["id"], camera["type"], camera["home_id"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return entities
|
return entities
|
||||||
|
|
||||||
async_add_entities(await get_entities(), True)
|
async_add_entities(await get_entities(), True)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
|
||||||
"""Set up the Netatmo camera platform."""
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class NetatmoLight(NetatmoBase, LightEntity):
|
class NetatmoLight(NetatmoBase, LightEntity):
|
||||||
"""Representation of a Netatmo Presence camera light."""
|
"""Representation of a Netatmo Presence camera light."""
|
||||||
|
|
||||||
@ -97,15 +98,17 @@ class NetatmoLight(NetatmoBase, LightEntity):
|
|||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
self._listeners.append(
|
self._listeners.append(
|
||||||
self.hass.bus.async_listen("netatmo_event", self.handle_event)
|
async_dispatcher_connect(
|
||||||
|
self.hass,
|
||||||
|
f"signal-{DOMAIN}-webhook-{EVENT_TYPE_LIGHT_MODE}",
|
||||||
|
self.handle_event,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def handle_event(self, event):
|
@callback
|
||||||
|
def handle_event(self, event):
|
||||||
"""Handle webhook events."""
|
"""Handle webhook events."""
|
||||||
data = event.data["data"]
|
data = event["data"]
|
||||||
|
|
||||||
if not data.get("event_type"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not data.get("camera_id"):
|
if not data.get("camera_id"):
|
||||||
return
|
return
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_EVENT_TYPE,
|
ATTR_EVENT_TYPE,
|
||||||
@ -36,10 +37,9 @@ async def handle_webhook(hass, webhook_id, request):
|
|||||||
|
|
||||||
event_type = data.get(ATTR_EVENT_TYPE)
|
event_type = data.get(ATTR_EVENT_TYPE)
|
||||||
|
|
||||||
if event_type in ["outdoor", "therm_mode"]:
|
if event_type in EVENT_TYPE_MAP:
|
||||||
hass.bus.async_fire(
|
async_send_event(hass, event_type, data)
|
||||||
event_type=NETATMO_EVENT, event_data={"type": event_type, "data": data}
|
|
||||||
)
|
|
||||||
for event_data in data.get(EVENT_TYPE_MAP[event_type], []):
|
for event_data in data.get(EVENT_TYPE_MAP[event_type], []):
|
||||||
async_evaluate_event(hass, event_data)
|
async_evaluate_event(hass, event_data)
|
||||||
|
|
||||||
@ -61,13 +61,22 @@ def async_evaluate_event(hass, event_data):
|
|||||||
)
|
)
|
||||||
person_event_data[ATTR_IS_KNOWN] = person.get(ATTR_IS_KNOWN)
|
person_event_data[ATTR_IS_KNOWN] = person.get(ATTR_IS_KNOWN)
|
||||||
person_event_data[ATTR_FACE_URL] = person.get(ATTR_FACE_URL)
|
person_event_data[ATTR_FACE_URL] = person.get(ATTR_FACE_URL)
|
||||||
hass.bus.async_fire(
|
|
||||||
event_type=NETATMO_EVENT,
|
async_send_event(hass, event_type, person_event_data)
|
||||||
event_data={"type": event_type, "data": person_event_data},
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("%s: %s", event_type, event_data)
|
_LOGGER.debug("%s: %s", event_type, event_data)
|
||||||
hass.bus.async_fire(
|
async_send_event(hass, event_type, event_data)
|
||||||
event_type=NETATMO_EVENT,
|
|
||||||
event_data={"type": event_type, "data": event_data},
|
|
||||||
)
|
@callback
|
||||||
|
def async_send_event(hass, event_type, data):
|
||||||
|
"""Send events."""
|
||||||
|
hass.bus.async_fire(
|
||||||
|
event_type=NETATMO_EVENT, event_data={"type": event_type, "data": data}
|
||||||
|
)
|
||||||
|
async_dispatcher_send(
|
||||||
|
hass,
|
||||||
|
f"signal-{DOMAIN}-webhook-{event_type}",
|
||||||
|
{"type": event_type, "data": data},
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user