From eefe7820c557a2d4c3a92fa3e59df6bfdaff23dc Mon Sep 17 00:00:00 2001 From: Robert Strouse Date: Sat, 4 Mar 2023 11:30:07 -0800 Subject: [PATCH] Add blind and curtain control for v1.3.2 Updates the back end software to include tilt control for blinds. --- custom_components/espsomfy_rts/__init__.py | 4 +- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2039 bytes .../__pycache__/config_flow.cpython-310.pyc | Bin 0 -> 4397 bytes .../__pycache__/const.cpython-310.pyc | Bin 0 -> 612 bytes .../__pycache__/controller.cpython-310.pyc | Bin 0 -> 14384 bytes .../__pycache__/cover.cpython-310.pyc | Bin 0 -> 7001 bytes .../__pycache__/entity.cpython-310.pyc | Bin 0 -> 1550 bytes custom_components/espsomfy_rts/const.py | 1 + custom_components/espsomfy_rts/controller.py | 50 ++++++++++++-- custom_components/espsomfy_rts/cover.py | 62 +++++++++++++++++- custom_components/espsomfy_rts/manifest.json | 2 +- custom_components/espsomfy_rts/services.yaml | 31 +++++++++ 12 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 custom_components/espsomfy_rts/__pycache__/__init__.cpython-310.pyc create mode 100644 custom_components/espsomfy_rts/__pycache__/config_flow.cpython-310.pyc create mode 100644 custom_components/espsomfy_rts/__pycache__/const.cpython-310.pyc create mode 100644 custom_components/espsomfy_rts/__pycache__/controller.cpython-310.pyc create mode 100644 custom_components/espsomfy_rts/__pycache__/cover.cpython-310.pyc create mode 100644 custom_components/espsomfy_rts/__pycache__/entity.cpython-310.pyc diff --git a/custom_components/espsomfy_rts/__init__.py b/custom_components/espsomfy_rts/__init__.py index 5e46a5f..3c3e139 100644 --- a/custom_components/espsomfy_rts/__init__.py +++ b/custom_components/espsomfy_rts/__init__.py @@ -27,8 +27,8 @@ async def _async_ws_close(_: Event) -> None: entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_ws_close) ) - - hass.config_entries.async_setup_platforms(entry, PLATFORMS) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + # hass.config_entries.async_setup_platforms(entry, PLATFORMS) await controller.ws_connect() return True diff --git a/custom_components/espsomfy_rts/__pycache__/__init__.cpython-310.pyc b/custom_components/espsomfy_rts/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00ca7c48517069ef9b230bbdafed0635ed6a20de GIT binary patch literal 2039 zcmZWqOK%%D5GHq5Z&|kGM;s?9R1^WKw2=RRVbo1j#6bOkrR1j>{0hvG}@hmj!gQejl*uIF-S9Uq@rvf)7B>}sYy*GN>iEHJ&o#9 znf3=q?KD%TP0y^gU$*zU?D_s~d%M%w>2$Y2>vZ=IOz|L&vc6OaoXWM|h%_^E&t)RE zwI1j!N;7o!OuQNNgmw0bx_J3#`@7padvL2V&29T&$JD0^#LtwBW1-G!F?jlA8s}G2 zI{10J`_um6ZpQ@2k=6>(Atnc*)u}>MOkRm>q*4>Sk}~djur*x)2zmIsM&Nz};fiMD zoSf4WdhK0!nFo>&x_9mWLEeMqgpPwOHy}6GvI&-P{)X%Tyw3>$RQjFtDbMr5yXu91 zPR9kfQ5=`D^0|NG-B5}iD&y)o&E{^r>8~0sYR2j zI-LASO&NksI{%UU`{Tx2sb1@0)DwE6CzaSZ25_P24qVtuh7do^^oGzwjTB=F6vhVW zAY-gGJT>zy(x+*UO=I1IKpKJYF6;E{i8E?IY{*=No3ZRgvEFLkx!7$6Hp|b|t0i#L zoL3e2F#(UVNL5iRT1RZaO&J_>(T_%Prf{B3Le9(q_@^ zBJD)VWZDx7(=|1RgUxavv{`rOfc=U75uEBVG?_Xw3_PDh>Wy_CfO0V=#Wfam@E`#D5EfD+(+m zV+t(vrYsD|g{L0DzMlp6$v6kR&)-nsus<$*=H|HvO!3ZEU)b(^!Ng`!eGUH2s%wPn zAsbF!1lI($4kuje{h=JH2dE+|Z*`YIXSz_vd9l61p}L}++M2&P6p4Hz?$q3vOr-PLwRtvu z2kjHMuD-+1o$MPfdu1IWA>d`5u*Lal!KKMW1cCY_O`- zpRCUYgbxcS7)!pFC*$08mIrCSqFlsJMJgI60fHaUn68e-~5fK%<}Irnt`W&6+#)jVS} u|Er)7WP*j>c5bHmpZQbVOe7!0VhgdhqEBy~KwhKxC literal 0 HcmV?d00001 diff --git a/custom_components/espsomfy_rts/__pycache__/config_flow.cpython-310.pyc b/custom_components/espsomfy_rts/__pycache__/config_flow.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..015a097e52a075ac42943441f8eaacaf0910d1db GIT binary patch literal 4397 zcmaJ^U31&U6~!(<5QHd7mTZ064w@!T*rrr7P18)8woz>*(o7;}L?@Xb?OHOY+hnBS3^PJhF`wlDb$o+j$K3qlr^Ou@l&_wEOF?>+b2yXo|_ zYvB3o-%atKi-z%UYMgzlXuNG0EPINIFnmKW!DE(ipEIhPv6)!DrR!E)@omT|aW!## zN6YNEmbkvF>(zKFsrz+ZcjD=!;Wv^Qe@3@!@oaLzzo6@GJeSP-^SVA2Ura9fmy!j4 zAzAbnwX7a5CCmOY>eHeTuOzGfs;o!eQzZL!^Z-;T%%~Pr7&bErptJ$-%TZjD+>r6*ku$N|e zaIX_b;#*c6#J+L!{q?QwrllG;qO6tvZEcqvPC^3KzSh`RZwSuS7kptL_LTZZ{S_PU%j zJAN$u*YJKrh`+UTYxL_mTbJ_U95aw6rF?-F}GK95h{A2aLIWqT6 zQMt?I<(`{cBlDn8S?T)T`qW+3N?48T9)Eudqc`IA3z$ zqBi{7N2In+9(ws+=nXQQHR|;Txd*$Y2N@(@o_er!4`yk(9-bRGuZQ#N8F$0O%-fY| z;+SfNX-e30)qI}PTJ_lju^<{c55~1*JAD(`#SaMh;D7?A0 z2E~%v-8E5KZTs7QK~uweX(__1;_I&hYn438<51PGY1k-;MAMeU2WqjC9rn7xoiHDx zD}(KTX=OsBY(O&~Wby(HwQ_*2X~_jNnmTBX1oxi~2xnPWT~vpnL2$ zat3>n%P5q!4NY{(6U&F{7O;Co&bVfc@-Slh02IYXT*+-9OpddJ(Ty& zoFC!cw{kXu>7MYsLR@~|8X5bxFh|yi3+ssqO;mpg%)^HZE2z*5Lfa zISVs;_tkBrY* zZbGTnh&|#&p<~iyo1npwab$y3tH%t4SxTH^-Lqd4ocg{nzM|F5C;Vfh4RX!-;q`(@ z1&oxlw?L@9Wysybn`3ze#bPsj4=>3DX4Z|~Pf3?rQ<1r5Kv+3AV6AYtMP zkZ|b$lXSWLv;ijcPD8i%HJ4YQ3C&%;dmz)^9UTu5v~@5iPa*DB*6mIf`ZK4*8fY9h z`7?tyr)7hdB4|jmmYk!4s8KoPJT*VY1;};Ki#{8G%7perbv5YRPGufMyU%*GqegcI z1gp7XW>Si7P#gCsf_;BS6{>FzZms5!AomN5Ye4j55QwNV6|_O*8_-b-#V&LW_rv4X2230zzKR9nuz}z ztkp3zouirn$i}m5nkA}V^6^^6kz3{k;16fGG6q(;ZSV9vE#*L82%K9&zKZRs+G*h0 zR&zSE2K^ZEZZC~RC^J<_QW3_$o2K3OFr_&qe@-)3fZ$^y6o{2Q=u+Uc3V zd|_m<;YUy@_mS*_t$6$F}_+7Vb+yV zpB#4rCe510guF>ZJt{Pv5CSBEq9Na;;&m#1gJQzVwJ|9PJLl!vs~tZw>1*#5T}E@9z&77 z$&ic`L(X+rQloK`CO#WJMF~i+lY~K_+#rDe3}UL+gW$74Cm#1ygFvKRh^rrM?6kLT zZp&ZLGQ=I42<1CeYg0k&c1AZE_t!}FGKyc}&1heaVL?4E=T#21G`_3PxyyWU_C$|D zwqT1i=|nx8R%g9*i_zhuNNM!;X_@9r@)P{g&UXj6%wXq|k~n4K3KaG3NMbnUB83?A z^FfM?nyjj(ClX^xZlabZWUXh(vZQ=T&7be?Aps%SsJZh!GSs&&tsVc$WUo$jbXd{( zwocB}rO7dSVGIt+^gY49`lEdLyx(-{Rv%Rm_8 g4zF_xXS1l+*+o{b)cFz+Q0KT%oDj9SwUrD1135s3N&o-= literal 0 HcmV?d00001 diff --git a/custom_components/espsomfy_rts/__pycache__/const.cpython-310.pyc b/custom_components/espsomfy_rts/__pycache__/const.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b92d2071f6c0a10e851f81a7d8df4d20bd484a9 GIT binary patch literal 612 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 Pj8BZ%#wW(CDf|BbHHe-r literal 0 HcmV?d00001 diff --git a/custom_components/espsomfy_rts/__pycache__/cover.cpython-310.pyc b/custom_components/espsomfy_rts/__pycache__/cover.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1305b2dd166b7e262a51e029058089a98c13f067 GIT binary patch literal 7001 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 literal 0 HcmV?d00001 diff --git a/custom_components/espsomfy_rts/const.py b/custom_components/espsomfy_rts/const.py index c0e0b22..39540ca 100644 --- a/custom_components/espsomfy_rts/const.py +++ b/custom_components/espsomfy_rts/const.py @@ -5,6 +5,7 @@ API_CONTROLLER = "/controller" API_SHADES = "/shades" API_SHADECOMMAND = "/shadeCommand" +API_TILTCOMMAND = "/tiltCommand" API_DISCOVERY = "/discovery" EVT_CONTROLLER = "controller" EVT_SHADESTATE = "shadeState" diff --git a/custom_components/espsomfy_rts/controller.py b/custom_components/espsomfy_rts/controller.py index 7de7a67..c8b9a17 100644 --- a/custom_components/espsomfy_rts/controller.py +++ b/custom_components/espsomfy_rts/controller.py @@ -11,6 +11,7 @@ import aiohttp import websocket + from homeassistant.components.cover import CoverDeviceClass, CoverEntityFeature from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant @@ -23,6 +24,7 @@ from .const import ( API_DISCOVERY, API_SHADECOMMAND, + API_TILTCOMMAND, API_SHADES, DOMAIN, EVT_CONNECTED, @@ -220,21 +222,36 @@ def ensure_shade_configured(self, data): for entity in async_entries_for_config_entry(entities, self.config_entry_id): if entity.unique_id == uuid: return + dev_features = (CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.STOP + | CoverEntityFeature.SET_POSITION) + + dev_class = CoverDeviceClass.SHADE + if "shadeType" in data: + match int(data["shadeType"]): + case 1: + dev_class = CoverDeviceClass.BLIND + if "hasTilt" in data and data["hasTilt"] is True: + dev_features |= (CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT | CoverEntityFeature.SET_TILT_POSITION) + case 2: + dev_class = CoverDeviceClass.CURTAIN + case _: + dev_class = CoverDeviceClass.SHADE + + # Reload all the shades # self.api.load_shades() # I have no idea whether this reloads the devices or not. entities.async_get_or_create( domain=DOMAIN, platform=Platform.COVER, - original_device_class=CoverDeviceClass.SHADE, + original_device_class=dev_class, unique_id=uuid, device_id=device.id, original_name=data["name"], suggested_object_id=f"{str(data['name']).lower().replace(' ', '_')}", - supported_features=CoverEntityFeature.OPEN - | CoverEntityFeature.CLOSE - | CoverEntityFeature.STOP - | CoverEntityFeature.SET_POSITION, + supported_features=dev_features, ) print(f"Shade not found {uuid} and one was added") @@ -333,6 +350,19 @@ async def load_shades(self) -> Any | None: else: _LOGGER.error(await resp.text()) + async def tilt_open(self, shade_id: int): + """Send the command to open the tilt""" + await self.tilt_command({"shadeId": shade_id, "command": "up"}) + + async def tilt_close(self, shade_id: int): + """Send the command to close the tilt""" + await self.tilt_command({"shadeId": shade_id, "command": "down"}) + + async def position_tilt(self, shade_id: int, position: int): + """Send the command to position the shade""" + print(f"Setting tilt position to {position}") + await self.tilt_command({"shadeId": shade_id, "target": position}) + async def open_shade(self, shade_id: int): """Send the command to open the shade""" await self.shade_command({"shadeId": shade_id, "command": "up"}) @@ -359,6 +389,16 @@ async def shade_command(self, data): else: _LOGGER.error(await resp.text()) + async def tilt_command(self, data): + """Send commands to ESPSomfyRTS via PUT request""" + async with self._session.put( + f"{self._api_url}{API_TILTCOMMAND}", json=data + ) as resp: + if resp.status == 200: + pass + else: + _LOGGER.error(await resp.text()) + async def get_initial(self): """Get the initial config from nodejs-PoolController.""" try: diff --git a/custom_components/espsomfy_rts/cover.py b/custom_components/espsomfy_rts/cover.py index 4070aa7..a3c3929 100644 --- a/custom_components/espsomfy_rts/cover.py +++ b/custom_components/espsomfy_rts/cover.py @@ -1,4 +1,4 @@ -"""Support for ESPSomfy RTS Shades.""" +"""Support for ESPSomfy RTS Shades and Blinds.""" from __future__ import annotations from typing import Any @@ -7,6 +7,7 @@ from homeassistant.components.cover import ( ATTR_POSITION, + ATTR_TILT_POSITION, CoverDeviceClass, CoverEntity, CoverEntityFeature, @@ -25,6 +26,9 @@ SVC_CLOSE_SHADE = "close_shade" SVC_STOP_SHADE = "stop_shade" SVC_SET_SHADE_POS = "set_shade_position" +SVC_TILT_OPEN = "tilt_open" +SVC_TILT_CLOSE = "tilt_close" +SVC_SET_TILT_POS = "set_tilt_position" async def async_setup_entry( @@ -49,9 +53,16 @@ async def async_setup_entry( {vol.Required(ATTR_POSITION): cv.string}, "async_set_cover_position", ) + platform.async_register_entity_service( + SVC_SET_TILT_POS, + {vol.Required(ATTR_POSITION): cv.string}, + "async_set_cover_tilt_position", + ) platform.async_register_entity_service(SVC_OPEN_SHADE, {}, "async_open_cover") platform.async_register_entity_service(SVC_CLOSE_SHADE, {}, "async_close_cover") platform.async_register_entity_service(SVC_STOP_SHADE, {}, "async_stop_cover") + platform.async_register_entity_service(SVC_TILT_OPEN, {}, "async_tilt_open") + platform.async_register_entity_service(SVC_TILT_CLOSE, {}, "async_tilt_close") class ESPSomfyShade(ESPSomfyEntity, CoverEntity): @@ -62,10 +73,13 @@ def __init__(self, controller: ESPSomfyController, data): self._controller = controller self._shade_id = data["shadeId"] self._position = data["position"] + self._tilt_postition = 100 + self._tilt_direction = 0 self._attr_unique_id = f"{controller.unique_id}_{self._shade_id}" self._attr_name = data["name"] self._direction = 0 self._available = True + self._has_tilt = False self._attr_device_class = CoverDeviceClass.SHADE self._attr_supported_features = ( @@ -74,6 +88,24 @@ def __init__(self, controller: ESPSomfyController, data): | CoverEntityFeature.STOP | CoverEntityFeature.SET_POSITION ) + if "hasTilt" in data and data["hasTilt"] is True: + self._attr_supported_features |= ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.SET_TILT_POSITION + ) + self._has_tilt = True + self._tilt_postition = data["tiltPosition"] if "tiltPosition" in data else 100 + self._tilt_direction = data["tiltDirection"] if "tiltDirecion" in data else 0 + if "shadeType" in data: + match data["shadeType"]: + case 1: + self._attr_device_class = CoverDeviceClass.BLIND + case 2: + self._attr_device_class = CoverDeviceClass.CURTAIN + case _: + self._attr_device_class = CoverDeviceClass.SHADE + self._attr_is_closed: bool = False # print(f"Set up shade {self._attr_unique_id} - {self._attr_name}") @@ -87,6 +119,13 @@ def _handle_coordinator_update(self) -> None: self._position = int(self._controller.data["position"]) if "direction" in self._controller.data: self._direction = int(self._controller.data["direction"]) + if "hasTilt" in self._controller.data: + self._has_tilt = self._controller.data["hasTilt"] + if self._has_tilt is True: + if "tiltDirection" in self._controller.data: + self._tilt_direction = int(self._controller.data["tiltDirection"]) + if "tiltPosition" in self._controller.data: + self._tilt_postition = int(self._controller.data["tiltPosition"]) self._available = True elif self._controller.data["event"] == EVT_SHADEREMOVED: self._available = False @@ -109,9 +148,15 @@ def should_poll(self) -> bool: return False @property - def current_cover_position(self) -> int: + def current_cover_position(self) -> int | None: """Return the current position of the shade.""" return 100 - self._position + @property + def current_cover_tilt_position(self) -> int | None: + """Return current position of cover tilt. 0 is closed, 100 is open.""" + if not self._has_tilt: + return None + return 100 - self._tilt_postition @property def is_opening(self) -> bool: @@ -133,6 +178,19 @@ def is_open(self) -> bool: """Return true if cover is closed.""" return self._position == 0 + async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: + """Set the tilt postion""" + await self._controller.api.position_tilt( + self._shade_id, 100 - kwargs[ATTR_TILT_POSITION] + ) + async def async_open_cover_tilt(self, **kwargs: Any) -> None: + """Open the tilt position""" + await self._controller.api.tilt_open(self._shade_id) + + async def async_close_cover_tilt(self, **kwargs: Any) -> None: + """Close the tilt position""" + await self._controller.api.tilt_close(self._shade_id) + async def async_set_cover_position(self, **kwargs: Any) -> None: """Set the cover position.""" await self._controller.api.position_shade( diff --git a/custom_components/espsomfy_rts/manifest.json b/custom_components/espsomfy_rts/manifest.json index b7824ce..0c4d075 100644 --- a/custom_components/espsomfy_rts/manifest.json +++ b/custom_components/espsomfy_rts/manifest.json @@ -20,5 +20,5 @@ "dependencies": [], "codeowners": ["@rstrouse"], "iot_class": "local_push", - "version": "1.0.0" + "version": "1.0.8" } diff --git a/custom_components/espsomfy_rts/services.yaml b/custom_components/espsomfy_rts/services.yaml index a047a84..feb824d 100644 --- a/custom_components/espsomfy_rts/services.yaml +++ b/custom_components/espsomfy_rts/services.yaml @@ -38,6 +38,37 @@ set_shade_postion: integration: espsomfy_rts domain: cover +tilt_open: + name: Tilt Open + description: Tilts the slats open + target: + integration: espsomfy_rts + domain: cover + +tilt_close: + name: Tilt Close + description: Tilts the slats closed + target: + integration: espsomfy_rts + domain: cover + +set_tilt_postion: + name: Set Position + description: Sets the tilt position to % of open + fields: + position: + name: position + description: Percentage of open for the shade slats + required: true + example: 50 + selector: + text: + target: + entity: + integration: espsomfy_rts + domain: cover + +