diff --git a/README.md b/README.md index 936f6dd..61c577a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ Component for sending radio commands through the AirSend (RF433) or AirSend duo - Restart Home Assistant. - Restart the AirSend addon. +6. **Configure the Integration**: + - Navigate to Settings -> Devices & services -> + Add Integration. + - Search for and select AirSend. + - Select the rooms associated with your devices to complete the setup. + These steps will integrate AirSend with Home Assistant, allowing you to manage and automate AirSend-related tasks through the Home Assistant interface. diff --git a/airsend.example.yaml b/airsend.example.yaml index 55e1f62..7482fd2 100644 --- a/airsend.example.yaml +++ b/airsend.example.yaml @@ -1,4 +1,4 @@ -#internal_url: http://192.168.0.21:33863/ +#internal_url: http://172.30.33.0:33863/ devices: AirSend: type: 0 diff --git a/configuration.example.yaml b/configuration.example.yaml deleted file mode 100644 index 17d3872..0000000 --- a/configuration.example.yaml +++ /dev/null @@ -1,11 +0,0 @@ - -# Configure a default setup of Home Assistant (frontend, api, etc) -default_config: - -group: !include groups.yaml -automation: !include automations.yaml -script: !include scripts.yaml -scene: !include scenes.yaml - -airsend: !include airsend.yaml - diff --git a/custom_components/airsend/brand/icon.png b/custom_components/airsend/brand/icon.png index a24c0c9..cb2629e 100644 Binary files a/custom_components/airsend/brand/icon.png and b/custom_components/airsend/brand/icon.png differ diff --git a/custom_components/airsend/brand/icon@2x.png b/custom_components/airsend/brand/icon@2x.png new file mode 100644 index 0000000..995b3af Binary files /dev/null and b/custom_components/airsend/brand/icon@2x.png differ diff --git a/custom_components/airsend/cover.py b/custom_components/airsend/cover.py index aa403f0..d3dfd9f 100644 --- a/custom_components/airsend/cover.py +++ b/custom_components/airsend/cover.py @@ -8,6 +8,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.const import CONF_INTERNAL_URL from . import DOMAIN @@ -29,7 +30,7 @@ async def async_setup_entry( async_add_entities(entities) -class AirSendCover(CoverEntity): +class AirSendCover(RestoreEntity, CoverEntity): """Representation of an AirSend Cover.""" def __init__(self, hass: HomeAssistant, device: Device) -> None: @@ -117,3 +118,31 @@ class AirSendCover(CoverEntity): self._attr_current_cover_position = position self._closed = position == 0 self.async_write_ha_state() + + async def async_added_to_hass(self): + """Restore last known state when added to hass.""" + await super().async_added_to_hass() + + # Get the last known state + last_state = await self.async_get_last_state() + + if last_state: + # Restore position for covers with position support (type 4099) + if self._device.is_cover_with_position: + # Try to restore position from attributes + if last_state.attributes.get('current_position') is not None: + self._attr_current_cover_position = last_state.attributes['current_position'] + + # Override position based on state if fully open/closed + if last_state.state == 'closed': + self._attr_current_cover_position = 0 + elif last_state.state == 'open': + self._attr_current_cover_position = 100 + + # Restore closed/open state for all covers + if last_state.state == 'closed': + self._closed = True + elif last_state.state == 'open': + self._closed = False + self.async_write_ha_state() + # If no last_state, keep the defaults (50% for position covers) diff --git a/custom_components/airsend/device.py b/custom_components/airsend/device.py index 197854e..568ab52 100644 --- a/custom_components/airsend/device.py +++ b/custom_components/airsend/device.py @@ -13,8 +13,9 @@ RTYPE_LABELS = { 4096: "AirSend Button", 4097: "AirSend Switch", 4098: "AirSend Cover", - 4099: "AirSend Cover (position)", - 4100: "AirSend Light", + 4099: "AirSend Slider", + 4100: "AirSend Tilt", + 4101: "AirSend Light", } @@ -90,7 +91,7 @@ class Device: if self._channel: result = str(self._channel['id']) if result: - uniquefield = ['source', 'mac', 'seed'] + uniquefield = ['source', 'mac', 'seed', 'token'] for field in uniquefield: if field in self._channel: result += "_" @@ -145,7 +146,7 @@ class Device: @property def is_light(self) -> bool: - return self._rtype == 4100 + return self._rtype == 4101 @property def refresh_value(self) -> int: diff --git a/custom_components/airsend/manifest.json b/custom_components/airsend/manifest.json index cf94242..b27abe5 100644 --- a/custom_components/airsend/manifest.json +++ b/custom_components/airsend/manifest.json @@ -2,10 +2,11 @@ "domain": "airsend", "name": "AirSend", "documentation": "https://github.com/devmel/hass_airsend", + "issue_tracker": "https://github.com/devmel/hass_airsend/issues", "dependencies": ["http"], "config_flow": true, "codeowners": ["@devmel"], "requirements": [], - "version": "4.0", + "version": "4.0.0", "iot_class": "local_push" } diff --git a/custom_components/airsend/sensor.py b/custom_components/airsend/sensor.py index a5b2f4b..1394c32 100644 --- a/custom_components/airsend/sensor.py +++ b/custom_components/airsend/sensor.py @@ -6,6 +6,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.const import CONF_INTERNAL_URL, UnitOfTemperature, LIGHT_LUX @@ -51,7 +52,7 @@ async def async_setup_entry( async_add_entities(entities) -class AirSendAnySensor(SensorEntity): +class AirSendAnySensor(RestoreEntity, SensorEntity): """Generic AirSend sensor (type 1) — no coordinator, push only.""" def __init__(self, hass: HomeAssistant, device: Device, internal_url: str) -> None: @@ -83,6 +84,18 @@ class AirSendAnySensor(SensorEntity): def device_info(self) -> DeviceInfo: return self._device.device_info + @property + def native_value(self): + return self._state + + async def async_added_to_hass(self) -> None: + """Restore last known state when added to hass.""" + await super().async_added_to_hass() + last_state = await self.async_get_last_state() + if last_state is not None: + self._state = last_state.state + self.async_write_ha_state() + class AirSendTempSensor(CoordinatorEntity, SensorEntity): """AirSend device temperature sensor — uses shared coordinator.""" diff --git a/custom_components/airsend/switch.py b/custom_components/airsend/switch.py index 0c010c9..d50e9c7 100644 --- a/custom_components/airsend/switch.py +++ b/custom_components/airsend/switch.py @@ -8,6 +8,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.const import CONF_INTERNAL_URL from . import DOMAIN @@ -28,7 +29,7 @@ async def async_setup_entry( async_add_entities(entities) -class AirSendSwitch(SwitchEntity): +class AirSendSwitch(RestoreEntity, SwitchEntity): """Representation of an AirSend Switch.""" def __init__(self, hass: HomeAssistant, device: Device) -> None: @@ -91,3 +92,12 @@ class AirSendSwitch(SwitchEntity): if await self._send({"method": 1, "type": 0, "value": "OFF"}): self._state = False self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Restore last known state when added to hass.""" + await super().async_added_to_hass() + + last_state = await self.async_get_last_state() + if last_state: + self._state = last_state.state == 'on' + self.async_write_ha_state() \ No newline at end of file diff --git a/secrets.example.yaml b/secrets.example.yaml index 6e9b63a..58c22a9 100644 --- a/secrets.example.yaml +++ b/secrets.example.yaml @@ -2,6 +2,4 @@ # Use this file to store secrets like usernames and passwords. # Learn more at https://www.home-assistant.io/docs/configuration/secrets/ some_password: welcome -apiKey: 18xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx26 -spurl: sp://airsend_password@192.168.0.2 - +spurl: sp://airsend_password@[fe80::xxxx:xxxx:xxxx:xxxx]?gw=0&rhost=192.168.xxx.xxx