Motion blinds support direct wifi blinds (#67372)
* bump motionblinds to 0.6.0 * fix unknown type * fix name for wifi direct blinds * push motionblinds to 0.6.1 * fix RSSI sensor * Do not add singnal sensor twice for direct WiFi blinds * fix device registry * fix typo * missing import * fix styling * fix spelling
This commit is contained in:
parent
5d2a65bb21
commit
03ec77fb62
7 changed files with 109 additions and 45 deletions
|
@ -4,7 +4,7 @@ import logging
|
||||||
from socket import timeout
|
from socket import timeout
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from motionblinds import AsyncMotionMulticast, ParseException
|
from motionblinds import DEVICE_TYPES_WIFI, AsyncMotionMulticast, ParseException
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP
|
||||||
|
@ -23,6 +23,7 @@ from .const import (
|
||||||
KEY_COORDINATOR,
|
KEY_COORDINATOR,
|
||||||
KEY_GATEWAY,
|
KEY_GATEWAY,
|
||||||
KEY_MULTICAST_LISTENER,
|
KEY_MULTICAST_LISTENER,
|
||||||
|
KEY_VERSION,
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
UPDATE_INTERVAL,
|
UPDATE_INTERVAL,
|
||||||
|
@ -147,29 +148,31 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
# Fetch initial data so we have data when entities subscribe
|
# Fetch initial data so we have data when entities subscribe
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
|
||||||
KEY_GATEWAY: motion_gateway,
|
|
||||||
KEY_COORDINATOR: coordinator,
|
|
||||||
}
|
|
||||||
|
|
||||||
if motion_gateway.firmware is not None:
|
if motion_gateway.firmware is not None:
|
||||||
version = f"{motion_gateway.firmware}, protocol: {motion_gateway.protocol}"
|
version = f"{motion_gateway.firmware}, protocol: {motion_gateway.protocol}"
|
||||||
else:
|
else:
|
||||||
version = f"Protocol: {motion_gateway.protocol}"
|
version = f"Protocol: {motion_gateway.protocol}"
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = {
|
||||||
|
KEY_GATEWAY: motion_gateway,
|
||||||
|
KEY_COORDINATOR: coordinator,
|
||||||
|
KEY_VERSION: version,
|
||||||
|
}
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert entry.unique_id is not None
|
assert entry.unique_id is not None
|
||||||
|
|
||||||
device_registry = dr.async_get(hass)
|
if motion_gateway.device_type not in DEVICE_TYPES_WIFI:
|
||||||
device_registry.async_get_or_create(
|
device_registry = dr.async_get(hass)
|
||||||
config_entry_id=entry.entry_id,
|
device_registry.async_get_or_create(
|
||||||
connections={(dr.CONNECTION_NETWORK_MAC, motion_gateway.mac)},
|
config_entry_id=entry.entry_id,
|
||||||
identifiers={(DOMAIN, motion_gateway.mac)},
|
connections={(dr.CONNECTION_NETWORK_MAC, motion_gateway.mac)},
|
||||||
manufacturer=MANUFACTURER,
|
identifiers={(DOMAIN, motion_gateway.mac)},
|
||||||
name=entry.title,
|
manufacturer=MANUFACTURER,
|
||||||
model="Wi-Fi bridge",
|
name=entry.title,
|
||||||
sw_version=version,
|
model="Wi-Fi bridge",
|
||||||
)
|
sw_version=version,
|
||||||
|
)
|
||||||
|
|
||||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ DEFAULT_INTERFACE = "any"
|
||||||
KEY_GATEWAY = "gateway"
|
KEY_GATEWAY = "gateway"
|
||||||
KEY_COORDINATOR = "coordinator"
|
KEY_COORDINATOR = "coordinator"
|
||||||
KEY_MULTICAST_LISTENER = "multicast_listener"
|
KEY_MULTICAST_LISTENER = "multicast_listener"
|
||||||
|
KEY_VERSION = "version"
|
||||||
|
|
||||||
ATTR_WIDTH = "width"
|
ATTR_WIDTH = "width"
|
||||||
ATTR_ABSOLUTE_POSITION = "absolute_position"
|
ATTR_ABSOLUTE_POSITION = "absolute_position"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Support for Motion Blinds using their WLAN API."""
|
"""Support for Motion Blinds using their WLAN API."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from motionblinds import BlindType
|
from motionblinds import DEVICE_TYPES_WIFI, BlindType
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
|
@ -12,7 +12,11 @@ from homeassistant.components.cover import (
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import (
|
||||||
|
config_validation as cv,
|
||||||
|
device_registry as dr,
|
||||||
|
entity_platform,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
@ -24,6 +28,7 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
KEY_COORDINATOR,
|
KEY_COORDINATOR,
|
||||||
KEY_GATEWAY,
|
KEY_GATEWAY,
|
||||||
|
KEY_VERSION,
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
SERVICE_SET_ABSOLUTE_POSITION,
|
SERVICE_SET_ABSOLUTE_POSITION,
|
||||||
)
|
)
|
||||||
|
@ -74,26 +79,40 @@ async def async_setup_entry(
|
||||||
entities = []
|
entities = []
|
||||||
motion_gateway = hass.data[DOMAIN][config_entry.entry_id][KEY_GATEWAY]
|
motion_gateway = hass.data[DOMAIN][config_entry.entry_id][KEY_GATEWAY]
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
|
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
|
||||||
|
sw_version = hass.data[DOMAIN][config_entry.entry_id][KEY_VERSION]
|
||||||
|
|
||||||
for blind in motion_gateway.device_list.values():
|
for blind in motion_gateway.device_list.values():
|
||||||
if blind.type in POSITION_DEVICE_MAP:
|
if blind.type in POSITION_DEVICE_MAP:
|
||||||
entities.append(
|
entities.append(
|
||||||
MotionPositionDevice(
|
MotionPositionDevice(
|
||||||
coordinator, blind, POSITION_DEVICE_MAP[blind.type], config_entry
|
coordinator,
|
||||||
|
blind,
|
||||||
|
POSITION_DEVICE_MAP[blind.type],
|
||||||
|
config_entry,
|
||||||
|
sw_version,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif blind.type in TILT_DEVICE_MAP:
|
elif blind.type in TILT_DEVICE_MAP:
|
||||||
entities.append(
|
entities.append(
|
||||||
MotionTiltDevice(
|
MotionTiltDevice(
|
||||||
coordinator, blind, TILT_DEVICE_MAP[blind.type], config_entry
|
coordinator,
|
||||||
|
blind,
|
||||||
|
TILT_DEVICE_MAP[blind.type],
|
||||||
|
config_entry,
|
||||||
|
sw_version,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif blind.type in TDBU_DEVICE_MAP:
|
elif blind.type in TDBU_DEVICE_MAP:
|
||||||
entities.append(
|
entities.append(
|
||||||
MotionTDBUDevice(
|
MotionTDBUDevice(
|
||||||
coordinator, blind, TDBU_DEVICE_MAP[blind.type], config_entry, "Top"
|
coordinator,
|
||||||
|
blind,
|
||||||
|
TDBU_DEVICE_MAP[blind.type],
|
||||||
|
config_entry,
|
||||||
|
sw_version,
|
||||||
|
"Top",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entities.append(
|
entities.append(
|
||||||
|
@ -102,6 +121,7 @@ async def async_setup_entry(
|
||||||
blind,
|
blind,
|
||||||
TDBU_DEVICE_MAP[blind.type],
|
TDBU_DEVICE_MAP[blind.type],
|
||||||
config_entry,
|
config_entry,
|
||||||
|
sw_version,
|
||||||
"Bottom",
|
"Bottom",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -111,12 +131,25 @@ async def async_setup_entry(
|
||||||
blind,
|
blind,
|
||||||
TDBU_DEVICE_MAP[blind.type],
|
TDBU_DEVICE_MAP[blind.type],
|
||||||
config_entry,
|
config_entry,
|
||||||
|
sw_version,
|
||||||
"Combined",
|
"Combined",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning("Blind type '%s' not yet supported", blind.blind_type)
|
_LOGGER.warning(
|
||||||
|
"Blind type '%s' not yet supported, " "assuming RollerBlind",
|
||||||
|
blind.blind_type,
|
||||||
|
)
|
||||||
|
entities.append(
|
||||||
|
MotionPositionDevice(
|
||||||
|
coordinator,
|
||||||
|
blind,
|
||||||
|
POSITION_DEVICE_MAP[BlindType.RollerBlind],
|
||||||
|
config_entry,
|
||||||
|
sw_version,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -131,22 +164,34 @@ async def async_setup_entry(
|
||||||
class MotionPositionDevice(CoordinatorEntity, CoverEntity):
|
class MotionPositionDevice(CoordinatorEntity, CoverEntity):
|
||||||
"""Representation of a Motion Blind Device."""
|
"""Representation of a Motion Blind Device."""
|
||||||
|
|
||||||
def __init__(self, coordinator, blind, device_class, config_entry):
|
def __init__(self, coordinator, blind, device_class, config_entry, sw_version):
|
||||||
"""Initialize the blind."""
|
"""Initialize the blind."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
self._blind = blind
|
self._blind = blind
|
||||||
self._config_entry = config_entry
|
self._config_entry = config_entry
|
||||||
|
|
||||||
|
if blind.device_type in DEVICE_TYPES_WIFI:
|
||||||
|
via_device = ()
|
||||||
|
connections = {(dr.CONNECTION_NETWORK_MAC, blind.mac)}
|
||||||
|
name = blind.blind_type
|
||||||
|
else:
|
||||||
|
via_device = (DOMAIN, blind._gateway.mac)
|
||||||
|
connections = {}
|
||||||
|
name = f"{blind.blind_type}-{blind.mac[12:]}"
|
||||||
|
sw_version = None
|
||||||
|
|
||||||
self._attr_device_class = device_class
|
self._attr_device_class = device_class
|
||||||
self._attr_name = f"{blind.blind_type}-{blind.mac[12:]}"
|
self._attr_name = name
|
||||||
self._attr_unique_id = blind.mac
|
self._attr_unique_id = blind.mac
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
|
connections=connections,
|
||||||
identifiers={(DOMAIN, blind.mac)},
|
identifiers={(DOMAIN, blind.mac)},
|
||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
model=blind.blind_type,
|
model=blind.blind_type,
|
||||||
name=f"{blind.blind_type}-{blind.mac[12:]}",
|
name=name,
|
||||||
via_device=(DOMAIN, blind._gateway.mac),
|
via_device=via_device,
|
||||||
|
sw_version=sw_version,
|
||||||
hw_version=blind.wireless_name,
|
hw_version=blind.wireless_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -247,9 +292,11 @@ class MotionTiltDevice(MotionPositionDevice):
|
||||||
class MotionTDBUDevice(MotionPositionDevice):
|
class MotionTDBUDevice(MotionPositionDevice):
|
||||||
"""Representation of a Motion Top Down Bottom Up blind Device."""
|
"""Representation of a Motion Top Down Bottom Up blind Device."""
|
||||||
|
|
||||||
def __init__(self, coordinator, blind, device_class, config_entry, motor):
|
def __init__(
|
||||||
|
self, coordinator, blind, device_class, config_entry, sw_version, motor
|
||||||
|
):
|
||||||
"""Initialize the blind."""
|
"""Initialize the blind."""
|
||||||
super().__init__(coordinator, blind, device_class, config_entry)
|
super().__init__(coordinator, blind, device_class, config_entry, sw_version)
|
||||||
self._motor = motor
|
self._motor = motor
|
||||||
self._motor_key = motor[0]
|
self._motor_key = motor[0]
|
||||||
self._attr_name = f"{blind.blind_type}-{motor}-{blind.mac[12:]}"
|
self._attr_name = f"{blind.blind_type}-{motor}-{blind.mac[12:]}"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Motion Blinds",
|
"name": "Motion Blinds",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/motion_blinds",
|
"documentation": "https://www.home-assistant.io/integrations/motion_blinds",
|
||||||
"requirements": ["motionblinds==0.5.13"],
|
"requirements": ["motionblinds==0.6.1"],
|
||||||
"dependencies": ["network"],
|
"dependencies": ["network"],
|
||||||
"codeowners": ["@starkillerOG"],
|
"codeowners": ["@starkillerOG"],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""Support for Motion Blinds sensors."""
|
"""Support for Motion Blinds sensors."""
|
||||||
from motionblinds import BlindType
|
from motionblinds import DEVICE_TYPES_WIFI, BlindType
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -31,13 +31,15 @@ async def async_setup_entry(
|
||||||
if blind.type == BlindType.TopDownBottomUp:
|
if blind.type == BlindType.TopDownBottomUp:
|
||||||
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Bottom"))
|
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Bottom"))
|
||||||
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Top"))
|
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Top"))
|
||||||
elif blind.battery_voltage > 0:
|
elif blind.battery_voltage is not None and blind.battery_voltage > 0:
|
||||||
# Only add battery powered blinds
|
# Only add battery powered blinds
|
||||||
entities.append(MotionBatterySensor(coordinator, blind))
|
entities.append(MotionBatterySensor(coordinator, blind))
|
||||||
|
|
||||||
entities.append(
|
# Do not add signal sensor twice for direct WiFi blinds
|
||||||
MotionSignalStrengthSensor(coordinator, motion_gateway, TYPE_GATEWAY)
|
if motion_gateway.device_type not in DEVICE_TYPES_WIFI:
|
||||||
)
|
entities.append(
|
||||||
|
MotionSignalStrengthSensor(coordinator, motion_gateway, TYPE_GATEWAY)
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -52,9 +54,14 @@ class MotionBatterySensor(CoordinatorEntity, SensorEntity):
|
||||||
"""Initialize the Motion Battery Sensor."""
|
"""Initialize the Motion Battery Sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
if blind.device_type in DEVICE_TYPES_WIFI:
|
||||||
|
name = f"{blind.blind_type}-battery"
|
||||||
|
else:
|
||||||
|
name = f"{blind.blind_type}-battery-{blind.mac[12:]}"
|
||||||
|
|
||||||
self._blind = blind
|
self._blind = blind
|
||||||
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, blind.mac)})
|
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, blind.mac)})
|
||||||
self._attr_name = f"{blind.blind_type}-battery-{blind.mac[12:]}"
|
self._attr_name = name
|
||||||
self._attr_unique_id = f"{blind.mac}-battery"
|
self._attr_unique_id = f"{blind.mac}-battery"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -96,9 +103,14 @@ class MotionTDBUBatterySensor(MotionBatterySensor):
|
||||||
"""Initialize the Motion Battery Sensor."""
|
"""Initialize the Motion Battery Sensor."""
|
||||||
super().__init__(coordinator, blind)
|
super().__init__(coordinator, blind)
|
||||||
|
|
||||||
|
if blind.device_type in DEVICE_TYPES_WIFI:
|
||||||
|
name = f"{blind.blind_type}-{motor}-battery"
|
||||||
|
else:
|
||||||
|
name = f"{blind.blind_type}-{motor}-battery-{blind.mac[12:]}"
|
||||||
|
|
||||||
self._motor = motor
|
self._motor = motor
|
||||||
self._attr_unique_id = f"{blind.mac}-{motor}-battery"
|
self._attr_unique_id = f"{blind.mac}-{motor}-battery"
|
||||||
self._attr_name = f"{blind.blind_type}-{motor}-battery-{blind.mac[12:]}"
|
self._attr_name = name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self):
|
def native_value(self):
|
||||||
|
@ -130,17 +142,18 @@ class MotionSignalStrengthSensor(CoordinatorEntity, SensorEntity):
|
||||||
"""Initialize the Motion Signal Strength Sensor."""
|
"""Initialize the Motion Signal Strength Sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
if device_type == TYPE_GATEWAY:
|
||||||
|
name = "Motion gateway signal strength"
|
||||||
|
elif device.device_type in DEVICE_TYPES_WIFI:
|
||||||
|
name = f"{device.blind_type} signal strength"
|
||||||
|
else:
|
||||||
|
name = f"{device.blind_type} signal strength - {device.mac[12:]}"
|
||||||
|
|
||||||
self._device = device
|
self._device = device
|
||||||
self._device_type = device_type
|
self._device_type = device_type
|
||||||
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device.mac)})
|
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device.mac)})
|
||||||
self._attr_unique_id = f"{device.mac}-RSSI"
|
self._attr_unique_id = f"{device.mac}-RSSI"
|
||||||
|
self._attr_name = name
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the blind signal strength sensor."""
|
|
||||||
if self._device_type == TYPE_GATEWAY:
|
|
||||||
return "Motion gateway signal strength"
|
|
||||||
return f"{self._device.blind_type} signal strength - {self._device.mac[12:]}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
|
|
|
@ -1011,7 +1011,7 @@ mitemp_bt==0.0.5
|
||||||
moehlenhoff-alpha2==1.1.2
|
moehlenhoff-alpha2==1.1.2
|
||||||
|
|
||||||
# homeassistant.components.motion_blinds
|
# homeassistant.components.motion_blinds
|
||||||
motionblinds==0.5.13
|
motionblinds==0.6.1
|
||||||
|
|
||||||
# homeassistant.components.motioneye
|
# homeassistant.components.motioneye
|
||||||
motioneye-client==0.3.12
|
motioneye-client==0.3.12
|
||||||
|
|
|
@ -671,7 +671,7 @@ minio==5.0.10
|
||||||
moehlenhoff-alpha2==1.1.2
|
moehlenhoff-alpha2==1.1.2
|
||||||
|
|
||||||
# homeassistant.components.motion_blinds
|
# homeassistant.components.motion_blinds
|
||||||
motionblinds==0.5.13
|
motionblinds==0.6.1
|
||||||
|
|
||||||
# homeassistant.components.motioneye
|
# homeassistant.components.motioneye
|
||||||
motioneye-client==0.3.12
|
motioneye-client==0.3.12
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue