Skip to content

Commit

Permalink
Added support for realtime position updates (not currently saving state)
Browse files Browse the repository at this point in the history
  • Loading branch information
pink88 committed Nov 25, 2024
1 parent e7161a5 commit 77c9d2c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 15 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Tuiss2HA
This adds support for Blinds2go Electronic blinds, powered by the Tuiss Smartview platform (if other brands exist they should work, but are untested). These blinds use bluetooth low energy and are controlled through a simple cover interface. As well as control of the blinds position through home assistant, this also includes 3 services.
1. get_battery_status: can be used to get the battery status (normal or low)
2. set_blind_position: allows setting decimal precision for the blind over home assistants built in integer position, which allows refined tilt controls.
2. set_blind_position: allows setting decimal precision for the blind over home assistants built in integer position, which allows refined tilt controls in some models.
3. get_blind_position: can be used to periodically poll your blinds to get their position, as using the Tuiss app or bluetooth remotes will not automatically update the position of the blind in home assistant due to limitations in the tuiss platform itself.

Note: This integration only controls blinds using BLE (bluetooth low energy), it will not control blinds that also or only support RF control.
Expand Down Expand Up @@ -75,6 +75,7 @@ To overwrite home assistants built in integer accuracy, you can use the "Tuiss2h

## Limitations ##
- Setting the top and bottom thresholds of the blind. Currently, you still need to pair with and use the Tuiss app to set these values.
- Realtime blind positioning as moving will work after the first use, as this is required to calibrate the speed of your blind motor.

## Troubleshooting ##
- I've only tested with HAOS installed on a Raspberry Pi4b and the built in Bluetooth module did not work, so I had to use a couple ESP32 devices with Bluetooth proxy software installed (See [here](https://esphome.io/components/bluetooth_proxy.html))
Expand Down
53 changes: 47 additions & 6 deletions custom_components/tuiss2ha/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asyncio
import logging
import voluptuous as vol
import datetime

from typing import Any

Expand Down Expand Up @@ -79,6 +80,10 @@ def __init__(self, blind) -> None:
self._attr_unique_id = f"{self._blind._id}_cover"
self._attr_name = self._blind.name
self._state = None
self._startTime = None
self._endTime = None
self._traversalTime = None
self._locked = False


@property
Expand Down Expand Up @@ -118,6 +123,11 @@ def current_cover_position(self):
return None
return self._blind._current_cover_position

@property
def traversal_time(self):
"""The speed of the blind, used to calculate the realtime position."""
return self._traversalTime

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed or not."""
Expand Down Expand Up @@ -161,6 +171,7 @@ async def async_added_to_hass(self) -> None:
)
self._blind.register_callback(self.async_write_ha_state)


async def async_will_remove_from_hass(self) -> None:
"""Entity being removed from hass."""
self._blind.remove_callback(self.async_write_ha_state)
Expand All @@ -185,26 +196,56 @@ async def async_set_cover_position(self, **kwargs: Any) -> None:
await self.async_move_cover(movVal,100 - kwargs[ATTR_POSITION])



async def async_move_cover(self, movVal, targetPos):
await self._blind.attempt_connection()
if self._blind._client.is_connected:
if self._blind._client.is_connected and self._locked == False:
self._locked = True
startPos = self._blind._current_cover_position
self._blind._moving = movVal

#Update the state and trigger the moving
await self.async_scheduled_update_request()
await self._blind.set_position(targetPos)
self._endTime = None
self._startTime = datetime.datetime.now()

while self._blind._client.is_connected:
await self._blind.check_connection()
#Update the position in realtime based on average traversal time
if self._traversalTime is not None:
_LOGGER.debug("StartPos: %s. Timedelta: %s",startPos, (datetime.datetime.now() - self._startTime).total_seconds())
self._blind._current_cover_position = startPos + ((datetime.datetime.now() - self._startTime).total_seconds()*self._traversalTime*movVal)
await self.async_scheduled_update_request()
await asyncio.sleep(1)
self._blind._current_cover_position = 100 - targetPos
self._blind._moving = 0
await self.async_scheduled_update_request()

#set the traversal time average and update final states only if the blind has not been stopped, as that updates itself
if not self._blind._is_stopping:
self._endTime = datetime.datetime.now()
await self.update_traversal_time(100-targetPos, startPos)

self._blind._current_cover_position = 100 - targetPos
self._blind._moving = 0
await self.async_scheduled_update_request()

#unlock the entity to allow more changes
self._locked = False


async def update_traversal_time(self, targetPos, startPos):
timeTaken = (self._endTime - self._startTime).total_seconds()
traversalDistance = abs(targetPos - startPos)
self._traversalTime = traversalDistance/timeTaken
_LOGGER.debug("%s: Time Taken: %s. Start Pos: %s. End Pos: %s. Distance Travelled: %s. Traversal Time: %s", self._attr_name, timeTaken, startPos, targetPos, traversalDistance, self._traversalTime)



async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the cover."""
self._blind._is_stopping = True
await self._blind.stop()
if self._blind._client:
while self._blind._client.is_connected:
await asyncio.sleep(1)
self._blind._moving = 0
await self.async_scheduled_update_request()
await self.async_scheduled_update_request()
self._locked = False
10 changes: 2 additions & 8 deletions custom_components/tuiss2ha/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def __init__(self, host: str, name: str, hub: Hub) -> None:
self._moving = 0
self._current_cover_position: None
self._desired_position:None
self._is_stopping = False


@property
Expand Down Expand Up @@ -187,18 +188,11 @@ async def stop(self) -> None:
if self._client and self._client.is_connected:
await self.send_command(UUID, command)
await self.get_blind_position()
self._is_stopping = False
else:
_LOGGER.debug("%s: Stop failed. %s", self.name, self._client.is_connected)


async def check_connection(self) -> None:
_LOGGER.debug(
"%s (%s) connected state is %s",
self.name,
self._ble_device,
self._client.is_connected,)



##################################################################################################
## GET METHODS ############################################################################
Expand Down

0 comments on commit 77c9d2c

Please sign in to comment.