Merge pull request #38964 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2020-08-17 12:43:35 +02:00 committed by GitHub
commit 740209a81d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 291 additions and 38 deletions

View File

@ -52,18 +52,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
coordinates=(lat, lon), precip_type=config.get(CONF_PRECIP_TYPE) coordinates=(lat, lon), precip_type=config.get(CONF_PRECIP_TYPE)
) )
add_devices([ECCamera(radar_object, config.get(CONF_NAME))], True) add_devices(
[ECCamera(radar_object, config.get(CONF_NAME), config[CONF_LOOP])], True
)
class ECCamera(Camera): class ECCamera(Camera):
"""Implementation of an Environment Canada radar camera.""" """Implementation of an Environment Canada radar camera."""
def __init__(self, radar_object, camera_name): def __init__(self, radar_object, camera_name, is_loop):
"""Initialize the camera.""" """Initialize the camera."""
super().__init__() super().__init__()
self.radar_object = radar_object self.radar_object = radar_object
self.camera_name = camera_name self.camera_name = camera_name
self.is_loop = is_loop
self.content_type = "image/gif" self.content_type = "image/gif"
self.image = None self.image = None
self.timestamp = None self.timestamp = None
@ -90,7 +93,7 @@ class ECCamera(Camera):
@Throttle(MIN_TIME_BETWEEN_UPDATES) @Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self): def update(self):
"""Update radar image.""" """Update radar image."""
if CONF_LOOP: if self.is_loop:
self.image = self.radar_object.get_loop() self.image = self.radar_object.get_loop()
else: else:
self.image = self.radar_object.get_latest_frame() self.image = self.radar_object.get_latest_frame()

View File

@ -235,10 +235,10 @@ class FibaroController:
scenes = self._client.scenes.list() scenes = self._client.scenes.list()
self._scene_map = {} self._scene_map = {}
for device in scenes: for device in scenes:
if "visible" in device and not device.visible: if "name" not in device or "id" not in device:
continue continue
device.fibaro_controller = self device.fibaro_controller = self
if device.roomID == 0: if "roomID" not in device or device.roomID == 0:
room_name = "Unknown" room_name = "Unknown"
else: else:
room_name = self._room_map[device.roomID].name room_name = self._room_map[device.roomID].name
@ -250,6 +250,7 @@ class FibaroController:
device.unique_id_str = f"{self.hub_serial}.scene.{device.id}" device.unique_id_str = f"{self.hub_serial}.scene.{device.id}"
self._scene_map[device.id] = device self._scene_map[device.id] = device
self.fibaro_devices["scene"].append(device) self.fibaro_devices["scene"].append(device)
_LOGGER.debug("%s scene -> %s", device.ha_id, device)
def _read_devices(self): def _read_devices(self):
"""Read and process the device list.""" """Read and process the device list."""
@ -259,8 +260,10 @@ class FibaroController:
last_climate_parent = None last_climate_parent = None
for device in devices: for device in devices:
try: try:
if "name" not in device or "id" not in device:
continue
device.fibaro_controller = self device.fibaro_controller = self
if device.roomID == 0: if "roomID" not in device or device.roomID == 0:
room_name = "Unknown" room_name = "Unknown"
else: else:
room_name = self._room_map[device.roomID].name room_name = self._room_map[device.roomID].name

View File

@ -4,6 +4,7 @@ from datetime import timedelta
import logging import logging
from meteofrance.client import MeteoFranceClient from meteofrance.client import MeteoFranceClient
from meteofrance.helpers import is_valid_warning_department
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
@ -131,7 +132,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
_LOGGER.debug( _LOGGER.debug(
"Department corresponding to %s is %s", entry.title, department, "Department corresponding to %s is %s", entry.title, department,
) )
if department: if is_valid_warning_department(department):
if not hass.data[DOMAIN].get(department): if not hass.data[DOMAIN].get(department):
coordinator_alert = DataUpdateCoordinator( coordinator_alert = DataUpdateCoordinator(
hass, hass,
@ -155,7 +156,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
) )
else: else:
_LOGGER.warning( _LOGGER.warning(
"Weather alert not available: The city %s is not in France or Andorre.", "Weather alert not available: The city %s is not in metropolitan France or Andorre.",
entry.title, entry.title,
) )

View File

@ -3,6 +3,12 @@
"name": "Météo-France", "name": "Météo-France",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/meteo_france", "documentation": "https://www.home-assistant.io/integrations/meteo_france",
"requirements": ["meteofrance-api==0.1.0"], "requirements": [
"codeowners": ["@hacf-fr", "@oncleben31", "@Quentame"] "meteofrance-api==0.1.1"
} ],
"codeowners": [
"@hacf-fr",
"@oncleben31",
"@Quentame"
]
}

View File

@ -262,8 +262,11 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
for room in home["rooms"]: for room in home["rooms"]:
if data["event_type"] == "set_point": if data["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"] == STATE_NETATMO_OFF:
self._hvac_mode = HVAC_MODE_OFF self._hvac_mode = HVAC_MODE_OFF
elif room["therm_setpoint_mode"] == STATE_NETATMO_MAX:
self._hvac_mode = HVAC_MODE_HEAT
self._target_temperature = DEFAULT_MAX_TEMP
else: else:
self._target_temperature = room["therm_setpoint_temperature"] self._target_temperature = room["therm_setpoint_temperature"]
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -239,6 +239,7 @@ class OnkyoDevice(MediaPlayerEntity):
self._source_mapping = sources self._source_mapping = sources
self._reverse_mapping = {value: key for key, value in sources.items()} self._reverse_mapping = {value: key for key, value in sources.items()}
self._attributes = {} self._attributes = {}
self._hdmi_out_supported = True
def command(self, command): def command(self, command):
"""Run an eiscp command and catch connection errors.""" """Run an eiscp command and catch connection errors."""
@ -251,6 +252,7 @@ class OnkyoDevice(MediaPlayerEntity):
else: else:
_LOGGER.info("%s is disconnected. Attempting to reconnect", self._name) _LOGGER.info("%s is disconnected. Attempting to reconnect", self._name)
return False return False
_LOGGER.debug("Result for %s: %s", command, result)
return result return result
def update(self): def update(self):
@ -268,7 +270,13 @@ class OnkyoDevice(MediaPlayerEntity):
volume_raw = self.command("volume query") volume_raw = self.command("volume query")
mute_raw = self.command("audio-muting query") mute_raw = self.command("audio-muting query")
current_source_raw = self.command("input-selector query") current_source_raw = self.command("input-selector query")
hdmi_out_raw = self.command("hdmi-output-selector query") # If the following command is sent to a device with only one HDMI out,
# the display shows 'Not Available'.
# We avoid this by checking if HDMI out is supported
if self._hdmi_out_supported:
hdmi_out_raw = self.command("hdmi-output-selector query")
else:
hdmi_out_raw = []
preset_raw = self.command("preset query") preset_raw = self.command("preset query")
if not (volume_raw and mute_raw and current_source_raw): if not (volume_raw and mute_raw and current_source_raw):
return return
@ -298,6 +306,8 @@ class OnkyoDevice(MediaPlayerEntity):
if not hdmi_out_raw: if not hdmi_out_raw:
return return
self._attributes["video_out"] = ",".join(hdmi_out_raw[1]) self._attributes["video_out"] = ",".join(hdmi_out_raw[1])
if hdmi_out_raw[1] == "N/A":
self._hdmi_out_supported = False
@property @property
def name(self): def name(self):

View File

@ -80,24 +80,24 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity):
if self.values.dimming_duration is not None: if self.values.dimming_duration is not None:
self._supported_features |= SUPPORT_TRANSITION self._supported_features |= SUPPORT_TRANSITION
if self.values.color is None or self.values.color_channels is None: if self.values.color is not None:
return self._supported_features |= SUPPORT_COLOR
self._supported_features |= SUPPORT_COLOR if self.values.color_channels is not None:
# Support Color Temp if both white channels
if (self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) and (
self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE
):
self._supported_features |= SUPPORT_COLOR_TEMP
# Support Color Temp if both white channels # Support White value if only a single white channel
if (self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) and ( if ((self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) != 0) ^ (
self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE (self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE) != 0
): ):
self._supported_features |= SUPPORT_COLOR_TEMP self._supported_features |= SUPPORT_WHITE_VALUE
# Support White value if only a single white channel if self.values.color is not None:
if ((self.values.color_channels.value & COLOR_CHANNEL_WARM_WHITE) != 0) ^ ( self._calculate_color_values()
(self.values.color_channels.value & COLOR_CHANNEL_COLD_WHITE) != 0
):
self._supported_features |= SUPPORT_WHITE_VALUE
self._calculate_rgb_values()
@property @property
def brightness(self): def brightness(self):
@ -248,10 +248,8 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity):
self.values.primary.send_value(0) self.values.primary.send_value(0)
def _calculate_rgb_values(self): def _calculate_color_values(self):
# Color Channels """Parse color rgb and color temperature data."""
self._color_channels = self.values.color_channels.data[ATTR_VALUE]
# Color Data String # Color Data String
data = self.values.color.data[ATTR_VALUE] data = self.values.color.data[ATTR_VALUE]
@ -259,6 +257,12 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity):
rgb = [int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)] rgb = [int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)]
self._hs = color_util.color_RGB_to_hs(*rgb) self._hs = color_util.color_RGB_to_hs(*rgb)
if self.values.color_channels is None:
return
# Color Channels
self._color_channels = self.values.color_channels.data[ATTR_VALUE]
# Parse remaining color channels. OpenZWave appends white channels # Parse remaining color channels. OpenZWave appends white channels
# that are present. # that are present.
index = 7 index = 7

View File

@ -534,7 +534,15 @@ class Recorder(threading.Thread):
if self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith( if self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith(
SQLITE_URL_PREFIX SQLITE_URL_PREFIX
): ):
validate_or_move_away_sqlite_database(self.db_url) with self.hass.timeout.freeze(DOMAIN):
#
# Here we run an sqlite3 quick_check. In the majority
# of cases, the quick_check takes under 10 seconds.
#
# On systems with very large databases and
# very slow disk or cpus, this can take a while.
#
validate_or_move_away_sqlite_database(self.db_url)
if self.engine is not None: if self.engine is not None:
self.engine.dispose() self.engine.dispose()

View File

@ -250,7 +250,7 @@ class SamsungTVWSBridge(SamsungTVBridge):
host=self.host, host=self.host,
port=self.port, port=self.port,
token=self.token, token=self.token,
timeout=10, timeout=8,
name=VALUE_CONF_NAME, name=VALUE_CONF_NAME,
) )
self._remote.open() self._remote.open()

View File

@ -1,7 +1,7 @@
"""Constants used by Home Assistant components.""" """Constants used by Home Assistant components."""
MAJOR_VERSION = 0 MAJOR_VERSION = 0
MINOR_VERSION = 114 MINOR_VERSION = 114
PATCH_VERSION = "1" PATCH_VERSION = "2"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 7, 1) REQUIRED_PYTHON_VER = (3, 7, 1)

View File

@ -902,7 +902,7 @@ messagebird==1.2.0
meteoalertapi==0.1.6 meteoalertapi==0.1.6
# homeassistant.components.meteo_france # homeassistant.components.meteo_france
meteofrance-api==0.1.0 meteofrance-api==0.1.1
# homeassistant.components.mfi # homeassistant.components.mfi
mficlient==0.3.0 mficlient==0.3.0

View File

@ -424,7 +424,7 @@ mbddns==0.1.2
mcstatus==2.3.0 mcstatus==2.3.0
# homeassistant.components.meteo_france # homeassistant.components.meteo_france
meteofrance-api==0.1.0 meteofrance-api==0.1.1
# homeassistant.components.mfi # homeassistant.components.mfi
mficlient==0.3.0 mficlient==0.3.0

View File

@ -33,6 +33,12 @@ def light_new_ozw_data_fixture():
return load_fixture("ozw/light_new_ozw_network_dump.csv") return load_fixture("ozw/light_new_ozw_network_dump.csv")
@pytest.fixture(name="light_pure_rgb_dimmer_data", scope="session")
def light_pure_rgb_dimmer_data_fixture():
"""Load light rgb and dimmer MQTT data and return it."""
return load_fixture("ozw/light_pure_rgb_dimmer_dump.csv")
@pytest.fixture(name="light_no_rgb_data", scope="session") @pytest.fixture(name="light_no_rgb_data", scope="session")
def light_no_rgb_data_fixture(): def light_no_rgb_data_fixture():
"""Load light dimmer MQTT data and return it.""" """Load light dimmer MQTT data and return it."""
@ -139,6 +145,17 @@ async def light_rgb_msg_fixture(hass):
return message return message
@pytest.fixture(name="light_pure_rgb_msg")
async def light_pure_rgb_msg_fixture(hass):
"""Return a mock MQTT msg with a pure rgb light actuator message."""
light_json = json.loads(
await hass.async_add_executor_job(load_fixture, "ozw/light_pure_rgb.json")
)
message = MQTTMessage(topic=light_json["topic"], payload=light_json["payload"])
message.encode()
return message
@pytest.fixture(name="switch_msg") @pytest.fixture(name="switch_msg")
async def switch_msg_fixture(hass): async def switch_msg_fixture(hass):
"""Return a mock MQTT msg with a switch actuator message.""" """Return a mock MQTT msg with a switch actuator message."""

View File

@ -350,6 +350,48 @@ async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages):
assert state.attributes["color_temp"] == 153 assert state.attributes["color_temp"] == 153
async def test_pure_rgb_dimmer_light(
hass, light_pure_rgb_dimmer_data, light_msg, light_pure_rgb_msg, sent_messages
):
"""Test light with no color channels command class."""
receive_message = await setup_ozw(hass, fixture=light_pure_rgb_dimmer_data)
# Test loaded
state = hass.states.get("light.kitchen_rgb_strip_level")
assert state is not None
assert state.state == "on"
assert state.attributes["supported_features"] == 17
# Test setting hs_color
new_color = [300, 70]
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": "light.kitchen_rgb_strip_level", "hs_color": new_color},
blocking=True,
)
assert len(sent_messages) == 2
msg = sent_messages[-1]
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
assert msg["payload"] == {"Value": 255, "ValueIDKey": 122257425}
msg = sent_messages[-2]
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
assert msg["payload"] == {"Value": "#ff4cff0000", "ValueIDKey": 122470423}
# Feedback on state
light_pure_rgb_msg.decode()
light_pure_rgb_msg.payload["Value"] = "#ff4cff0000"
light_pure_rgb_msg.encode()
receive_message(light_pure_rgb_msg)
await hass.async_block_till_done()
state = hass.states.get("light.kitchen_rgb_strip_level")
assert state is not None
assert state.state == "on"
assert state.attributes["hs_color"] == (300.0, 70.196)
async def test_no_rgb_light(hass, light_no_rgb_data, light_no_rgb_msg, sent_messages): async def test_no_rgb_light(hass, light_no_rgb_data, light_no_rgb_msg, sent_messages):
"""Test setting up config entry.""" """Test setting up config entry."""
receive_message = await setup_ozw(hass, fixture=light_no_rgb_data) receive_message = await setup_ozw(hass, fixture=light_no_rgb_data)

View File

@ -97,7 +97,7 @@ MOCK_CALLS_ENTRY_WS = {
"host": "fake", "host": "fake",
"name": "HomeAssistant", "name": "HomeAssistant",
"port": 8001, "port": 8001,
"timeout": 10, "timeout": 8,
"token": "abcde", "token": "abcde",
} }

25
tests/fixtures/ozw/light_pure_rgb.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
"topic": "OpenZWave/1/node/7/instance/1/commandclass/51/value/122470423/",
"payload": {
"Label": "Color",
"Value": "#ff00000000",
"Units": "#RRGGBBWW",
"ValueSet": false,
"ValuePolled": false,
"ChangeVerified": false,
"Min": 0,
"Max": 0,
"Type": "String",
"Instance": 1,
"CommandClass": "COMMAND_CLASS_COLOR",
"Index": 0,
"Node": 7,
"Genre": "User",
"Help": "Color (in RGB format)",
"ValueIDKey": 122470423,
"ReadOnly": false,
"WriteOnly": false,
"Event": "valueAdded",
"TimeStamp": 1597142799
}
}

File diff suppressed because one or more lines are too long