From 055cf05b4882b81b05232bc90227f5e4ed91737a Mon Sep 17 00:00:00 2001 From: Devmel Apps Date: Fri, 4 Aug 2023 16:41:19 +0200 Subject: [PATCH] async states --- README.md | 17 ++++++-------- custom_components/airsend/__init__.py | 17 ++++++++------ custom_components/airsend/button.py | 2 +- custom_components/airsend/cover.py | 11 ++++----- custom_components/airsend/device.py | 33 ++++++++++++++++++++------- custom_components/airsend/switch.py | 4 ++-- install | 2 +- 7 files changed, 51 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 6c9842c..78f418d 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,18 @@ Component for sending radio commands through the AirSend (RF433) or AirSend duo ## Installation -1. Into the terminal, run `wget -q -O - https://raw.githubusercontent.com/devmel/hass_airsend/master/install | bash -` +1. Install and start [hass_airsend-addon](https://github.com/devmel/hass_airsend-addon). +2. Go to `airsend.cloud -> import/export -> Export YAML` and copy the airsend.yaml file to the folder `config` +2. Add `airsend: !include airsend.yaml` at the end of your `configuration.yaml` file +3. Into the terminal, run `wget -q -O - https://raw.githubusercontent.com/devmel/hass_airsend/master/install | bash -` OR copy the `airsend` folder into your [custom_components folder](https://developers.home-assistant.io/docs/creating_integration_file_structure/#where-home-assistant-looks-for-integrations). -2. To allow a local LAN connection please install and start [hass_airsend-addon](https://github.com/devmel/hass_airsend-addon). -3. Restart Home Assistant -4. Add `airsend:` to your HA configuration (see configuration below). -5. Restart Home Assistant +4. Restart Home Assistant ## Configuration -### YAML - -To integrate `airsend` into Home Assistant, go to `airsend.cloud -> import/export -> Export YAML` and add the contents of the downloaded file into your HA configuration `configuration.yaml`. - #### Local LAN connection -The configuration allows to use the local mode (if [hass_airsend-addon](https://github.com/devmel/hass_airsend-addon) is started) by adding the field `spurl: !secret spurl` in each device. In this mode you must modify the file `secrets.yaml` by adding the local url of the AirSend with its local ipv4 (ex: 192.168.x.x so `spurl: sp://airsend_password@192.168.x.x`), the local ipv6 `fe80::` does not work because of virtualization. You can also remove fields `apiKey`. +The configuration allows to use the local mode by adding the field `spurl: !secret spurl` in each device. In this mode you must modify the file `secrets.yaml` by adding the local url of the AirSend with its local ipv4 (ex: 192.168.x.x so `spurl: sp://airsend_password@192.168.x.x`), the local ipv6 `fe80::` does not work because of virtualization. You can also remove fields `apiKey`. +The local mode requires the execution of [hass_airsend-addon](https://github.com/devmel/hass_airsend-addon), if it is not on the same machine it is possible to add the field `internal_url: http://x.x.x.x:33863/` in airsend.conf ## Preview diff --git a/custom_components/airsend/__init__.py b/custom_components/airsend/__init__.py index 3bc15f2..afb270d 100644 --- a/custom_components/airsend/__init__.py +++ b/custom_components/airsend/__init__.py @@ -3,14 +3,13 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType from homeassistant.helpers import discovery from homeassistant.components.hassio import ( - async_get_addon_info, + get_addons_info, ) from homeassistant.const import CONF_INTERNAL_URL DOMAIN = "airsend" AS_TYPE = ["switch", "cover", "button"] - async def async_setup(hass: HomeAssistant, config: ConfigType): """Set up the AirSend component.""" if DOMAIN not in config: @@ -22,12 +21,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType): pass if internalurl == "": try: - addon_info: dict = await async_get_addon_info(hass, "local_airsend") - ip = addon_info["ip_address"] - if ip: - internalurl = "http://" + str(ip) + ":33863/" - except KeyError: + addons_info = get_addons_info(hass) + for name, options in addons_info.items(): + if "_airsend" in name: + ip = options["ip_address"] + if ip: + internalurl = "http://" + str(ip) + ":33863/" + except: pass + if internalurl != "" and not internalurl.endswith('/'): + internalurl += "/" config[DOMAIN][CONF_INTERNAL_URL] = internalurl for plateform in AS_TYPE: discovery.load_platform(hass, plateform, DOMAIN, config[DOMAIN].copy(), config) diff --git a/custom_components/airsend/button.py b/custom_components/airsend/button.py index b05ec9e..5539650 100644 --- a/custom_components/airsend/button.py +++ b/custom_components/airsend/button.py @@ -72,5 +72,5 @@ class AirSendButton(ButtonEntity): def press(self, **kwargs: Any) -> None: """Handle the button press.""" note = {"method": 1, "type": 0, "value": "TOGGLE"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self.schedule_update_ha_state() diff --git a/custom_components/airsend/cover.py b/custom_components/airsend/cover.py index d1a4b85..f568de0 100644 --- a/custom_components/airsend/cover.py +++ b/custom_components/airsend/cover.py @@ -6,7 +6,6 @@ from .device import Device from homeassistant.components.cover import CoverEntity from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType - from homeassistant.const import CONF_DEVICES, CONF_INTERNAL_URL from . import DOMAIN @@ -75,12 +74,12 @@ class AirSendCover(CoverEntity): @property def is_closed(self): """Return if the cover is closed.""" - return not self._closed + return self._closed def open_cover(self, **kwargs: Any) -> None: """Open the cover.""" note = {"method": 1, "type": 0, "value": "UP"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._closed = False if self._device.is_cover_with_position: self._attr_current_cover_position = 100 @@ -89,7 +88,7 @@ class AirSendCover(CoverEntity): def close_cover(self, **kwargs: Any) -> None: """Close cover.""" note = {"method": 1, "type": 0, "value": "DOWN"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._closed = True if self._device.is_cover_with_position: self._attr_current_cover_position = 0 @@ -98,7 +97,7 @@ class AirSendCover(CoverEntity): def stop_cover(self, **kwargs): """Stop the cover.""" note = {"method": 1, "type": 0, "value": "STOP"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._closed = False if self._device.is_cover_with_position: self._attr_current_cover_position = 50 @@ -108,7 +107,7 @@ class AirSendCover(CoverEntity): """Move the cover to a specific position.""" position = int(kwargs["position"]) note = {"method": 1, "type": 9, "value": position} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._attr_current_cover_position = position self._closed = False if self._attr_current_cover_position == 0: diff --git a/custom_components/airsend/device.py b/custom_components/airsend/device.py index 5fbb9d1..48205b1 100644 --- a/custom_components/airsend/device.py +++ b/custom_components/airsend/device.py @@ -1,6 +1,7 @@ """AirSend device.""" import logging import json +import hashlib from requests import get, post, exceptions from . import DOMAIN @@ -23,6 +24,7 @@ class Device: self._rtype = None self._apikey = None self._spurl = None + self._wait = False self._channel = {} self._note = None try: @@ -41,6 +43,10 @@ class Device: self._spurl = options["spurl"] except KeyError: pass + try: + self._wait = eval(str(options["wait"])) + except KeyError: + pass try: self._channel = options["channel"] except KeyError: @@ -83,10 +89,15 @@ class Device: return True return False - def transfer(self, note) -> bool: + def transfer(self, note, entity_id = None) -> bool: """Send a command.""" status_code = 404 - if self._serviceurl and self._spurl: + ret = False + wait = 'false, "callback":"http://127.0.0.1/"' + if self._wait == True: + wait = 'true' + if self._serviceurl and self._spurl and entity_id is not None: + uid = hashlib.sha256(entity_id.encode('utf-8')).hexdigest()[:12] jnote = json.dumps(note) if ( self._note is not None @@ -101,9 +112,9 @@ class Device: ): jnote = json.dumps(self._note) payload = ( - '{"wait": true, "channel":' + '{"wait": '+wait+', "channel":' + json.dumps(self._channel) - + ', "thingnotes":{"notes":[' + + ', "thingnotes":{"uid":"0x'+uid+'", "notes":[' + jnote + "]}}" ) @@ -119,9 +130,14 @@ class Device: data=payload, timeout=6, ) - status_code = 500 - jdata = json.loads(response.text) - if jdata["type"] < 0x100: + if self._wait == True: + ret = True + status_code = 500 + jdata = json.loads(response.text) + if jdata["type"] < 0x100: + status_code = response.status_code + else: + ret = None status_code = response.status_code except exceptions.RequestException: pass @@ -164,9 +180,10 @@ class Device: try: response = get(cloud_url, headers=headers, timeout=10) status_code = response.status_code + ret = True except exceptions.RequestException: pass if status_code == 200: - return True + return ret _LOGGER.error("Transfer error '%s' : '%s'", self.name, status_code) raise Exception("Transfer error " + self.name + " : " + str(status_code)) diff --git a/custom_components/airsend/switch.py b/custom_components/airsend/switch.py index 0473ced..965ebb7 100644 --- a/custom_components/airsend/switch.py +++ b/custom_components/airsend/switch.py @@ -77,13 +77,13 @@ class AirSendSwitch(SwitchEntity): def turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" note = {"method": 1, "type": 0, "value": "ON"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._state = True self.schedule_update_ha_state() def turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" note = {"method": 1, "type": 0, "value": "OFF"} - if self._device.transfer(note): + if self._device.transfer(note, self.entity_id) == True: self._state = False self.schedule_update_ha_state() diff --git a/install b/install index 8df145d..67c9bbe 100644 --- a/install +++ b/install @@ -56,7 +56,7 @@ if [ -n "$haPath" ]; then cd "$haPath/custom_components" || error "Could not change path to $haPath/custom_components" info "Downloading AirSend Home Assistant Component" - wget "https://github.com/devmel/hass_airsend/releases/download/1.0/hass_airsend.zip" + wget "https://github.com/devmel/hass_airsend/releases/download/latest/hass_airsend.zip" if [ -d "$haPath/custom_components/hass_airsend" ]; then warn "airsend directory already exist, cleaning up..."