HYXiPOWER Integration - Energy - Home Assistant Community
I have a HYXiPOWER battery and inverter, but I noticed that there’s not an integration available for HomeAssistant yet. Anyone know of a HACS integration that can connect? Or anyone willing to make one? I noticed that t…
HYXiPOWER Integration - Energy - Home Assistant Community = 40rem)" rel="stylesheet" data-target="desktop" /> = 40rem)" rel="stylesheet" data-target="chat_desktop" /> = 40rem)" rel="stylesheet" data-target="discourse-ai_desktop" /> = 40rem)" rel="stylesheet" data-target="discourse-reactions_desktop" /> = 40rem)" rel="stylesheet" data-target="poll_desktop" /> = 40rem)" rel="stylesheet" data-target="desktop_theme" data-theme-id="19" data-theme-name="scroll to top"/> HYXiPOWER Integration Energy Veldkornet August 31, 2025, 4:48pm 1 I have a HYXiPOWER battery and inverter, but I noticed that there’s not an integration available for HomeAssistant yet. Anyone know of a HACS integration that can connect? Or anyone willing to make one? I noticed that there is a public REST api available, if it’s also for the batteries, I’m not sure… https://open.hyxicloud.com/#/document Maybe MODBUS is the best option? Veldkornet August 31, 2025, 6:58pm 2 Created a feature request here. limonade67 (Limonade67) September 1, 2025, 4:33pm 3 Hello, I’m writing from France. Sorry for my bad Englsih. I’m also interested in a HyxiPower Integration. I tried to do it my self with the help of Copilot and Gemini, but I Could not request with postman. I gave up. limonade67 (Limonade67) January 30, 2026, 9:10am 4 With the help of two AIs (Perplexity and Claude), I successfully integrated the Hyxi cloud into Home Assistant. It took me over 60 hours. Three-quarters of that time was wasted because, as a total beginner, I let the AIs lead me in the wrong directions. image506×302 13.7 KB Veldkornet January 30, 2026, 11:38am 5 Oooh nice! Would you mind posting the config for the rest of us? limonade67 (Limonade67) February 13, 2026, 3:29pm 7 Hello, Sorry for the delay. I posted it here : GitHub - limonade67/Hyxi-Power-API-for-Home-assistant: Intégration AppDaemon pour connecter vos micro-onduleurs HYXi (via l'API open.hyxicloud.com) à Home Assistant. Veldkornet February 13, 2026, 4:53pm 8 Ah man, I can’t figure out this appdaemon thing, the directories etc don’t exist as you describe. Don’t suppose you can convert it to a HACS add-on? Or just post the REST config needed? Veldkornet February 19, 2026, 9:21pm 9 I got it working! I figured out that the files should be in /addon_configs/a0d7b954_appdaemon/apps/ when using the appdaemon addon, then also I needed to change the sensor completely, I specifically need it for the battery which I guess you don’t use. EDIT: I’ve updated it to automatically discover the plants on your account, and then get all devices in that plant. Currently though, I only pull in type of “hybrid inverter”, because I don’t have anything else to test with… hyxi_cloud.py: # -*- coding: utf-8 -*- import appdaemon.plugins.hass.hassapi as hass import requests import hmac import hashlib import base64 import time import json class HyxiCloud(hass.Hass): def initialize(self): """Dynamic Discovery for HYXi Cloud Systems""" self.log("=" * 60) self.log("HYXi Cloud - Dynamic Multi-Plant Monitoring") self.log("=" * 60) # 1. Configuration self.base_url = self.args.get("base_url", "https://open.hyxicloud.com") self.access_key = self.args["access_key"] self.secret_key = self.args["secret_key"] self.poll_interval = int(self.args.get("poll_interval", 60)) # 2. Runtime State self.token = None self.token_expires_at = 0 self.discovered_devices = [] # 3. Start Discovery self.run_in(self.setup_and_poll, 1) def _generate_headers(self, path, method, use_body_hash=False): timestamp = str(int(time.time() * 1000)) nonce = format(int(time.time() * 1000), "x")[-8:] content_str = "grantType:1" if (use_body_hash and path == "/api/authorization/v1/token") else "" hex_hash = hashlib.sha512(content_str.encode("utf-8")).hexdigest() string_to_sign = f"{path}\n{method.upper()}\n{hex_hash}\n" token_str = self.token if self.token else "" sign_string = f"{self.access_key}{token_str}{timestamp}{nonce}{string_to_sign}" hmac_bytes = hmac.new(self.secret_key.encode("utf-8"), sign_string.encode("utf-8"), hashlib.sha512).digest() return { "AccessKey": self.access_key, "Timestamp": timestamp, "Nonce": nonce, "Sign": base64.b64encode(hmac_bytes).decode("utf-8"), "Authorization": token_str, "Content-Type": "application/json;charset=utf-8" } def _refresh_token(self): path = "/api/authorization/v1/token" headers = self._generate_headers(path, "POST", use_body_hash=True) try: r = requests.post(f"{self.base_url}{path}", json={"grantType": 1}, headers=headers, timeout=10) res = r.json() if res.get("success") and "data" in res: token_val = res["data"].get("token") or res["data"].get("access_token") if token_val: self.token = f"Bearer {token_val}" self.token_expires_at = time.time() + 7200 self.log("Token Refreshed Successfully") return True except Exception as e: self.log(f"Token error: {e}", level="ERROR") return False def setup_and_poll(self, kwargs): if not self._refresh_token(): self.run_in(self.setup_and_poll, 30) return self.discover_assets() self.poll_cycle({}) self.run_every(self.poll_cycle, "now", self.poll_interval) def discover_assets(self): self.discovered_devices = [] p_path = "/api/plant/v1/page" try: r = requests.post(f"{self.base_url}{p_path}", json={"pageSize": 10, "currentPage": 1}, headers=self._generate_headers(p_path, "POST")) plants = r.json().get("data", {}).get("list", []) for p in plants: p_id = p["plantId"] p_name_raw = p["plantName"] p_name_slug = p_name_raw.replace(" ", "_").lower() d_path = "/api/plant/v1/devicePage" dr = requests.post(f"{self.base_url}{d_path}", json={"plantId": p_id, "pageSize": 50, "currentPage": 1}, headers=self._generate_headers(d_path, "POST")) for d in dr.json().get("data", {}).get("deviceList", []): if d["deviceType"] == "HYBRID_INVERTER": self.discovered_devices.append({ "plant_raw": p_name_raw, "plant_slug": p_name_slug, "sn": d["deviceSn"], "name_raw": d["deviceName"], "name_slug": d["deviceName"].replace(" ", "_").lower() }) self.log(f"Discovered {p_name_raw} Inverter: {d['deviceSn']}") except Exception as e: self.log(f"Discovery Error: {e}", level="ERROR") def poll_cycle(self, kwargs): if time.time() > self.token_expires_at: self._refresh_token() for dev in self.discovered_devices: self._update_device(dev) def _update_device(self, dev): path = "/api/device/v1/queryDeviceData" url = f"{self.base_url}{path}?deviceSn={dev['sn']}" try: r = requests.get(url, headers=self._generate_headers(path, "GET"), timeout=10) data = r.json().get("data", []) if not data: return vals = {item["dataKey"]: item["dataValue"] for item in data} def get_f(key, mult=1.0): try: return round(float(vals.get(key, 0)) * mult, 2) except: return 0.0 # --- SETUP NAMING & IDS (COMPLIANT) --- sn = dev['sn'] prefix = f"sensor.hyxi_{dev['plant_slug']}" label_prefix = f"HYXi {dev['plant_raw']}" # --- CALCULATIONS --- soc = get_f("batSoc") pbat = get_f("pbat") solar_p = get_f("ppv") grid_w = get_f("gridP", 1000.0) # kW to W conversion load = get_f("ph1Loadp") + get_f("ph2Loadp") + get_f("ph3Loadp") # Icon Logic icon_step = min(90, max(10, int(soc / 10) * 10)) batt_icon = f"mdi:battery-charging-{icon_step}" if pbat < -10 else f"mdi:battery-{icon_step}" # --- PUSH TO HA (POWER) --- self._set_ha(f"{prefix}_battery_soc", soc, f"{label_prefix} Battery SOC", "%", "battery", f"{sn}-soc", batt_icon) self._set_ha(f"{prefix}_battery_power", pbat, f"{label_prefix} Battery Power", "W", "power", f"{sn}-pbat", batt_icon) self._set_ha(f"{prefix}_solar", solar_p, f"{label_prefix} Solar", "W", "power", f"{sn}-pv", "mdi:solar-power") self._set_ha(f"{prefix}_load", load, f"{label_prefix} Home Load", "W", "power", f"{sn}-load", "mdi:home-lightning-bolt") self._set_ha(f"{prefix}_grid_import", abs(grid_w) if grid_w < 0 else 0, f"{label_prefix} Grid Import", "W", "power", f"{sn}-grid-imp", "mdi:transmission-tower-import") self._set_ha(f"{prefix}_grid_export", grid_w if grid_w > 0 else 0, f"{label_prefix} Grid Export", "W", "power", f"{sn}-grid-exp", "mdi:transmission-tower-export") self._set_ha(f"{prefix}_battery_charging", abs(pbat) if pbat < 0 else 0, f"{label_prefix} Battery Charging", "W", "power", f"{sn}-bat-chg", "mdi:battery-arrow-up") self._set_ha(f"{prefix}_battery_discharging", pbat if pbat > 0 else 0, f"{label_prefix} Battery Discharging", "W", "power", f"{sn}-bat-dis", "mdi:battery-arrow-down") # --- PUSH TO HA (ENERGY) --- self._set_ha(f"{prefix}_total_yield", get_f("totalE"), f"{label_prefix} Lifetime Yield", "kWh", "energy", f"{sn}-yield", "mdi:solar-panel") self._set_ha(f"{prefix}_bat_charge_total", get_f("batCharge"), f"{label_prefix} Total Battery Charge", "kWh", "energy", f"{sn}-chg-tot", "mdi:battery-plus") self._set_ha(f"{prefix}_bat_discharge_total", get_f("batDisCharge"), f"{label_prefix} Total Battery Discharge", "kWh", "energy", f"{sn}-dis-tot", "mdi:battery-minus") # --- DIAGNOSTICS --- self._set_ha(f"{prefix}_battery_soh", get_f("batSoh"), f"{label_prefix} Battery SOH", "%", None, f"{sn}-soh", "mdi:heart-pulse") self._set_ha(f"{prefix}_temp", get_f("tinv"), f"{label_prefix} Inverter Temperature", "°C", "temperature", f"{sn}-temp") # --- LAST SEEN --- ts = get_f("collectTime") if ts > 0: readable = time.strftime('%H:%M:%S', time.localtime(ts)) self._set_ha(f"{prefix}_last_seen", readable, f"{label_prefix} Last Sync", None, None, f"{sn}-sync", "mdi:clock-check-outline") except Exception as e: self.log(f"Update failed: {e}", level="ERROR") def _set_ha(self, entity_id, value, friendly_name, unit, dev_class, uid, icon=None): """Sets HA state with Unique ID for UI management and state_class for statistics""" attributes = { "friendly_name": friendly_name, "unique_id": uid } if unit: attributes["unit_of_measurement"] = unit if dev_class: attributes["device_class"] = dev_class if icon: attributes["icon"] = icon # Enable history and Energy Dashboard compatibility if unit == "kWh": attributes["state_class"] = "total_increasing" elif unit in ["W", "%", "°C"]: attributes["state_class"] = "measurement" self.set_state(entity_id, state=str(value), attributes=attributes) def terminate(self): """Cleanup logic for graceful reloads""" self.log("Shutting down HYXi monitoring...") Veldkornet February 21, 2026, 10:34pm 10 While that appdaemon thing works, it was limited in what it could do, so I ended up making a HACS integration: GitHub - Veldkornet/ha-hyxi-cloud: HYXI Cloud HACS Integration paco06 February 25, 2026, 5:23pm 11 I tested the HACS integration with microinverters and it works correctly. It displays solar power and total energy yield. I also have a meter, but I don’t see it in the integration. Is there any way to integrate production and sales information with the Home Assistant energy display? Regards. Veldkornet February 25, 2026, 6:01pm 12 Hey! Great, glad to hear that! I did put in support for all types, including meter since that information is available on their API guide, but I don’t know what response is returned to know what types of sensors should be added (as I don’t have one myself). I only have a HYBRID_INVERTER with connected BATTERY, so that’s all I could properly test… But, if things work how they should… if you enable debug mode it should print out all the values in the logs when it polls (because I’m not mapping them anywhere), and then I can add them for you! I’ll push version 1.2.2 in a few minutes, and this should enable the logging. You can log an issue in GitHub with all the information and then I’ll get it added. How to Enable Debug Logging Go to Settings > Devices & Services . Find the HYXi Cloud integration card. Click the three vertical dots (⋮) in the bottom right corner of that card. Select Enable debug logging. Note: A small “bug” icon will appear on the card to show it’s active. Wait for 5–10 minutes to ensure the integration has performed at least one or two “fetch” cycles (since your interval is 5 minutes). Go back to the same menu (three dots) and click Disable debug logging. If you don’t see the “Enable Debug Logging”, check that…