Add scene support

This commit is contained in:
Thomas Lovén 2022-10-20 17:27:31 +02:00
parent bdd6ab840c
commit 071aa7a9ac
8 changed files with 107 additions and 19 deletions

View File

@ -13,20 +13,6 @@ I do not guarantee it will work or even that it will not harm your system. I don
- Hopefully, your Plejd mesh will be auto discovered and you should see a message popping up in your integrations page. - Hopefully, your Plejd mesh will be auto discovered and you should see a message popping up in your integrations page.
- Log in with the credentials you use in the Plejd app when prompted (email address and password) - Log in with the credentials you use in the Plejd app when prompted (email address and password)
## Current limitations
- I only have a single DIM-01 device, so that's the only one I know for sure works.
- Only one channel per device is expected to work right now. E.g. only one lamp connected to a DIM-02 will show up in Home Assistant.
## About connections
Plejd devices doesn't seem to like to have multiple connections going.
Once a controller like the official Plejd app or Home Assistant connects, they will hide their precense from everyone else until a time after that connection is broken.
That means that if you only have one Plejd device you may not be able to use Home Assistant and the app to controll the device at the same time, and either may have a hard time connecting while the other is running.
Even after turning off the app or Home Assistant it may take 30 minutes to several hours before the othe rcan connect again. Turning off bluetooth on the last connected device or cutting power to the Plejd for a minute may help.
## Getting more debug information ## Getting more debug information

View File

@ -30,6 +30,7 @@ async def async_setup_entry(hass, config_entry):
plejdManager = pyplejd.PlejdManager(config_entry.data) plejdManager = pyplejd.PlejdManager(config_entry.data)
devices = await plejdManager.get_devices() devices = await plejdManager.get_devices()
scenes = await plejdManager.get_scenes()
# Add a service entry if there are no devices - just so the user can get diagnostics data # Add a service entry if there are no devices - just so the user can get diagnostics data
if sum(d.type in ["light", "switch"] for d in devices.values()) == 0: if sum(d.type in ["light", "switch"] for d in devices.values()) == 0:
@ -52,6 +53,9 @@ async def async_setup_entry(hass, config_entry):
hass.data[DOMAIN].setdefault("devices", {}).update({ hass.data[DOMAIN].setdefault("devices", {}).update({
config_entry.entry_id: devices config_entry.entry_id: devices
}) })
hass.data[DOMAIN].setdefault("scenes", {}).update({
config_entry.entry_id: scenes
})
hass.data[DOMAIN].setdefault("manager", {}).update({ hass.data[DOMAIN].setdefault("manager", {}).update({
config_entry.entry_id: plejdManager, config_entry.entry_id: plejdManager,
}) })
@ -83,7 +87,9 @@ async def async_setup_entry(hass, config_entry):
plejdManager.add_mesh_device(service_info.device) plejdManager.add_mesh_device(service_info.device)
await hass.config_entries.async_forward_entry_setups(config_entry, ["light", "switch"]) await hass.config_entries.async_forward_entry_setups(config_entry,
["light", "switch", "button"]
)
# Ping mesh intermittently to keep the connection alive # Ping mesh intermittently to keep the connection alive
async def _ping(now=None): async def _ping(now=None):

View File

@ -0,0 +1,47 @@
import logging
from homeassistant.components.button import ButtonEntity
_LOGGER = logging.getLogger(__name__)
DOMAIN = "plejd"
async def async_setup_entry(hass, config_entry, async_add_entities):
scenes = hass.data[DOMAIN]["scenes"].get(config_entry.entry_id, [])
entities = []
for s in scenes:
button = PlejdSceneButton(s, config_entry.entry_id)
entities.append(button)
async_add_entities(entities, False)
class PlejdSceneButton(ButtonEntity):
def __init__(self, device, entry_id):
super().__init__()
self.device = device
self.entry_id = entry_id
@property
def device_info(self):
return {
"identifiers": {(DOMAIN, f"{self.entry_id}:{self.device.index}")},
"name": self.device.name,
"manufacturer": "Plejd",
#"connections": ???,
}
@property
def has_entity_name(self):
return True
@property
def name(self):
return None
@property
def unique_id(self):
return f"{self.entry_id}:{self.device.index}"
async def async_press(self):
await self.device.activate()

View File

@ -1,4 +1,3 @@
from builtins import property
import logging import logging
from homeassistant.components.light import LightEntity, ColorMode from homeassistant.components.light import LightEntity, ColorMode
from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator

View File

@ -4,8 +4,8 @@ from datetime import timedelta
from bleak_retry_connector import close_stale_connections from bleak_retry_connector import close_stale_connections
from .mesh import PlejdMesh from .mesh import PlejdMesh
from .api import get_cryptokey, get_devices, get_site_data from .api import get_cryptokey, get_devices, get_site_data, get_scenes
from .plejd_device import PlejdDevice from .plejd_device import PlejdDevice, PlejdScene
from .const import PLEJD_SERVICE from .const import PLEJD_SERVICE
@ -18,6 +18,7 @@ class PlejdManager:
self.mesh = PlejdMesh() self.mesh = PlejdMesh()
self.mesh.statecallback = self._update_device self.mesh.statecallback = self._update_device
self.devices = { } self.devices = { }
self.scenes = []
self.credentials = credentials self.credentials = credentials
def add_mesh_device(self, device): def add_mesh_device(self, device):
@ -46,6 +47,13 @@ class PlejdManager:
_LOGGER.info(self.devices) _LOGGER.info(self.devices)
return self.devices return self.devices
async def get_scenes(self):
scenes = await get_scenes(**self.credentials)
self.scenes = [PlejdScene(self, **s) for s in scenes]
_LOGGER.info("Scenes")
_LOGGER.info(self.scenes)
return self.scenes
async def _update_device(self, deviceState): async def _update_device(self, deviceState):
address = deviceState["address"] address = deviceState["address"]
if address in self.devices: if address in self.devices:

View File

@ -103,3 +103,18 @@ async def get_devices(**credentials):
} }
return retval return retval
async def get_scenes(**credentials):
site_data = await get_site_data(**credentials)
retval = []
for scene in site_data["scenes"]:
if scene["hiddenFromSceneList"]: continue
sceneId = scene["sceneId"]
index = site_data["sceneIndex"].get(sceneId)
retval.append({
"index": index,
"title": scene["title"],
})
return retval

View File

@ -136,6 +136,13 @@ class PlejdMesh():
await self.poll() await self.poll()
return retval return retval
async def activate_scene(self, index):
payload = binascii.a2b_hex(f"0201100021{index:02x}")
retval = await self.write(payload)
if self.pollonWrite:
await self.poll()
return retval
async def ping(self): async def ping(self):
if self.client is None: if self.client is None:
return False return False

View File

@ -48,7 +48,6 @@ class PlejdDevice:
def __repr__(self): def __repr__(self):
return f"<PlejdDevice(<manager>, {self.address}, {self.BLE_address}, {self.data}>" return f"<PlejdDevice(<manager>, {self.address}, {self.BLE_address}, {self.data}>"
pass
@property @property
def available(self): def available(self):
@ -111,3 +110,24 @@ class PlejdDevice:
async def turn_off(self): async def turn_off(self):
await self.manager.mesh.set_state(self.address, False) await self.manager.mesh.set_state(self.address, False)
class PlejdScene:
def __init__(self, manager, index, title):
self._manager = manager
self._index = index
self._title = title
def __repr__(self):
return f"<PlejdScene(<manager>, {self._index}, '{self._title}'>"
@property
def name(self):
return self._title
@property
def index(self):
return self._index
async def activate(self):
await self._manager.mesh.activate_scene(self._index)