1 """Home Assistant wrapper for a pyWeMo device."""
3 from __future__
import annotations
6 from dataclasses
import dataclass, fields
7 from datetime
import timedelta
8 from functools
import partial
10 from typing
import TYPE_CHECKING, Literal
12 from pywemo
import Insight, LongPressMixin, WeMoDevice
13 from pywemo.exceptions
import ActionException, PyWeMoException
14 from pywemo.subscribe
import EVENT_TYPE_LONG_PRESS, SubscriptionRegistry
18 ATTR_CONFIGURATION_URL,
31 from .const
import DOMAIN, WEMO_SUBSCRIPTION_EVENT
32 from .models
import async_wemo_data
34 _LOGGER = logging.getLogger(__name__)
37 type ErrorStringKey = Literal[
"long_press_requires_subscription"]
39 type OptionsFieldKey = Literal[
"enable_subscription",
"enable_long_press"]
43 """Error validating options."""
46 self, field_key: OptionsFieldKey, error_key: ErrorStringKey, message: str
48 """Store field and error_key so the exception handler can used them.
50 The field_key and error_key strings must be the same as in strings.json.
53 field_key: Name of the options.step.init.data key that corresponds to this error.
54 field_key must also match one of the field names inside the Options class.
55 error_key: Name of the options.error key that corresponds to this error.
56 message: Message for the Exception class.
64 @dataclass(frozen=True)
66 """Configuration options for the DeviceCoordinator class.
68 Note: The field names must match the keys (OptionsFieldKey)
69 from options.step.init.data in strings.json.
73 enable_subscription: bool =
True
76 enable_long_press: bool =
True
79 """Validate parameters."""
80 if not self.enable_subscription
and self.enable_long_press:
82 "enable_subscription",
83 "long_press_requires_subscription",
84 "Local push update subscriptions must be enabled to use long-press events",
89 """Home Assistant wrapper for a pyWeMo device."""
91 options: Options |
None =
None
93 def __init__(self, hass: HomeAssistant, wemo: WeMoDevice) ->
None:
94 """Initialize DeviceCoordinator."""
103 self.
device_iddevice_id: str |
None =
None
110 """Set up the device coordinator."""
114 self, _device: WeMoDevice, event_type: str, params: str
116 """Receives push notifications from WeMo devices."""
117 _LOGGER.debug(
"Subscription event (%s) for %s", event_type, self.
wemowemo.name)
118 if event_type == EVENT_TYPE_LONG_PRESS:
120 WEMO_SUBSCRIPTION_EVENT,
123 CONF_NAME: self.
wemowemo.name,
124 CONF_TYPE: event_type,
126 CONF_UNIQUE_ID: self.
wemowemo.serial_number,
130 updated = self.
wemowemo.subscription_update(event_type, params)
131 self.
hasshasshass.loop.call_soon_threadsafe(
133 self.
hasshasshass.async_create_background_task,
135 f
"{self.name} subscription_callback",
141 """Unregister push subscriptions and remove from coordinators dict."""
147 assert self.
device_iddevice_id
is not None
150 if self.
optionsoptions.enable_subscription:
158 """Turn on/off push updates from the device."""
160 if enable_subscription:
162 await self.
hasshasshass.async_add_executor_job(registry.register, self.
wemowemo)
163 elif self.
optionsoptions
is not None:
164 await self.
hasshasshass.async_add_executor_job(registry.unregister, self.
wemowemo)
167 """Turn on/off long-press events from the device."""
171 if enable_long_press:
172 await self.
hasshasshass.async_add_executor_job(
173 self.
wemowemo.ensure_long_press_virtual_device
175 elif self.
optionsoptions
is not None:
176 await self.
hasshasshass.async_add_executor_job(
177 self.
wemowemo.remove_long_press_virtual_device
179 except PyWeMoException:
181 "Failed to enable long press support for device: %s", self.
wemowemo.name
186 self, hass: HomeAssistant, config_entry: ConfigEntry
188 """Update the configuration options for the device."""
189 options =
Options(**config_entry.options)
191 "async_set_options old(%s) new(%s)", repr(self.
optionsoptions), repr(options)
193 for field
in fields(options):
194 new_value = getattr(options, field.name)
195 if self.
optionsoptions
is None or getattr(self.
optionsoptions, field.name) != new_value:
197 await getattr(self, f
"_async_set_{field.name}")(new_value)
201 """Update the state by the Wemo device."""
207 except UpdateFailed
as err:
210 _LOGGER.exception(
"Subscription callback failed")
212 except Exception
as err:
215 _LOGGER.exception(
"Unexpected error fetching %s data", self.
namename)
221 """Return True if polling is needed to update the state for the device.
223 The alternative, when this returns False, is to rely on the subscription
224 "push updates" to update the device state in Home Assistant.
238 """Update WeMo state."""
253 """Try updating within an async lock."""
256 await self.
hasshasshass.async_add_executor_job(
257 self.
wemowemo.get_state, force_update
259 except ActionException
as err:
264 """Create device information. Modify if special device."""
266 if wemo.model_name.lower() ==
"dli emulated belkin socket":
267 _dev_info[ATTR_CONFIGURATION_URL] = f
"http://{wemo.host}"
268 _dev_info[ATTR_IDENTIFIERS] = {(DOMAIN, wemo.serial_number[:-1])}
274 connections={(CONNECTION_UPNP, wemo.udn)},
275 identifiers={(DOMAIN, wemo.serial_number)},
276 manufacturer=
"Belkin",
277 model=wemo.model_name,
280 sw_version=wemo.firmware_version,
285 hass: HomeAssistant, config_entry: ConfigEntry, wemo: WeMoDevice
286 ) -> DeviceCoordinator:
287 """Register a device with home assistant and enable pywemo event callbacks."""
289 await device.async_refresh()
290 if not device.last_update_success
and device.last_exception:
291 raise device.last_exception
292 device_registry = dr.async_get(hass)
293 entry = device_registry.async_get_or_create(
296 device.async_setup(device_id=entry.id)
299 config_entry.async_on_unload(
300 config_entry.add_update_listener(device.async_set_options)
302 await device.async_set_options(hass, config_entry)
309 """Return DeviceCoordinator for device_id."""
None async_setup(self, str device_id)
None _async_set_enable_long_press(self, bool enable_long_press)
None _async_set_enable_subscription(self, bool enable_subscription)
None _async_locked_update(self, bool force_update)
None __init__(self, HomeAssistant hass, WeMoDevice wemo)
None async_shutdown(self)
None _async_update_data(self)
None async_set_options(self, HomeAssistant hass, ConfigEntry config_entry)
None subscription_callback(self, WeMoDevice _device, str event_type, str params)
None _async_subscription_callback(self, bool updated)
None __init__(self, OptionsFieldKey field_key, ErrorStringKey error_key, str message)
None async_set_updated_data(self, _DataT data)
str|float get_state(dict[str, float] data, str key)
DeviceInfo _device_info(WeMoDevice wemo)
dict[str, DeviceCoordinator] _async_coordinators(HomeAssistant hass)
DeviceCoordinator async_get_coordinator(HomeAssistant hass, str device_id)
DeviceCoordinator async_register_device(HomeAssistant hass, ConfigEntry config_entry, WeMoDevice wemo)
SubscriptionRegistry _async_registry(HomeAssistant hass)
DeviceInfo _create_device_info(WeMoDevice wemo)
WemoData async_wemo_data(HomeAssistant hass)