From b1b75d227c00e104a84b763386fcfffc94db2b6d Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:18:31 -0500 Subject: [PATCH 01/12] Add files via upload --- custom_components/vesync/__init__.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/custom_components/vesync/__init__.py b/custom_components/vesync/__init__.py index 41bb19f..2eb131e 100644 --- a/custom_components/vesync/__init__.py +++ b/custom_components/vesync/__init__.py @@ -59,8 +59,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b _LOGGER.error("Unable to login to the VeSync server") return False - forward_setup = hass.config_entries.async_forward_entry_setup - hass.data[DOMAIN] = {config_entry.entry_id: {}} hass.data[DOMAIN][config_entry.entry_id][VS_MANAGER] = manager @@ -92,14 +90,14 @@ async def async_update_data(): hass.data[DOMAIN][config_entry.entry_id][vs_p] = [] if device_dict[vs_p]: hass.data[DOMAIN][config_entry.entry_id][vs_p].extend(device_dict[vs_p]) - hass.async_create_task(forward_setup(config_entry, p)) + await hass.config_entries.async_forward_entry_setups(config_entry, [p.value]) async def async_new_device_discovery(service: ServiceCall) -> None: """Discover if new devices should be added.""" manager = hass.data[DOMAIN][config_entry.entry_id][VS_MANAGER] dev_dict = await async_process_devices(hass, manager) - def _add_new_devices(platform: str) -> None: + async def _add_new_devices(platform: str) -> None: """Add new devices to hass.""" old_devices = hass.data[DOMAIN][config_entry.entry_id][PLATFORMS[platform]] if new_devices := list( @@ -111,10 +109,10 @@ def _add_new_devices(platform: str) -> None: hass, VS_DISCOVERY.format(PLATFORMS[platform]), new_devices ) else: - hass.async_create_task(forward_setup(config_entry, platform)) + await hass.config_entries.async_forward_entry_setups(config_entry, [platform.value]) for k in PLATFORMS: - _add_new_devices(k) + await _add_new_devices(k) hass.services.async_register( DOMAIN, SERVICE_UPDATE_DEVS, async_new_device_discovery From 530fbd774f98da7265cc743da701f52a290dd0c7 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:19:14 -0500 Subject: [PATCH 02/12] Add files via upload From 9ecf69d46349ce33f74bdd4ecd7a42d55838c6e0 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:24:52 -0500 Subject: [PATCH 03/12] Refactor VeSync light handling and color temperature --- custom_components/vesync/light.py | 176 ++++++++++-------------------- 1 file changed, 58 insertions(+), 118 deletions(-) diff --git a/custom_components/vesync/light.py b/custom_components/vesync/light.py index 73d0160..7f7b6d4 100644 --- a/custom_components/vesync/light.py +++ b/custom_components/vesync/light.py @@ -3,9 +3,8 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, - ATTR_COLOR_TEMP, - COLOR_MODE_BRIGHTNESS, - COLOR_MODE_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, + ColorMode, LightEntity, ) from homeassistant.config_entries import ConfigEntry @@ -13,9 +12,13 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util.color import ( + color_temperature_kelvin_to_mired, + color_temperature_mired_to_kelvin, +) from .common import VeSyncDevice, has_feature -from .const import DEV_TYPE_TO_HA, DOMAIN, VS_DISCOVERY, VS_FAN_TYPES, VS_LIGHTS +from .const import DEV_TYPE_TO_HA, DOMAIN, VS_DISCOVERY, VS_LIGHTS _LOGGER = logging.getLogger(__name__) @@ -26,7 +29,6 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up lights.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] @callback @@ -62,25 +64,19 @@ def _setup_entities(devices, async_add_entities, coordinator): def _vesync_brightness_to_ha(vesync_brightness): try: - # check for validity of brightness value received brightness_value = int(vesync_brightness) except ValueError: - # deal if any unexpected/non numeric value _LOGGER.debug( "VeSync - received unexpected 'brightness' value from pyvesync api: %s", vesync_brightness, ) return None - # convert percent brightness to ha expected range return round((max(1, brightness_value) / 100) * 255) def _ha_brightness_to_vesync(ha_brightness): - # get brightness from HA data brightness = int(ha_brightness) - # ensure value between 1-255 brightness = max(1, min(brightness, 255)) - # convert to percent that vesync api expects brightness = round((brightness / 255) * 100) return max(1, min(brightness, 100)) @@ -88,126 +84,82 @@ def _ha_brightness_to_vesync(ha_brightness): class VeSyncBaseLight(VeSyncDevice, LightEntity): """Base class for VeSync Light Devices Representations.""" - def __init_(self, light, coordinator): - """Initialize the VeSync light device.""" - super().__init__(light, coordinator) - @property def brightness(self): """Get light brightness.""" - # get value from pyvesync library api, return _vesync_brightness_to_ha(self.device.brightness) def turn_on(self, **kwargs): """Turn the device on.""" attribute_adjustment_only = False - # set white temperature - if self.color_mode in (COLOR_MODE_COLOR_TEMP,) and ATTR_COLOR_TEMP in kwargs: - # get white temperature from HA data - color_temp = int(kwargs[ATTR_COLOR_TEMP]) - # ensure value between min-max supported Mireds - color_temp = max(self.min_mireds, min(color_temp, self.max_mireds)) - # convert Mireds to Percent value that api expects - color_temp = round( - ((color_temp - self.min_mireds) / (self.max_mireds - self.min_mireds)) - * 100 + + # Handle Color Temperature (Kelvin) + if self.color_mode == ColorMode.COLOR_TEMP and ATTR_COLOR_TEMP_KELVIN in kwargs: + kelvin = int(kwargs[ATTR_COLOR_TEMP_KELVIN]) + # Ensure within Kelvin bounds + kelvin = max(self.min_color_temp_kelvin, min(kelvin, self.max_color_temp_kelvin)) + + # VeSync API expects 0-100 percentage (0=Cold, 100=Warm) + # Map Kelvin to Percent + mireds = color_temperature_kelvin_to_mired(kelvin) + color_temp_pct = round( + ((mireds - self.min_mireds) / (self.max_mireds - self.min_mireds)) * 100 ) - # flip cold/warm to what pyvesync api expects - color_temp = 100 - color_temp - # ensure value between 0-100 - color_temp = max(0, min(color_temp, 100)) - # call pyvesync library api method to set color_temp - self.device.set_color_temp(color_temp) - # flag attribute_adjustment_only, so it doesn't turn_on the device redundantly + # Flip logic for pyvesync (100 - pct) + color_temp_pct = 100 - color_temp_pct + color_temp_pct = max(0, min(color_temp_pct, 100)) + + self.device.set_color_temp(color_temp_pct) attribute_adjustment_only = True - # set brightness level - if ( - self.color_mode in (COLOR_MODE_BRIGHTNESS, COLOR_MODE_COLOR_TEMP) - and ATTR_BRIGHTNESS in kwargs - ): - # get brightness from HA data + + # Handle Brightness + if ATTR_BRIGHTNESS in kwargs: brightness = _ha_brightness_to_vesync(kwargs[ATTR_BRIGHTNESS]) self.device.set_brightness(brightness) - # flag attribute_adjustment_only, so it doesn't turn_on the device redundantly attribute_adjustment_only = True - # check flag if should skip sending the turn_on command + if attribute_adjustment_only: return - # send turn_on command to pyvesync api + self.device.turn_on() -class VeSyncDimmableLightHA(VeSyncBaseLight, LightEntity): +class VeSyncDimmableLightHA(VeSyncBaseLight): """Representation of a VeSync dimmable light device.""" - def __init__(self, device, coordinator) -> None: - """Initialize the VeSync dimmable light device.""" - super().__init__(device, coordinator) - - @property - def color_mode(self): - """Set color mode for this entity.""" - return COLOR_MODE_BRIGHTNESS - - @property - def supported_color_modes(self): - """Flag supported color_modes (in an array format).""" - return [COLOR_MODE_BRIGHTNESS] + _attr_color_mode = ColorMode.BRIGHTNESS + _attr_supported_color_modes = {ColorMode.BRIGHTNESS} -class VeSyncTunableWhiteLightHA(VeSyncBaseLight, LightEntity): +class VeSyncTunableWhiteLightHA(VeSyncBaseLight): """Representation of a VeSync Tunable White Light device.""" - def __init__(self, device, coordinator) -> None: - """Initialize the VeSync Tunable White Light device.""" - super().__init__(device, coordinator) + _attr_color_mode = ColorMode.COLOR_TEMP + _attr_supported_color_modes = {ColorMode.COLOR_TEMP} @property - def color_temp(self): - """Get device white temperature.""" - # get value from pyvesync library api, + def color_temp_kelvin(self): + """Get device white temperature in Kelvin.""" result = self.device.color_temp_pct try: - # check for validity of brightness value received - color_temp_value = int(result) - except ValueError: - # deal if any unexpected/non numeric value - _LOGGER.debug( - "VeSync - received unexpected 'color_temp_pct' value from pyvesync api: %s", - result, - ) - return 0 - # flip cold/warm - color_temp_value = 100 - color_temp_value - # ensure value between 0-100 - color_temp_value = max(0, min(color_temp_value, 100)) - # convert percent value to Mireds - color_temp_value = round( - self.min_mireds - + ((self.max_mireds - self.min_mireds) / 100 * color_temp_value) - ) - # ensure value between minimum and maximum Mireds - return max(self.min_mireds, min(color_temp_value, self.max_mireds)) + pct = int(result) + except (ValueError, TypeError): + return self.min_color_temp_kelvin + + # Convert percent back to Mireds + pct = 100 - pct + mireds = self.min_mireds + ((self.max_mireds - self.min_mireds) / 100 * pct) + return color_temperature_mired_to_kelvin(mireds) @property - def min_mireds(self): - """Set device coldest white temperature.""" - return 154 # 154 Mireds ( 1,000,000 divided by 6500 Kelvin = 154 Mireds) - + def min_mireds(self): return 154 # 6500K @property - def max_mireds(self): - """Set device warmest white temperature.""" - return 370 # 370 Mireds ( 1,000,000 divided by 2700 Kelvin = 370 Mireds) + def max_mireds(self): return 370 # 2700K @property - def color_mode(self): - """Set color mode for this entity.""" - return COLOR_MODE_COLOR_TEMP - + def min_color_temp_kelvin(self): return 2700 @property - def supported_color_modes(self): - """Flag supported color_modes (in an array format).""" - return [COLOR_MODE_COLOR_TEMP] + def max_color_temp_kelvin(self): return 6500 class VeSyncNightLightHA(VeSyncDimmableLightHA): @@ -217,59 +169,47 @@ def __init__(self, device, coordinator) -> None: """Initialize the VeSync device.""" super().__init__(device, coordinator) self.device = device - self.has_brightness = has_feature( - self.device, "details", "night_light_brightness" - ) + self.has_brightness = has_feature(self.device, "details", "night_light_brightness") @property def unique_id(self): - """Return the ID of this device.""" return f"{super().unique_id}-night-light" @property def name(self): - """Return the name of the device.""" return f"{super().name} night light" @property def brightness(self): - """Get night light brightness.""" - return ( - _vesync_brightness_to_ha(self.device.details["night_light_brightness"]) - if self.has_brightness - else {"on": 255, "dim": 125, "off": 0}[self.device.details["night_light"]] - ) + if self.has_brightness: + return _vesync_brightness_to_ha(self.device.details["night_light_brightness"]) + return {"on": 255, "dim": 125, "off": 0}.get(self.device.details["night_light"], 0) @property def is_on(self): - """Return True if night light is on.""" if has_feature(self.device, "details", "night_light"): return self.device.details["night_light"] in ["on", "dim"] if self.has_brightness: - return self.device.details["night_light_brightness"] > 0 + return self.device.details.get("night_light_brightness", 0) > 0 + return False @property def entity_category(self): - """Return the configuration entity category.""" return EntityCategory.CONFIG def turn_on(self, **kwargs): - """Turn the night light on.""" - if self.device._config_dict["module"] in VS_FAN_TYPES: + if self.device._config_dict.get("module") in VS_FAN_TYPES: if ATTR_BRIGHTNESS in kwargs and kwargs[ATTR_BRIGHTNESS] < 255: self.device.set_night_light("dim") else: self.device.set_night_light("on") elif ATTR_BRIGHTNESS in kwargs: - self.device.set_night_light_brightness( - _ha_brightness_to_vesync(kwargs[ATTR_BRIGHTNESS]) - ) + self.device.set_night_light_brightness(_ha_brightness_to_vesync(kwargs[ATTR_BRIGHTNESS])) else: self.device.set_night_light_brightness(100) def turn_off(self, **kwargs): - """Turn the night light off.""" - if self.device._config_dict["module"] in VS_FAN_TYPES: + if self.device._config_dict.get("module") in VS_FAN_TYPES: self.device.set_night_light("off") else: self.device.set_night_light_brightness(0) From 322151419f8a1790a68b7f7b4de0a8f15ff1c98c Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:29:13 -0500 Subject: [PATCH 04/12] Update DHCP import and type annotation --- custom_components/vesync/config_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/vesync/config_flow.py b/custom_components/vesync/config_flow.py index 7b2aa33..7a9b5c1 100644 --- a/custom_components/vesync/config_flow.py +++ b/custom_components/vesync/config_flow.py @@ -6,10 +6,10 @@ import voluptuous as vol from homeassistant import config_entries -from homeassistant.components import dhcp from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo from .const import DOMAIN @@ -66,7 +66,7 @@ async def async_step_user(self, user_input=None): else self._show_form(errors={"base": "invalid_auth"}) ) - async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: + async def async_step_dhcp(self, discovery_info: DhcpServiceInfo) -> FlowResult: """Handle DHCP discovery.""" hostname = discovery_info.hostname From 59402e269fcd3db3f8b5dabc552010f333ea52e5 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:30:28 -0500 Subject: [PATCH 05/12] Change log level from warning to debug --- custom_components/vesync/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/vesync/common.py b/custom_components/vesync/common.py index 96ba099..364f1dd 100644 --- a/custom_components/vesync/common.py +++ b/custom_components/vesync/common.py @@ -49,7 +49,7 @@ async def async_process_devices(hass, manager): ["cid", "uuid", "mac_id"], ) - _LOGGER.warning( + _LOGGER.debug( "Found the following devices: %s", redacted, ) From cc51489b4f6d988a89b7e997f7e91cca90953044 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:38:33 -0500 Subject: [PATCH 06/12] Update version, codeowner and documentation links in manifest --- custom_components/vesync/manifest.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/custom_components/vesync/manifest.json b/custom_components/vesync/manifest.json index 1a041b6..63c66c1 100644 --- a/custom_components/vesync/manifest.json +++ b/custom_components/vesync/manifest.json @@ -1,7 +1,10 @@ { "domain": "vesync", "name": "VeSync", + "version": "1.4.0" + "integration_type": "hub", "codeowners": [ + "@jdaleo23", "@markperdue", "@webdjoe", "@thegardenmonkey", @@ -15,11 +18,10 @@ "macaddress": "*" } ], - "documentation": "https://github.com/micahqcade/custom_vesync", + "documentation": "https://github.com/jdaleo23/custom_vesync", "iot_class": "cloud_polling", - "issue_tracker": "https://github.com/micahqcade/custom_vesync", + "issue_tracker": "https://github.com/jdaleo23/custom_vesync", "requirements": [ "pyvesync==2.1.12" ], - "version": "1.3.2" -} \ No newline at end of file +} From 14f543d242cdef6e82bb51cddd39f35a64b9ace9 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:45:02 -0500 Subject: [PATCH 07/12] Fix formatting in manifest.json --- custom_components/vesync/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/vesync/manifest.json b/custom_components/vesync/manifest.json index 63c66c1..aac2705 100644 --- a/custom_components/vesync/manifest.json +++ b/custom_components/vesync/manifest.json @@ -1,7 +1,7 @@ { "domain": "vesync", "name": "VeSync", - "version": "1.4.0" + "version": "1.4.0", "integration_type": "hub", "codeowners": [ "@jdaleo23", From b35d8d30df899209d21f810ced0743db66c98564 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:58:18 -0500 Subject: [PATCH 08/12] Update requirements in manifest.json --- custom_components/vesync/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/vesync/manifest.json b/custom_components/vesync/manifest.json index aac2705..88d3464 100644 --- a/custom_components/vesync/manifest.json +++ b/custom_components/vesync/manifest.json @@ -23,5 +23,5 @@ "issue_tracker": "https://github.com/jdaleo23/custom_vesync", "requirements": [ "pyvesync==2.1.12" - ], + ] } From 75c1dbc3525d7e6509f621862764097dfc5af9b1 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:16:52 -0500 Subject: [PATCH 09/12] Refactor supported_features and add turn_off method --- custom_components/vesync/fan.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/custom_components/vesync/fan.py b/custom_components/vesync/fan.py index a9b7f06..e015887 100644 --- a/custom_components/vesync/fan.py +++ b/custom_components/vesync/fan.py @@ -85,13 +85,19 @@ def __init__(self, fan, coordinator) -> None: self._speed_range = (1, 3) @property - def supported_features(self): + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - return ( - FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE - if self.speed_count > 1 - else FanEntityFeature.SET_SPEED - ) + # Start with power features which are now required for the turn_on/off actions to work + features = FanEntityFeature.TURN_ON | FanEntityFeature.TURN_OFF + + # Add speed features if applicable + features |= FanEntityFeature.SET_SPEED + + # Add preset features if more than one speed exists + if self.speed_count > 1: + features |= FanEntityFeature.PRESET_MODE + + return features @property def percentage(self): @@ -150,7 +156,7 @@ def set_preset_mode(self, preset_mode): """Set the preset mode of device.""" if preset_mode not in self.preset_modes: raise ValueError( - "{preset_mode} is not one of the valid preset modes: {self.preset_modes}" + f"{preset_mode} is not one of the valid preset modes: {self.preset_modes}" ) if not self.smartfan.is_on: @@ -169,7 +175,6 @@ def set_preset_mode(self, preset_mode): def turn_on( self, - # speed: str | None = None, percentage: int | None = None, preset_mode: str | None = None, **kwargs, @@ -181,3 +186,8 @@ def turn_on( if percentage is None: percentage = 50 self.set_percentage(percentage) + + def turn_off(self, **kwargs) -> None: + """Turn the device off.""" + self.smartfan.turn_off() + self.schedule_update_ha_state() From d75c19cb65ac095c61f962c8e49199c933a09c73 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:17:14 -0500 Subject: [PATCH 10/12] Bump version from 1.4.0 to 1.4.1 --- custom_components/vesync/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/vesync/manifest.json b/custom_components/vesync/manifest.json index 88d3464..eef2507 100644 --- a/custom_components/vesync/manifest.json +++ b/custom_components/vesync/manifest.json @@ -1,7 +1,7 @@ { "domain": "vesync", "name": "VeSync", - "version": "1.4.0", + "version": "1.4.1", "integration_type": "hub", "codeowners": [ "@jdaleo23", From 49989da6eb211b296ca27694faa44d66706963c0 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:41:37 -0500 Subject: [PATCH 11/12] Revise README with updated project info and badges Updated project description and badge links in README.md. Removed TODO list and contributing section. --- README.md | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ee15c0e..2e780c7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # **Important message** > -> This a fork of the existing archived project created by vlebourl. Please contribute here. +> This is a fork of the existing project most recently managed by by micahqcade, however it hasn't had activity in ~2 years. I'm personally using this one, feel free to contribute here. [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) -[![GitHub release](https://img.shields.io/github/v/release/vlebourl/custom_vesync.svg)](https://GitHub.com/vlebourl/custom_vesync/releases/) +[![GitHub release](https://img.shields.io/github/v/release/jdaleo23/custom_vesync.svg)](https://GitHub.com/jdaleo23/custom_vesync/releases/) +# Legacy Information... # VeSync custom component for Home Assistant Custom component for Home Assistant to interact with smart devices via the VeSync platform. @@ -44,22 +45,3 @@ logger: custom_components.vesync: debug pyvesync: debug ``` - -## TODO LIST -``` -- [x] Air Fryer Properties (AirFryer158) -- [ ] Air Fryer Methods -- [ ] Create the Card -``` - -### Contributing - -All contributions are very welcomed! -Please make sure to install `pre-commit` and run the pre-commit hook before submitting a PR. - -```sh -pip install pre-commit -pre-commit install -pre-commit run --all-files -``` - From d8965ac5c9b12ae8ada734f37cb4f69377d0a150 Mon Sep 17 00:00:00 2001 From: jdaleo23 <153868428+jdaleo23@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:42:17 -0500 Subject: [PATCH 12/12] Fix typo in README about project activity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e780c7..5e6e63b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # **Important message** > -> This is a fork of the existing project most recently managed by by micahqcade, however it hasn't had activity in ~2 years. I'm personally using this one, feel free to contribute here. +> This is a fork of the existing project most recently managed by micahqcade, however it doesn't appear to have had activity in ~2 years. I'm personally using this one, feel free to contribute here. [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) [![GitHub release](https://img.shields.io/github/v/release/jdaleo23/custom_vesync.svg)](https://GitHub.com/jdaleo23/custom_vesync/releases/)