mirror of
https://github.com/home-assistant/core.git
synced 2026-01-03 22:35:32 +01:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c783dc1b4 | ||
|
|
650f3413a4 | ||
|
|
176c2f3978 | ||
|
|
9d617d446e | ||
|
|
ea5f621351 | ||
|
|
b49aa9485c | ||
|
|
22f16759e5 | ||
|
|
8895c65dd4 | ||
|
|
dfed834617 | ||
|
|
7d0f5fa31a | ||
|
|
b65c92ce91 | ||
|
|
7c084975b7 | ||
|
|
943cc243b5 | ||
|
|
ba48fd6c51 | ||
|
|
2f1d30fa85 | ||
|
|
7a96f8a4eb | ||
|
|
df6ee2e51f | ||
|
|
6bacda3cee | ||
|
|
d4b68454a3 | ||
|
|
36b099592e | ||
|
|
d156e95f8a | ||
|
|
f3c7b77afa | ||
|
|
45dbd49852 | ||
|
|
e4f88a02f7 | ||
|
|
32652fde48 | ||
|
|
0dc926a131 | ||
|
|
43486917bf | ||
|
|
d967ecc8c6 | ||
|
|
a7bdd2aafe | ||
|
|
e6cc35cff2 | ||
|
|
b7d0a21a8b | ||
|
|
beaaa1478c | ||
|
|
74d8569aab | ||
|
|
dc0d27ec18 | ||
|
|
64707d89dd |
@@ -362,6 +362,7 @@ homeassistant/components/rmvtransport/* @cgtobi
|
||||
homeassistant/components/roku/* @ctalkington
|
||||
homeassistant/components/roomba/* @pschmitt @cyr-ius @shenxn
|
||||
homeassistant/components/roon/* @pavoni
|
||||
homeassistant/components/rpi_gpio_pwm/* @soldag
|
||||
homeassistant/components/rpi_power/* @shenxn @swetoast
|
||||
homeassistant/components/ruckus_unleashed/* @gabe565
|
||||
homeassistant/components/safe_mode/* @home-assistant/core
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"name": "AirVisual",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airvisual",
|
||||
"requirements": ["pyairvisual==5.0.3"],
|
||||
"requirements": ["pyairvisual==5.0.4"],
|
||||
"codeowners": ["@bachya"]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"requirements": ["bimmer_connected==0.7.8"],
|
||||
"requirements": ["bimmer_connected==0.7.12"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@gerard33", "@rikroe"]
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
await coordinator.async_refresh()
|
||||
|
||||
if not coordinator.last_update_success:
|
||||
coordinator.shutdown()
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
@@ -431,7 +431,7 @@ class EvoBroker:
|
||||
return
|
||||
|
||||
if refresh:
|
||||
self.hass.helpers.event.async_call_later(1, self.async_update())
|
||||
self.hass.helpers.event.async_call_later(1, self.async_update)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"domain": "google_translate",
|
||||
"name": "Google Translate Text-to-Speech",
|
||||
"documentation": "https://www.home-assistant.io/integrations/google_translate",
|
||||
"requirements": ["gTTS-token==1.1.3"],
|
||||
"requirements": ["gTTS-token==1.1.4"],
|
||||
"codeowners": []
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"name": "Gree Climate",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/gree",
|
||||
"requirements": ["greeclimate==0.9.2"],
|
||||
"requirements": ["greeclimate==0.9.5"],
|
||||
"codeowners": ["@cmroche"]
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Netatmo",
|
||||
"documentation": "https://www.home-assistant.io/integrations/netatmo",
|
||||
"requirements": [
|
||||
"pyatmo==4.1.0"
|
||||
"pyatmo==4.2.0"
|
||||
],
|
||||
"after_dependencies": [
|
||||
"cloud",
|
||||
|
||||
@@ -45,10 +45,10 @@ class NetatmoBase(Entity):
|
||||
data_class["name"],
|
||||
signal_name,
|
||||
self.async_update_callback,
|
||||
LAT_NE=data_class["LAT_NE"],
|
||||
LON_NE=data_class["LON_NE"],
|
||||
LAT_SW=data_class["LAT_SW"],
|
||||
LON_SW=data_class["LON_SW"],
|
||||
lat_ne=data_class["lat_ne"],
|
||||
lon_ne=data_class["lon_ne"],
|
||||
lat_sw=data_class["lat_sw"],
|
||||
lon_sw=data_class["lon_sw"],
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
@@ -202,10 +202,10 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
||||
PUBLICDATA_DATA_CLASS_NAME,
|
||||
signal_name,
|
||||
None,
|
||||
LAT_NE=area.lat_ne,
|
||||
LON_NE=area.lon_ne,
|
||||
LAT_SW=area.lat_sw,
|
||||
LON_SW=area.lon_sw,
|
||||
lat_ne=area.lat_ne,
|
||||
lon_ne=area.lon_ne,
|
||||
lat_sw=area.lat_sw,
|
||||
lon_sw=area.lon_sw,
|
||||
)
|
||||
for sensor_type in SUPPORTED_PUBLIC_SENSOR_TYPES:
|
||||
new_entities.append(
|
||||
@@ -473,10 +473,10 @@ class NetatmoPublicSensor(NetatmoBase):
|
||||
self._data_classes.append(
|
||||
{
|
||||
"name": PUBLICDATA_DATA_CLASS_NAME,
|
||||
"LAT_NE": area.lat_ne,
|
||||
"LON_NE": area.lon_ne,
|
||||
"LAT_SW": area.lat_sw,
|
||||
"LON_SW": area.lon_sw,
|
||||
"lat_ne": area.lat_ne,
|
||||
"lon_ne": area.lon_ne,
|
||||
"lat_sw": area.lat_sw,
|
||||
"lon_sw": area.lon_sw,
|
||||
"area_name": area.area_name,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
}
|
||||
@@ -563,10 +563,10 @@ class NetatmoPublicSensor(NetatmoBase):
|
||||
self._data_classes = [
|
||||
{
|
||||
"name": PUBLICDATA_DATA_CLASS_NAME,
|
||||
"LAT_NE": area.lat_ne,
|
||||
"LON_NE": area.lon_ne,
|
||||
"LAT_SW": area.lat_sw,
|
||||
"LON_SW": area.lon_sw,
|
||||
"lat_ne": area.lat_ne,
|
||||
"lon_ne": area.lon_ne,
|
||||
"lat_sw": area.lat_sw,
|
||||
"lon_sw": area.lon_sw,
|
||||
"area_name": area.area_name,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
}
|
||||
@@ -577,10 +577,10 @@ class NetatmoPublicSensor(NetatmoBase):
|
||||
PUBLICDATA_DATA_CLASS_NAME,
|
||||
self._signal_name,
|
||||
self.async_update_callback,
|
||||
LAT_NE=area.lat_ne,
|
||||
LON_NE=area.lon_ne,
|
||||
LAT_SW=area.lat_sw,
|
||||
LON_SW=area.lon_sw,
|
||||
lat_ne=area.lat_ne,
|
||||
lon_ne=area.lon_ne,
|
||||
lat_sw=area.lat_sw,
|
||||
lon_sw=area.lon_sw,
|
||||
)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -289,6 +289,8 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
async def async_step_plex_website_auth(self):
|
||||
"""Begin external auth flow on Plex website."""
|
||||
self.hass.http.register_view(PlexAuthorizationCallbackView)
|
||||
hass_url = get_url(self.hass)
|
||||
headers = {"Origin": hass_url}
|
||||
payload = {
|
||||
"X-Plex-Device-Name": X_PLEX_DEVICE_NAME,
|
||||
"X-Plex-Version": X_PLEX_VERSION,
|
||||
@@ -298,9 +300,9 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"X-Plex-Model": "Plex OAuth",
|
||||
}
|
||||
session = async_get_clientsession(self.hass)
|
||||
self.plexauth = PlexAuth(payload, session)
|
||||
self.plexauth = PlexAuth(payload, session, headers)
|
||||
await self.plexauth.initiate_auth()
|
||||
forward_url = f"{get_url(self.hass)}{AUTH_CALLBACK_PATH}?flow_id={self.flow_id}"
|
||||
forward_url = f"{hass_url}{AUTH_CALLBACK_PATH}?flow_id={self.flow_id}"
|
||||
auth_url = self.plexauth.auth_url(forward_url)
|
||||
return self.async_external_step(step_id="obtain_token", url=auth_url)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/plex",
|
||||
"requirements": [
|
||||
"plexapi==4.1.1",
|
||||
"plexauth==0.0.5",
|
||||
"plexwebsocket==0.0.12"
|
||||
"plexapi==4.1.1",
|
||||
"plexauth==0.0.6",
|
||||
"plexwebsocket==0.0.12"
|
||||
],
|
||||
"dependencies": ["http"],
|
||||
"after_dependencies": ["sonos"],
|
||||
|
||||
@@ -84,7 +84,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
if resource_template is not None:
|
||||
resource_template.hass = hass
|
||||
resource = resource_template.render(parse_result=False)
|
||||
resource = resource_template.async_render(parse_result=False)
|
||||
|
||||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
@@ -189,6 +189,6 @@ class RestBinarySensor(BinarySensorEntity):
|
||||
async def async_update(self):
|
||||
"""Get the latest data from REST API and updates the state."""
|
||||
if self._resource_template is not None:
|
||||
self.rest.set_url(self._resource_template.render(parse_result=False))
|
||||
self.rest.set_url(self._resource_template.async_render(parse_result=False))
|
||||
|
||||
await self.rest.async_update()
|
||||
|
||||
@@ -103,7 +103,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
if resource_template is not None:
|
||||
resource_template.hass = hass
|
||||
resource = resource_template.render(parse_result=False)
|
||||
resource = resource_template.async_render(parse_result=False)
|
||||
|
||||
if username and password:
|
||||
if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION:
|
||||
@@ -202,7 +202,7 @@ class RestSensor(Entity):
|
||||
async def async_update(self):
|
||||
"""Get the latest data from REST API and update the state."""
|
||||
if self._resource_template is not None:
|
||||
self.rest.set_url(self._resource_template.render(parse_result=False))
|
||||
self.rest.set_url(self._resource_template.async_render(parse_result=False))
|
||||
|
||||
await self.rest.async_update()
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ from .const import (
|
||||
CONF_OFF_DELAY,
|
||||
CONF_REMOVE_DEVICE,
|
||||
CONF_SIGNAL_REPETITIONS,
|
||||
DATA_CLEANUP_CALLBACKS,
|
||||
DATA_LISTENER,
|
||||
DATA_RFXOBJECT,
|
||||
DEVICE_PACKET_TYPE_LIGHTING4,
|
||||
EVENT_RFXTRX_EVENT,
|
||||
SERVICE_SEND,
|
||||
@@ -93,8 +96,6 @@ DATA_TYPES = OrderedDict(
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DATA_RFXOBJECT = "rfxobject"
|
||||
DATA_LISTENER = "ha_stop"
|
||||
|
||||
|
||||
def _bytearray_string(data):
|
||||
@@ -188,6 +189,8 @@ async def async_setup_entry(hass, entry: config_entries.ConfigEntry):
|
||||
"""Set up the RFXtrx component."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS] = []
|
||||
|
||||
await async_setup_internal(hass, entry)
|
||||
|
||||
for domain in DOMAINS:
|
||||
@@ -212,12 +215,17 @@ async def async_unload_entry(hass, entry: config_entries.ConfigEntry):
|
||||
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SEND)
|
||||
|
||||
for cleanup_callback in hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS]:
|
||||
cleanup_callback()
|
||||
|
||||
listener = hass.data[DOMAIN][DATA_LISTENER]
|
||||
listener()
|
||||
|
||||
rfx_object = hass.data[DOMAIN][DATA_RFXOBJECT]
|
||||
await hass.async_add_executor_job(rfx_object.close_connection)
|
||||
|
||||
hass.data.pop(DOMAIN)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -428,6 +436,14 @@ def get_device_id(device, data_bits=None):
|
||||
return (f"{device.packettype:x}", f"{device.subtype:x}", id_string)
|
||||
|
||||
|
||||
def connect_auto_add(hass, entry_data, callback_fun):
|
||||
"""Connect to dispatcher for automatic add."""
|
||||
if entry_data[CONF_AUTOMATIC_ADD]:
|
||||
hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS].append(
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, callback_fun)
|
||||
)
|
||||
|
||||
|
||||
class RfxtrxEntity(RestoreEntity):
|
||||
"""Represents a Rfxtrx device.
|
||||
|
||||
|
||||
@@ -19,11 +19,10 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers import event as evt
|
||||
|
||||
from . import (
|
||||
CONF_AUTOMATIC_ADD,
|
||||
CONF_DATA_BITS,
|
||||
CONF_OFF_DELAY,
|
||||
SIGNAL_EVENT,
|
||||
RfxtrxEntity,
|
||||
connect_auto_add,
|
||||
find_possible_pt2262_device,
|
||||
get_device_id,
|
||||
get_pt2262_cmd,
|
||||
@@ -147,10 +146,7 @@ async def async_setup_entry(
|
||||
async_add_entities([sensor])
|
||||
|
||||
# Subscribe to main RFXtrx events
|
||||
if discovery_info[CONF_AUTOMATIC_ADD]:
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
SIGNAL_EVENT, binary_sensor_update
|
||||
)
|
||||
connect_auto_add(hass, discovery_info, binary_sensor_update)
|
||||
|
||||
|
||||
class RfxtrxBinarySensor(RfxtrxEntity, BinarySensorEntity):
|
||||
|
||||
@@ -360,13 +360,17 @@ class OptionsFlow(config_entries.OptionsFlow):
|
||||
"""Check if device can be replaced with selected device."""
|
||||
device_data = self._get_device_data(entry_id)
|
||||
event_code = device_data[CONF_EVENT_CODE]
|
||||
rfx_obj = get_rfx_object(event_code)
|
||||
if (
|
||||
rfx_obj.device.packettype == self._selected_device_object.device.packettype
|
||||
and rfx_obj.device.subtype == self._selected_device_object.device.subtype
|
||||
and self._selected_device_event_code != event_code
|
||||
):
|
||||
return True
|
||||
|
||||
if event_code is not None:
|
||||
rfx_obj = get_rfx_object(event_code)
|
||||
if (
|
||||
rfx_obj.device.packettype
|
||||
== self._selected_device_object.device.packettype
|
||||
and rfx_obj.device.subtype
|
||||
== self._selected_device_object.device.subtype
|
||||
and self._selected_device_event_code != event_code
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -33,3 +33,7 @@ SERVICE_SEND = "send"
|
||||
DEVICE_PACKET_TYPE_LIGHTING4 = 0x13
|
||||
|
||||
EVENT_RFXTRX_EVENT = "rfxtrx_event"
|
||||
|
||||
DATA_RFXOBJECT = "rfxobject"
|
||||
DATA_LISTENER = "ha_stop"
|
||||
DATA_CLEANUP_CALLBACKS = "cleanup_callbacks"
|
||||
|
||||
@@ -6,12 +6,11 @@ from homeassistant.const import CONF_DEVICES, STATE_OPEN
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
CONF_AUTOMATIC_ADD,
|
||||
CONF_DATA_BITS,
|
||||
CONF_SIGNAL_REPETITIONS,
|
||||
DEFAULT_SIGNAL_REPETITIONS,
|
||||
SIGNAL_EVENT,
|
||||
RfxtrxCommandEntity,
|
||||
connect_auto_add,
|
||||
get_device_id,
|
||||
get_rfx_object,
|
||||
)
|
||||
@@ -81,8 +80,7 @@ async def async_setup_entry(
|
||||
async_add_entities([entity])
|
||||
|
||||
# Subscribe to main RFXtrx events
|
||||
if discovery_info[CONF_AUTOMATIC_ADD]:
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, cover_update)
|
||||
connect_auto_add(hass, discovery_info, cover_update)
|
||||
|
||||
|
||||
class RfxtrxCover(RfxtrxCommandEntity, CoverEntity):
|
||||
|
||||
@@ -12,12 +12,11 @@ from homeassistant.const import CONF_DEVICES, STATE_ON
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
CONF_AUTOMATIC_ADD,
|
||||
CONF_DATA_BITS,
|
||||
CONF_SIGNAL_REPETITIONS,
|
||||
DEFAULT_SIGNAL_REPETITIONS,
|
||||
SIGNAL_EVENT,
|
||||
RfxtrxCommandEntity,
|
||||
connect_auto_add,
|
||||
get_device_id,
|
||||
get_rfx_object,
|
||||
)
|
||||
@@ -95,8 +94,7 @@ async def async_setup_entry(
|
||||
async_add_entities([entity])
|
||||
|
||||
# Subscribe to main RFXtrx events
|
||||
if discovery_info[CONF_AUTOMATIC_ADD]:
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, light_update)
|
||||
connect_auto_add(hass, discovery_info, light_update)
|
||||
|
||||
|
||||
class RfxtrxLight(RfxtrxCommandEntity, LightEntity):
|
||||
|
||||
@@ -20,11 +20,10 @@ from homeassistant.const import (
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
CONF_AUTOMATIC_ADD,
|
||||
CONF_DATA_BITS,
|
||||
DATA_TYPES,
|
||||
SIGNAL_EVENT,
|
||||
RfxtrxEntity,
|
||||
connect_auto_add,
|
||||
get_device_id,
|
||||
get_rfx_object,
|
||||
)
|
||||
@@ -127,8 +126,7 @@ async def async_setup_entry(
|
||||
async_add_entities([entity])
|
||||
|
||||
# Subscribe to main RFXtrx events
|
||||
if discovery_info[CONF_AUTOMATIC_ADD]:
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, sensor_update)
|
||||
connect_auto_add(hass, discovery_info, sensor_update)
|
||||
|
||||
|
||||
class RfxtrxSensor(RfxtrxEntity):
|
||||
|
||||
@@ -8,13 +8,12 @@ from homeassistant.const import CONF_DEVICES, STATE_ON
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
CONF_AUTOMATIC_ADD,
|
||||
CONF_DATA_BITS,
|
||||
CONF_SIGNAL_REPETITIONS,
|
||||
DEFAULT_SIGNAL_REPETITIONS,
|
||||
DOMAIN,
|
||||
SIGNAL_EVENT,
|
||||
RfxtrxCommandEntity,
|
||||
connect_auto_add,
|
||||
get_device_id,
|
||||
get_rfx_object,
|
||||
)
|
||||
@@ -92,8 +91,7 @@ async def async_setup_entry(
|
||||
async_add_entities([entity])
|
||||
|
||||
# Subscribe to main RFXtrx events
|
||||
if discovery_info[CONF_AUTOMATIC_ADD]:
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, switch_update)
|
||||
connect_auto_add(hass, discovery_info, switch_update)
|
||||
|
||||
|
||||
class RfxtrxSwitch(RfxtrxCommandEntity, SwitchEntity):
|
||||
|
||||
@@ -123,9 +123,6 @@ class PwmSimpleLed(LightEntity, RestoreEntity):
|
||||
self._brightness = last_state.attributes.get(
|
||||
"brightness", DEFAULT_BRIGHTNESS
|
||||
)
|
||||
self._led.set(
|
||||
is_on=self._is_on, brightness=_from_hass_brightness(self._brightness)
|
||||
)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
@@ -199,7 +196,6 @@ class PwmRgbLed(PwmSimpleLed):
|
||||
last_state = await self.async_get_last_state()
|
||||
if last_state:
|
||||
self._color = last_state.attributes.get("hs_color", DEFAULT_COLOR)
|
||||
self._led.set(color=_from_hass_color(self._color))
|
||||
|
||||
@property
|
||||
def hs_color(self):
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"domain": "rpi_gpio_pwm",
|
||||
"name": "pigpio Daemon PWM LED",
|
||||
"documentation": "https://www.home-assistant.io/integrations/rpi_gpio_pwm",
|
||||
"requirements": ["pwmled==1.6.6"],
|
||||
"codeowners": []
|
||||
"requirements": ["pwmled==1.6.7"],
|
||||
"codeowners": ["@soldag"]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import asyncio
|
||||
from uuid import UUID
|
||||
|
||||
from simplipy import API
|
||||
from simplipy.entity import EntityTypes
|
||||
from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError
|
||||
from simplipy.websocket import (
|
||||
EVENT_CAMERA_MOTION_DETECTED,
|
||||
@@ -590,6 +591,13 @@ class SimpliSafeEntity(CoordinatorEntity):
|
||||
else:
|
||||
self._serial = system.serial
|
||||
|
||||
try:
|
||||
sensor_type = EntityTypes(
|
||||
simplisafe.initial_event_to_use[system.system_id].get("sensorType")
|
||||
)
|
||||
except ValueError:
|
||||
sensor_type = EntityTypes.unknown
|
||||
|
||||
self._attrs = {
|
||||
ATTR_LAST_EVENT_INFO: simplisafe.initial_event_to_use[system.system_id].get(
|
||||
"info"
|
||||
@@ -597,9 +605,7 @@ class SimpliSafeEntity(CoordinatorEntity):
|
||||
ATTR_LAST_EVENT_SENSOR_NAME: simplisafe.initial_event_to_use[
|
||||
system.system_id
|
||||
].get("sensorName"),
|
||||
ATTR_LAST_EVENT_SENSOR_TYPE: simplisafe.initial_event_to_use[
|
||||
system.system_id
|
||||
].get("sensorType"),
|
||||
ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type.name,
|
||||
ATTR_LAST_EVENT_TIMESTAMP: simplisafe.initial_event_to_use[
|
||||
system.system_id
|
||||
].get("eventTimestamp"),
|
||||
@@ -724,3 +730,23 @@ class SimpliSafeEntity(CoordinatorEntity):
|
||||
@callback
|
||||
def async_update_from_websocket_event(self, event):
|
||||
"""Update the entity with the provided websocket event."""
|
||||
|
||||
|
||||
class SimpliSafeBaseSensor(SimpliSafeEntity):
|
||||
"""Define a SimpliSafe base (binary) sensor."""
|
||||
|
||||
def __init__(self, simplisafe, system, sensor):
|
||||
"""Initialize."""
|
||||
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
|
||||
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
|
||||
self._device_info["model"] = sensor.type.name
|
||||
self._device_info["name"] = sensor.name
|
||||
self._sensor = sensor
|
||||
self._sensor_type_human_name = " ".join(
|
||||
[w.title() for w in self._sensor.type.name.split("_")]
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return f"{self._system.address} {self._name} {self._sensor_type_human_name}"
|
||||
|
||||
@@ -159,7 +159,7 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
||||
try:
|
||||
await self._system.set_off()
|
||||
except SimplipyError as err:
|
||||
LOGGER.error('Error while disarming "%s": %s', self._system.name, err)
|
||||
LOGGER.error('Error while disarming "%s": %s', self._system.system_id, err)
|
||||
return
|
||||
|
||||
self._state = STATE_ALARM_DISARMED
|
||||
@@ -172,7 +172,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
||||
try:
|
||||
await self._system.set_home()
|
||||
except SimplipyError as err:
|
||||
LOGGER.error('Error while arming "%s" (home): %s', self._system.name, err)
|
||||
LOGGER.error(
|
||||
'Error while arming "%s" (home): %s', self._system.system_id, err
|
||||
)
|
||||
return
|
||||
|
||||
self._state = STATE_ALARM_ARMED_HOME
|
||||
@@ -185,7 +187,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
|
||||
try:
|
||||
await self._system.set_away()
|
||||
except SimplipyError as err:
|
||||
LOGGER.error('Error while arming "%s" (away): %s', self._system.name, err)
|
||||
LOGGER.error(
|
||||
'Error while arming "%s" (away): %s', self._system.system_id, err
|
||||
)
|
||||
return
|
||||
|
||||
self._state = STATE_ALARM_ARMING
|
||||
|
||||
@@ -6,42 +6,32 @@ from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_DOOR,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_MOISTURE,
|
||||
DEVICE_CLASS_MOTION,
|
||||
DEVICE_CLASS_SAFETY,
|
||||
DEVICE_CLASS_SMOKE,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import SimpliSafeEntity
|
||||
from . import SimpliSafeBaseSensor
|
||||
from .const import DATA_CLIENT, DOMAIN, LOGGER
|
||||
|
||||
SUPPORTED_BATTERY_SENSOR_TYPES = [
|
||||
EntityTypes.carbon_monoxide,
|
||||
EntityTypes.entry,
|
||||
EntityTypes.leak,
|
||||
EntityTypes.lock,
|
||||
EntityTypes.lock_keypad,
|
||||
EntityTypes.smoke,
|
||||
EntityTypes.temperature,
|
||||
]
|
||||
|
||||
SUPPORTED_SENSOR_TYPES = [
|
||||
EntityTypes.entry,
|
||||
EntityTypes.carbon_monoxide,
|
||||
EntityTypes.smoke,
|
||||
EntityTypes.leak,
|
||||
]
|
||||
|
||||
HA_SENSOR_TYPES = {
|
||||
EntityTypes.entry: DEVICE_CLASS_DOOR,
|
||||
TRIGGERED_SENSOR_TYPES = {
|
||||
EntityTypes.carbon_monoxide: DEVICE_CLASS_GAS,
|
||||
EntityTypes.smoke: DEVICE_CLASS_SMOKE,
|
||||
EntityTypes.entry: DEVICE_CLASS_DOOR,
|
||||
EntityTypes.glass_break: DEVICE_CLASS_SAFETY,
|
||||
EntityTypes.leak: DEVICE_CLASS_MOISTURE,
|
||||
}
|
||||
|
||||
SENSOR_MODELS = {
|
||||
EntityTypes.entry: "Entry Sensor",
|
||||
EntityTypes.carbon_monoxide: "Carbon Monoxide Detector",
|
||||
EntityTypes.smoke: "Smoke Detector",
|
||||
EntityTypes.leak: "Water Sensor",
|
||||
EntityTypes.motion: DEVICE_CLASS_MOTION,
|
||||
EntityTypes.smoke: DEVICE_CLASS_SMOKE,
|
||||
}
|
||||
|
||||
|
||||
@@ -56,37 +46,34 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
||||
continue
|
||||
|
||||
for sensor in system.sensors.values():
|
||||
if sensor.type in SUPPORTED_SENSOR_TYPES:
|
||||
sensors.append(SimpliSafeBinarySensor(simplisafe, system, sensor))
|
||||
if sensor.type in TRIGGERED_SENSOR_TYPES:
|
||||
sensors.append(
|
||||
TriggeredBinarySensor(
|
||||
simplisafe,
|
||||
system,
|
||||
sensor,
|
||||
TRIGGERED_SENSOR_TYPES[sensor.type],
|
||||
)
|
||||
)
|
||||
if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES:
|
||||
sensors.append(SimpliSafeSensorBattery(simplisafe, system, sensor))
|
||||
sensors.append(BatteryBinarySensor(simplisafe, system, sensor))
|
||||
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity):
|
||||
"""Define a SimpliSafe binary sensor entity."""
|
||||
class TriggeredBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity):
|
||||
"""Define a binary sensor related to whether an entity has been triggered."""
|
||||
|
||||
def __init__(self, simplisafe, system, sensor):
|
||||
def __init__(self, simplisafe, system, sensor, device_class):
|
||||
"""Initialize."""
|
||||
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
|
||||
self._system = system
|
||||
self._sensor = sensor
|
||||
super().__init__(simplisafe, system, sensor)
|
||||
self._device_class = device_class
|
||||
self._is_on = False
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return type of sensor."""
|
||||
return HA_SENSOR_TYPES[self._sensor.type]
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device registry information for this entity."""
|
||||
info = super().device_info
|
||||
info["identifiers"] = {(DOMAIN, self._sensor.serial)}
|
||||
info["model"] = SENSOR_MODELS[self._sensor.type]
|
||||
info["name"] = self._sensor.name
|
||||
return info
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
@@ -99,19 +86,14 @@ class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity):
|
||||
self._is_on = self._sensor.triggered
|
||||
|
||||
|
||||
class SimpliSafeSensorBattery(SimpliSafeEntity, BinarySensorEntity):
|
||||
class BatteryBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity):
|
||||
"""Define a SimpliSafe battery binary sensor entity."""
|
||||
|
||||
def __init__(self, simplisafe, system, sensor):
|
||||
"""Initialize."""
|
||||
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
|
||||
self._sensor = sensor
|
||||
super().__init__(simplisafe, system, sensor)
|
||||
self._is_low = False
|
||||
|
||||
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
|
||||
self._device_info["model"] = SENSOR_MODELS[sensor.type]
|
||||
self._device_info["name"] = sensor.name
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return type of sensor."""
|
||||
|
||||
@@ -4,7 +4,7 @@ from simplipy.entity import EntityTypes
|
||||
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import SimpliSafeEntity
|
||||
from . import SimpliSafeBaseSensor
|
||||
from .const import DATA_CLIENT, DOMAIN, LOGGER
|
||||
|
||||
|
||||
@@ -25,19 +25,14 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class SimplisafeFreezeSensor(SimpliSafeEntity):
|
||||
class SimplisafeFreezeSensor(SimpliSafeBaseSensor):
|
||||
"""Define a SimpliSafe freeze sensor entity."""
|
||||
|
||||
def __init__(self, simplisafe, system, sensor):
|
||||
"""Initialize."""
|
||||
super().__init__(simplisafe, system, sensor.name, serial=sensor.serial)
|
||||
self._sensor = sensor
|
||||
super().__init__(simplisafe, system, sensor)
|
||||
self._state = None
|
||||
|
||||
self._device_info["identifiers"] = {(DOMAIN, sensor.serial)}
|
||||
self._device_info["model"] = "Freeze Sensor"
|
||||
self._device_info["name"] = sensor.name
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return type of sensor."""
|
||||
|
||||
@@ -258,10 +258,9 @@ class SynoApi:
|
||||
self._entry.data[CONF_PASSWORD],
|
||||
self._entry.data[CONF_SSL],
|
||||
timeout=self._entry.options.get(CONF_TIMEOUT),
|
||||
device_token=self._entry.data.get("device_token"),
|
||||
)
|
||||
await self._hass.async_add_executor_job(
|
||||
self.dsm.login, self._entry.data.get("device_token")
|
||||
)
|
||||
await self._hass.async_add_executor_job(self.dsm.login)
|
||||
|
||||
self._with_surveillance_station = bool(
|
||||
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
|
||||
|
||||
@@ -128,6 +128,11 @@ class TasmotaLight(
|
||||
white_value = float(attributes["white_value"])
|
||||
percent_white = white_value / TASMOTA_BRIGHTNESS_MAX
|
||||
self._white_value = percent_white * 255
|
||||
if self._white_value == 0:
|
||||
self._color_temp = None
|
||||
self._white_value = None
|
||||
if self._white_value is not None and self._white_value > 0:
|
||||
self._hs = None
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Tasmota (beta)",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/tasmota",
|
||||
"requirements": ["hatasmota==0.0.25"],
|
||||
"requirements": ["hatasmota==0.0.25.1"],
|
||||
"dependencies": ["mqtt"],
|
||||
"mqtt": ["tasmota/discovery/#"],
|
||||
"codeowners": ["@emontnemery"]
|
||||
|
||||
@@ -348,9 +348,11 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle zeroconf discovery."""
|
||||
assert self.hass
|
||||
|
||||
discovery_info[
|
||||
CONF_HOST
|
||||
] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}"
|
||||
# If host already has port, no need to add it again
|
||||
if ":" not in discovery_info[CONF_HOST]:
|
||||
discovery_info[
|
||||
CONF_HOST
|
||||
] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}"
|
||||
|
||||
# Set default name to discovered device name by stripping zeroconf service
|
||||
# (`type`) from `name`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 117
|
||||
PATCH_VERSION = "2"
|
||||
PATCH_VERSION = "6"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER = (3, 7, 1)
|
||||
|
||||
@@ -8,7 +8,7 @@ attrs==19.3.0
|
||||
bcrypt==3.1.7
|
||||
certifi>=2020.6.20
|
||||
ciso8601==2.1.3
|
||||
cryptography==3.2.0
|
||||
cryptography==3.2
|
||||
defusedxml==0.6.0
|
||||
distro==1.5.0
|
||||
emoji==0.5.4
|
||||
|
||||
@@ -12,7 +12,7 @@ httpx==0.16.1
|
||||
importlib-metadata==1.6.0;python_version<'3.8'
|
||||
jinja2>=2.11.2
|
||||
PyJWT==1.7.1
|
||||
cryptography==3.2.0
|
||||
cryptography==3.2
|
||||
pip>=8.0.3
|
||||
python-slugify==4.0.1
|
||||
pytz>=2020.1
|
||||
|
||||
@@ -342,7 +342,7 @@ beautifulsoup4==4.9.1
|
||||
bellows==0.20.3
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.7.8
|
||||
bimmer_connected==0.7.12
|
||||
|
||||
# homeassistant.components.bizkaibus
|
||||
bizkaibus==0.1.1
|
||||
@@ -619,7 +619,7 @@ freesms==0.1.2
|
||||
fritzconnection==1.2.0
|
||||
|
||||
# homeassistant.components.google_translate
|
||||
gTTS-token==1.1.3
|
||||
gTTS-token==1.1.4
|
||||
|
||||
# homeassistant.components.garmin_connect
|
||||
garminconnect==0.1.13
|
||||
@@ -696,7 +696,7 @@ gpiozero==1.5.1
|
||||
gps3==0.33.3
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==0.9.2
|
||||
greeclimate==0.9.5
|
||||
|
||||
# homeassistant.components.greeneye_monitor
|
||||
greeneye_monitor==2.1
|
||||
@@ -732,7 +732,7 @@ hass-nabucasa==0.37.1
|
||||
hass_splunk==0.1.1
|
||||
|
||||
# homeassistant.components.tasmota
|
||||
hatasmota==0.0.25
|
||||
hatasmota==0.0.25.1
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.9.12
|
||||
@@ -1124,7 +1124,7 @@ pizzapi==0.0.3
|
||||
plexapi==4.1.1
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexauth==0.0.5
|
||||
plexauth==0.0.6
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexwebsocket==0.0.12
|
||||
@@ -1182,7 +1182,7 @@ pushbullet.py==0.11.0
|
||||
pushover_complete==1.1.1
|
||||
|
||||
# homeassistant.components.rpi_gpio_pwm
|
||||
pwmled==1.6.6
|
||||
pwmled==1.6.7
|
||||
|
||||
# homeassistant.components.august
|
||||
py-august==0.25.0
|
||||
@@ -1252,7 +1252,7 @@ pyaehw4a1==0.3.9
|
||||
pyaftership==0.1.2
|
||||
|
||||
# homeassistant.components.airvisual
|
||||
pyairvisual==5.0.3
|
||||
pyairvisual==5.0.4
|
||||
|
||||
# homeassistant.components.almond
|
||||
pyalmond==0.0.2
|
||||
@@ -1264,7 +1264,7 @@ pyarlo==0.2.3
|
||||
pyatag==0.3.4.4
|
||||
|
||||
# homeassistant.components.netatmo
|
||||
pyatmo==4.1.0
|
||||
pyatmo==4.2.0
|
||||
|
||||
# homeassistant.components.atome
|
||||
pyatome==0.1.1
|
||||
|
||||
@@ -302,7 +302,7 @@ fnvhash==0.1.0
|
||||
foobot_async==0.3.2
|
||||
|
||||
# homeassistant.components.google_translate
|
||||
gTTS-token==1.1.3
|
||||
gTTS-token==1.1.4
|
||||
|
||||
# homeassistant.components.garmin_connect
|
||||
garminconnect==0.1.13
|
||||
@@ -352,7 +352,7 @@ google-cloud-pubsub==0.39.1
|
||||
google-nest-sdm==0.1.6
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==0.9.2
|
||||
greeclimate==0.9.5
|
||||
|
||||
# homeassistant.components.griddy
|
||||
griddypower==0.1.0
|
||||
@@ -367,7 +367,7 @@ hangups==0.4.11
|
||||
hass-nabucasa==0.37.1
|
||||
|
||||
# homeassistant.components.tasmota
|
||||
hatasmota==0.0.25
|
||||
hatasmota==0.0.25.1
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.9.12
|
||||
@@ -541,7 +541,7 @@ pillow==7.2.0
|
||||
plexapi==4.1.1
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexauth==0.0.5
|
||||
plexauth==0.0.6
|
||||
|
||||
# homeassistant.components.plex
|
||||
plexwebsocket==0.0.12
|
||||
@@ -615,7 +615,7 @@ py_nextbusnext==0.1.4
|
||||
pyaehw4a1==0.3.9
|
||||
|
||||
# homeassistant.components.airvisual
|
||||
pyairvisual==5.0.3
|
||||
pyairvisual==5.0.4
|
||||
|
||||
# homeassistant.components.almond
|
||||
pyalmond==0.0.2
|
||||
@@ -627,7 +627,7 @@ pyarlo==0.2.3
|
||||
pyatag==0.3.4.4
|
||||
|
||||
# homeassistant.components.netatmo
|
||||
pyatmo==4.1.0
|
||||
pyatmo==4.2.0
|
||||
|
||||
# homeassistant.components.blackbird
|
||||
pyblackbird==0.5
|
||||
|
||||
2
setup.py
2
setup.py
@@ -44,7 +44,7 @@ REQUIRES = [
|
||||
"jinja2>=2.11.2",
|
||||
"PyJWT==1.7.1",
|
||||
# PyJWT has loose dependency. We want the latest one.
|
||||
"cryptography==3.2.0",
|
||||
"cryptography==3.2",
|
||||
"pip>=8.0.3",
|
||||
"python-slugify==4.0.1",
|
||||
"pytz>=2020.1",
|
||||
|
||||
@@ -116,7 +116,7 @@ async def test_setup_minimum_resource_template(hass):
|
||||
{
|
||||
"binary_sensor": {
|
||||
"platform": "rest",
|
||||
"resource_template": "http://localhost",
|
||||
"resource_template": "{% set url = 'http://localhost' %}{{ url }}",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -102,7 +102,7 @@ async def test_setup_minimum_resource_template(hass):
|
||||
{
|
||||
"sensor": {
|
||||
"platform": "rest",
|
||||
"resource_template": "http://localhost",
|
||||
"resource_template": "{% set url = 'http://localhost' %}{{ url }}",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -58,6 +58,44 @@ DEFAULT_CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_CONFIG_9_0_0_4 = {
|
||||
"ip": "192.168.15.10",
|
||||
"dn": "Tasmota",
|
||||
"fn": ["Test", "Beer", "Milk", "Four", None],
|
||||
"hn": "tasmota_49A3BC-0956",
|
||||
"if": 0, # iFan
|
||||
"lk": 1, # RGB + white channels linked to a single light
|
||||
"mac": "00000049A3BC",
|
||||
"md": "Sonoff Basic",
|
||||
"ofln": "Offline",
|
||||
"onln": "Online",
|
||||
"state": ["OFF", "ON", "TOGGLE", "HOLD"],
|
||||
"sw": "8.4.0.2",
|
||||
"swn": [None, None, None, None, None],
|
||||
"t": "tasmota_49A3BC",
|
||||
"ft": "%topic%/%prefix%/",
|
||||
"tp": ["cmnd", "stat", "tele"],
|
||||
"rl": [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"swc": [-1, -1, -1, -1, -1, -1, -1, -1],
|
||||
"btn": [0, 0, 0, 0],
|
||||
"so": {
|
||||
"4": 0, # Return MQTT response as RESULT or %COMMAND%
|
||||
"11": 0, # Swap button single and double press functionality
|
||||
"13": 0, # Allow immediate action on single button press
|
||||
"17": 1, # Show Color string as hex or comma-separated
|
||||
"20": 0, # Update of Dimmer/Color/CT without turning power on
|
||||
"30": 0, # Enforce Home Assistant auto-discovery as light
|
||||
"68": 0, # Multi-channel PWM instead of a single light
|
||||
"73": 0, # Enable Buttons decoupling and send multi-press and hold MQTT messages
|
||||
"82": 0, # Reduce the CT range from 153..500 to 200.380
|
||||
"114": 0, # Enable sending switch MQTT messages
|
||||
},
|
||||
"ty": 0, # Tuya MCU
|
||||
"lt_st": 0,
|
||||
"ver": 1,
|
||||
}
|
||||
|
||||
|
||||
async def help_test_availability_when_connection_lost(
|
||||
hass,
|
||||
mqtt_client_mock,
|
||||
|
||||
@@ -6,7 +6,7 @@ from homeassistant.components.tasmota.const import DEFAULT_PREFIX
|
||||
from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED
|
||||
|
||||
from .conftest import setup_tasmota_helper
|
||||
from .test_common import DEFAULT_CONFIG
|
||||
from .test_common import DEFAULT_CONFIG, DEFAULT_CONFIG_9_0_0_4
|
||||
|
||||
from tests.async_mock import patch
|
||||
from tests.common import async_fire_mqtt_message
|
||||
@@ -132,6 +132,29 @@ async def test_device_discover(
|
||||
assert device_entry.sw_version == config["sw"]
|
||||
|
||||
|
||||
async def test_device_discover_deprecated(
|
||||
hass, mqtt_mock, caplog, device_reg, entity_reg, setup_tasmota
|
||||
):
|
||||
"""Test setting up a device with deprecated discovery message."""
|
||||
config = copy.deepcopy(DEFAULT_CONFIG_9_0_0_4)
|
||||
mac = config["mac"]
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
f"{DEFAULT_PREFIX}/{mac}/config",
|
||||
json.dumps(config),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Verify device and registry entries are created
|
||||
device_entry = device_reg.async_get_device(set(), {("mac", mac)})
|
||||
assert device_entry is not None
|
||||
assert device_entry.manufacturer == "Tasmota"
|
||||
assert device_entry.model == config["md"]
|
||||
assert device_entry.name == config["dn"]
|
||||
assert device_entry.sw_version == config["sw"]
|
||||
|
||||
|
||||
async def test_device_update(
|
||||
hass, mqtt_mock, caplog, device_reg, entity_reg, setup_tasmota
|
||||
):
|
||||
|
||||
@@ -432,6 +432,8 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota):
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("white_value") == 127.5
|
||||
# Setting white > 0 should clear the color
|
||||
assert not state.attributes.get("rgb_color")
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","CT":300}'
|
||||
@@ -440,6 +442,15 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota):
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("color_temp") == 300
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":0}'
|
||||
)
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state == STATE_ON
|
||||
# Setting white to 0 should clear the white_value and color_temp
|
||||
assert not state.attributes.get("white_value")
|
||||
assert not state.attributes.get("color_temp")
|
||||
|
||||
async_fire_mqtt_message(
|
||||
hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Scheme":3}'
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_PIN,
|
||||
CONF_PORT,
|
||||
)
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
@@ -777,6 +778,35 @@ async def test_zeroconf_flow_already_configured(
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_zeroconf_flow_with_port_in_host(
|
||||
hass: HomeAssistantType,
|
||||
vizio_connect: pytest.fixture,
|
||||
vizio_bypass_setup: pytest.fixture,
|
||||
vizio_guess_device_type: pytest.fixture,
|
||||
) -> None:
|
||||
"""Test entity is already configured during zeroconf setup when port is in host."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=MOCK_SPEAKER_CONFIG,
|
||||
options={CONF_VOLUME_STEP: VOLUME_STEP},
|
||||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Try rediscovering same device, this time with port already in host
|
||||
discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy()
|
||||
discovery_info[
|
||||
CONF_HOST
|
||||
] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}"
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||
)
|
||||
|
||||
# Flow should abort because device is already setup
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_zeroconf_dupe_fail(
|
||||
hass: HomeAssistantType,
|
||||
vizio_connect: pytest.fixture,
|
||||
|
||||
Reference in New Issue
Block a user