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.
- 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

View File

@ -30,6 +30,7 @@ async def async_setup_entry(hass, config_entry):
plejdManager = pyplejd.PlejdManager(config_entry.data)
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
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({
config_entry.entry_id: devices
})
hass.data[DOMAIN].setdefault("scenes", {}).update({
config_entry.entry_id: scenes
})
hass.data[DOMAIN].setdefault("manager", {}).update({
config_entry.entry_id: plejdManager,
})
@ -83,7 +87,9 @@ async def async_setup_entry(hass, config_entry):
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
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
from homeassistant.components.light import LightEntity, ColorMode
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 .mesh import PlejdMesh
from .api import get_cryptokey, get_devices, get_site_data
from .plejd_device import PlejdDevice
from .api import get_cryptokey, get_devices, get_site_data, get_scenes
from .plejd_device import PlejdDevice, PlejdScene
from .const import PLEJD_SERVICE
@ -18,6 +18,7 @@ class PlejdManager:
self.mesh = PlejdMesh()
self.mesh.statecallback = self._update_device
self.devices = { }
self.scenes = []
self.credentials = credentials
def add_mesh_device(self, device):
@ -46,6 +47,13 @@ class PlejdManager:
_LOGGER.info(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):
address = deviceState["address"]
if address in self.devices:

View File

@ -103,3 +103,18 @@ async def get_devices(**credentials):
}
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()
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):
if self.client is None:
return False

View File

@ -48,7 +48,6 @@ class PlejdDevice:
def __repr__(self):
return f"<PlejdDevice(<manager>, {self.address}, {self.BLE_address}, {self.data}>"
pass
@property
def available(self):
@ -111,3 +110,24 @@ class PlejdDevice:
async def turn_off(self):
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)