This commit is contained in:
不求圣剑
2026-02-04 14:09:53 +08:00
parent 518ad6336e
commit fcf504df6f
2 changed files with 320 additions and 75 deletions

View File

@@ -68,6 +68,25 @@ SENSORS = {
"device_class": None, "device_class": None,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
# 电池能量统计
"battery_charge_energy": {
"json_key": "batChgEgy",
"name": "Battery Charge Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:battery-plus",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"battery_discharge_energy": {
"json_key": "batDisChgEgy",
"name": "Battery Discharge Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:battery-minus",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
# 太阳能 # 太阳能
"solar_power": { "solar_power": {
@@ -78,6 +97,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"solar_energy": {
"json_key": "pvEgy",
"name": "Solar Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-power",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"solar_power_pv1": { "solar_power_pv1": {
"json_key": "pv1", "json_key": "pv1",
"name": "Solar Power PV1", "name": "Solar Power PV1",
@@ -86,6 +114,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"solar_energy_pv1": {
"json_key": "pv1Egy",
"name": "Solar Energy PV1",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-panel",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"solar_power_pv2": { "solar_power_pv2": {
"json_key": "pv2", "json_key": "pv2",
"name": "Solar Power PV2", "name": "Solar Power PV2",
@@ -94,6 +131,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"solar_energy_pv2": {
"json_key": "pv2Egy",
"name": "Solar Energy PV2",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-panel",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"solar_power_pv3": { "solar_power_pv3": {
"json_key": "pv3", "json_key": "pv3",
"name": "Solar Power PV3", "name": "Solar Power PV3",
@@ -102,6 +148,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"solar_energy_pv3": {
"json_key": "pv3Egy",
"name": "Solar Energy PV3",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-panel",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"solar_power_pv4": { "solar_power_pv4": {
"json_key": "pv4", "json_key": "pv4",
"name": "Solar Power PV4", "name": "Solar Power PV4",
@@ -110,6 +165,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"solar_energy_pv4": {
"json_key": "pv4Egy",
"name": "Solar Energy PV4",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-panel",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
# 电网相关 # 电网相关
"grid_import_power": { # Grid -> System (outOngridPw) "grid_import_power": { # Grid -> System (outOngridPw)
@@ -120,6 +184,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"grid_import_energy": {
"json_key": "inOngridEgy",
"name": "Grid Import Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:transmission-tower-import",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"grid_export_power": { # System -> Grid/Home (inOngirdPw) "grid_export_power": { # System -> Grid/Home (inOngirdPw)
"json_key": "outOngridPw", "json_key": "outOngridPw",
"name": "Grid Export Power", "name": "Grid Export Power",
@@ -128,6 +201,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"grid_export_energy": {
"json_key": "outOngridEgy",
"name": "Grid Export Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:transmission-tower-export",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"max_output_power": { "max_output_power": {
"json_key": "maxOutPw", "json_key": "maxOutPw",
"name": "Max Output Power (OnGrid)", "name": "Max Output Power (OnGrid)",
@@ -146,6 +228,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"eps_output_energy": {
"json_key": "outEpsEgy",
"name": "EPS Output Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:power-plug",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"eps_input_power": { "eps_input_power": {
"json_key": "swEpsInPw", "json_key": "swEpsInPw",
"name": "EPS Input Power", "name": "EPS Input Power",
@@ -154,6 +245,15 @@ SENSORS = {
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
}, },
"eps_input_energy": {
"json_key": "inEpsEgy",
"name": "EPS Input Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:power-plug",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"eps_state": { "eps_state": {
"json_key": "swEpsState", "json_key": "swEpsState",
"name": "EPS State", "name": "EPS State",
@@ -245,7 +345,133 @@ SENSORS = {
"icon": "mdi:transmission-tower", "icon": "mdi:transmission-tower",
"device_class": SensorDeviceClass.POWER, "device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
} },
# 更多能量流向统计
"ac_to_battery_energy": {
"json_key": "acOtBatEgy",
"name": "AC to Battery Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:battery-arrow-up",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"pv_to_battery_energy": {
"json_key": "pvOtBatEgy",
"name": "PV to Battery Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-power-variant",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"pv_to_ac_energy": {
"json_key": "pvOtAcEgy",
"name": "PV to AC Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:solar-panel",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"pv_to_grid_energy": {
"json_key": "pvOtOngridEgy",
"name": "PV to Grid Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:transmission-tower-export",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"grid_to_ac_load_energy": {
"json_key": "ongridOtAcLoadEgy",
"name": "Grid to AC Load Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:home-import-outline",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"battery_to_ac_energy": {
"json_key": "batOtAcEgy",
"name": "Battery to AC Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:battery-arrow-down",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"battery_to_grid_energy": {
"json_key": "batOtGridEgy",
"name": "Battery to Grid Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:transmission-tower-export",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
"grid_to_battery_energy": {
"json_key": "ongridOtBatEgy",
"name": "Grid to Battery Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"icon": "mdi:battery-arrow-up",
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"scale": 0.01,
},
}
# 子设备传感器配置
SUBDEVICE_SENSORS = {
# 智能插座 (devType=6 or 1)
"plug": {
"power": {
"key": "outPw", # Fallback to 'power'
"name": "Power",
"unit": UnitOfPower.WATT,
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"icon": "mdi:power-socket-eu",
},
"energy": {
"key": "totalEgy",
"name": "Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"icon": "mdi:lightning-bolt",
"scale": 0.01,
},
},
# CT / Smart Meter (devType=2)
"ct": {
"power": {
"key": "TphasePw", # Fallback to sum of phases
"name": "Power",
"unit": UnitOfPower.WATT,
"device_class": SensorDeviceClass.POWER,
"state_class": SensorStateClass.MEASUREMENT,
"icon": "mdi:current-ac",
},
"energy_import": {
"key": "TphaseEgy", # Total Forward Active Energy
"name": "Energy Import",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"icon": "mdi:transmission-tower-import",
"scale": 0.01, # Assumption
},
"energy_export": {
"key": "TnphaseEgy", # Total Reverse Active Energy
"name": "Energy Export",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device_class": SensorDeviceClass.ENERGY,
"state_class": SensorStateClass.TOTAL_INCREASING,
"icon": "mdi:transmission-tower-export",
"scale": 0.01, # Assumption
},
},
} }
@@ -367,8 +593,25 @@ class JackeryDataCoordinator:
body = {} body = {}
# Merge logic # Merge logic
# Type 23: Statistical/Energy Data
if msg_code == 23 and isinstance(body, dict):
device_sn_in_body = body.get("deviceSn")
if device_sn_in_body == "system":
# Merge into main device cache
self._data_cache.update(body)
else:
# Find and update sub-device in cache
# Search in plugs and cts
for key in ["plugs", "plug", "cts"]:
items = self._data_cache.get(key)
if isinstance(items, list):
for item in items:
if item.get("sn") == device_sn_in_body or item.get("deviceSn") == device_sn_in_body:
item.update(body)
break
# Type 101: Sub-device full data # Type 101: Sub-device full data
if msg_code == 101 and isinstance(body, dict): elif msg_code == 101 and isinstance(body, dict):
# Normalize sub-device payloads for plugs/sockets/CTs # Normalize sub-device payloads for plugs/sockets/CTs
raw_plugs = body.get("plug") or body.get("plugs") or body.get("socket") or body.get("sockets") or [] raw_plugs = body.get("plug") or body.get("plugs") or body.get("socket") or body.get("sockets") or []
raw_cts = body.get("ct") or body.get("cts") or [] raw_cts = body.get("ct") or body.get("cts") or []
@@ -436,7 +679,7 @@ class JackeryDataCoordinator:
if not plugs or not isinstance(plugs, list): if not plugs or not isinstance(plugs, list):
plugs = data.get("cts") if isinstance(data.get("cts"), list) else None plugs = data.get("cts") if isinstance(data.get("cts"), list) else None
# 如果数据中根本没有 plugs/cts 字段,不做处理(避免在 Type 25 消息中误删) # 如果数据中根本没有 plugs/cts 字段,不做处理
if plugs is None: if plugs is None:
return return
@@ -449,29 +692,23 @@ class JackeryDataCoordinator:
now = time.time() now = time.time()
# 1. 更新 missing 状态 # 1. 更新 missing 状态
# A. 既然出现了,清除之前的缺失计时
for sn in current_sns: for sn in current_sns:
if sn in self._subdevice_missing_since: if sn in self._subdevice_missing_since:
_LOGGER.info(f"Sub-device {sn} reappeared, cancelling deletion.") _LOGGER.info(f"Sub-device {sn} reappeared, cancelling deletion.")
del self._subdevice_missing_since[sn] del self._subdevice_missing_since[sn]
# B. 检查已知但当前缺失的
for sn in self._known_plugs: for sn in self._known_plugs:
if sn not in current_sns: if sn not in current_sns:
if sn not in self._subdevice_missing_since: if sn not in self._subdevice_missing_since:
self._subdevice_missing_since[sn] = now self._subdevice_missing_since[sn] = now
_LOGGER.info(f"Sub-device {sn} missing, starting 60s deletion timer...") _LOGGER.info(f"Sub-device {sn} missing, starting 60s deletion timer...")
# 2. 执行真正的移除 (检查 missing 列表) # 2. 执行真正的移除
# 使用 list() 复制 keys允许在迭代中删除字典项
for sn in list(self._subdevice_missing_since.keys()): for sn in list(self._subdevice_missing_since.keys()):
# 如果该设备已不再已知列表里(可能已被删),清理记录并跳过
if sn not in self._known_plugs: if sn not in self._known_plugs:
del self._subdevice_missing_since[sn] del self._subdevice_missing_since[sn]
continue continue
# 只有当确实还在缺失状态(不在 current_sns时才检查时间
# (虽然上面的步骤 A 已经清理了出现的,但双重检查更稳妥)
if sn in current_sns: if sn in current_sns:
del self._subdevice_missing_since[sn] del self._subdevice_missing_since[sn]
continue continue
@@ -482,37 +719,42 @@ class JackeryDataCoordinator:
self._known_plugs.remove(sn) self._known_plugs.remove(sn)
del self._subdevice_missing_since[sn] del self._subdevice_missing_since[sn]
# 查找并删除相关实体 # Remove entities
keys_to_remove = []
for sensor_id, entity in list(self._sensors.items()): for sensor_id, entity in list(self._sensors.items()):
if sensor_id == f"plug_{sn}" or sensor_id == f"plug_switch_{sn}": # Match unique IDs containing the SN for sub-devices
keys_to_remove.append(sensor_id) # Format: jackery_plug_{sn}_xxx or jackery_ct_{sn}_xxx or jackery_plug_{sn}_switch
if f"_{sn}_" in sensor_id or sensor_id.endswith(f"_{sn}"):
self.hass.async_create_task(entity.async_remove(force_remove=True)) self.hass.async_create_task(entity.async_remove(force_remove=True))
# 3. 处理新增 # 3. 处理新增
new_entities = [] new_entities = []
new_switch_entities = [] new_switch_entities = []
for plug in plugs: for plug in plugs:
# Check SN key (could be 'sn' or 'deviceSn')
sn = plug.get("deviceSn") or plug.get("sn") sn = plug.get("deviceSn") or plug.get("sn")
dev_type = plug.get("devType") dev_type = plug.get("devType")
if dev_type is None and plug.get("subType") == 2: if dev_type is None and plug.get("subType") == 2:
dev_type = 2 dev_type = 2
# _LOGGER.debug(f"Checking sub-device: SN={sn}, Type={dev_type}")
if sn and sn not in self._known_plugs: if sn and sn not in self._known_plugs:
_LOGGER.info(f"Discovered new sub-device: {sn} (Type: {dev_type})") _LOGGER.info(f"Discovered new sub-device: {sn} (Type: {dev_type})")
self._known_plugs.add(sn) self._known_plugs.add(sn)
if hasattr(self, "config_entry_id"): if hasattr(self, "config_entry_id"):
entity = JackeryPlugSensor( # Create Sensors defined in SUBDEVICE_SENSORS
sensor_group = "ct" if dev_type == 2 else "plug"
group_config = SUBDEVICE_SENSORS.get(sensor_group, {})
for sensor_key, sensor_cfg in group_config.items():
entity = JackerySubDeviceSensor(
plug_sn=sn, plug_sn=sn,
dev_type=dev_type, dev_type=dev_type,
sensor_key=sensor_key,
sensor_config=sensor_cfg,
coordinator=self, coordinator=self,
config_entry_id=self.config_entry_id config_entry_id=self.config_entry_id
) )
new_entities.append(entity) new_entities.append(entity)
if dev_type != 2: if dev_type != 2:
from .switch import JackeryPlugSwitch from .switch import JackeryPlugSwitch
switch_entity = JackeryPlugSwitch( switch_entity = JackeryPlugSwitch(
@@ -942,6 +1184,10 @@ class JackerySensor(SensorEntity):
else: else:
self._attr_native_value = str(value) self._attr_native_value = str(value)
else: else:
scale = self._config.get("scale", 1)
try:
self._attr_native_value = float(value) * scale
except (TypeError, ValueError):
self._attr_native_value = value self._attr_native_value = value
self._attr_available = True self._attr_available = True
@@ -955,35 +1201,42 @@ class JackerySensor(SensorEntity):
} }
class JackeryPlugSensor(SensorEntity): class JackerySubDeviceSensor(SensorEntity):
"""Jackery Smart Plug / CT Sensor.""" """Jackery Smart Plug / CT Sub-device Sensor."""
def __init__( def __init__(
self, self,
plug_sn: str, plug_sn: str,
dev_type: int, dev_type: int,
sensor_key: str,
sensor_config: dict,
coordinator: JackeryDataCoordinator, coordinator: JackeryDataCoordinator,
config_entry_id: str, config_entry_id: str,
) -> None: ) -> None:
"""Initialize.""" """Initialize."""
self._plug_sn = plug_sn self._plug_sn = plug_sn
self._dev_type = dev_type self._dev_type = dev_type
self._sensor_key = sensor_key
self._sensor_config = sensor_config
self._coordinator = coordinator self._coordinator = coordinator
# Determine Name and Icon based on Type # Determine Device Name based on Type
if self._dev_type == 2: if self._dev_type == 2:
device_name = "CT" device_name = "CT"
icon = "mdi:current-ac"
else: else:
device_name = "Plug" device_name = "Plug"
icon = "mdi:power-socket-eu"
self._attr_name = f"{device_name} {plug_sn} Power" # Entity Name: "Power", "Energy", etc.
self._attr_native_unit_of_measurement = UnitOfPower.WATT self._attr_name = self._sensor_config["name"]
self._attr_icon = icon
self._attr_device_class = SensorDeviceClass.POWER self._attr_native_unit_of_measurement = self._sensor_config.get("unit")
self._attr_state_class = SensorStateClass.MEASUREMENT self._attr_icon = self._sensor_config.get("icon")
self._attr_unique_id = f"jackery_{device_name.lower()}_{plug_sn}_power" self._attr_device_class = self._sensor_config.get("device_class")
self._attr_state_class = self._sensor_config.get("state_class")
# Unique ID: jackery_ct_{sn}_power, jackery_plug_{sn}_energy, etc.
safe_key = self._sensor_key.replace("_", "") # e.g. energy_import -> energyimport
self._attr_unique_id = f"jackery_{device_name.lower()}_{plug_sn}_{safe_key}"
self._attr_has_entity_name = True self._attr_has_entity_name = True
self._attr_device_info = { self._attr_device_info = {
@@ -1001,10 +1254,10 @@ class JackeryPlugSensor(SensorEntity):
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
await super().async_added_to_hass() await super().async_added_to_hass()
# Register with coordinator using a unique ID format # Register with coordinator using a unique ID format
self._coordinator.register_sensor(f"plug_{self._plug_sn}", self) self._coordinator.register_sensor(self._attr_unique_id, self)
async def async_will_remove_from_hass(self) -> None: async def async_will_remove_from_hass(self) -> None:
self._coordinator.unregister_sensor(f"plug_{self._plug_sn}") self._coordinator.unregister_sensor(self._attr_unique_id)
await super().async_will_remove_from_hass() await super().async_will_remove_from_hass()
def _update_from_coordinator(self, data: dict) -> None: def _update_from_coordinator(self, data: dict) -> None:
@@ -1024,21 +1277,28 @@ class JackeryPlugSensor(SensorEntity):
# Store full raw data for attributes # Store full raw data for attributes
self._raw_data = dict(my_plug) self._raw_data = dict(my_plug)
# Update state (outPw / power) target_key = self._sensor_config.get("key")
try: val = my_plug.get(target_key)
# Try specific keys from protocol
# Protocol example: { "a": 12, ... } doesn't show power explicitly. # Fallback logic for specific keys if needed (like Power)
# Assuming 'outPw' or similar exists, or maybe 'p' or 'power'.
# Existing code used 'outPw'. Let's stick to it or add fallbacks if known.
if self._dev_type == 2:
val = my_plug.get("tPhasePw") or my_plug.get("TphasePw")
else:
val = my_plug.get("outPw")
if val is None: if val is None:
val = my_plug.get("power") # Common alternative if target_key == "outPw":
val = my_plug.get("power")
elif target_key == "TphasePw":
# Fallback sum calculation handled in coordinator usually, but check here too?
# Coordinator _calculate_energy_flow handles TphasePw calculation and puts it in 'cts' dict?
# No, _calculate_energy_flow modifies the MAIN data dict, not the individual items inside "cts" list usually.
# Wait, looking at _calculate_energy_flow: it iterates "cts" from data.get("cts"), calculates and modifies...
# actually it EXTRACTS values to local vars but doesn't explicitly write back to the list item unless I missed it.
# Let's check _calculate_energy_flow again. It reads from `ct_data = cts[0]`. It does NOT modify the list items.
# So fallback logic here is good.
pass
if val is not None: if val is not None:
self._attr_native_value = float(val) try:
native_val = float(val)
scale = self._sensor_config.get("scale", 1)
self._attr_native_value = native_val * scale
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
except (TypeError, ValueError): except (TypeError, ValueError):
@@ -1050,33 +1310,18 @@ class JackeryPlugSensor(SensorEntity):
return { return {
"plug_sn": self._plug_sn, "plug_sn": self._plug_sn,
"dev_type": self._dev_type, "dev_type": self._dev_type,
"raw_data": raw, "sensor_type": self._sensor_key,
# Normalized CT/plug fields (if present) # Normalized CT/plug fields (if present)
"sn": raw.get("sn") or raw.get("deviceSn"), "sn": raw.get("sn") or raw.get("deviceSn"),
"name": raw.get("name") or raw.get("scanName"), "name": raw.get("name") or raw.get("scanName"),
"commState": raw.get("commState"), "commState": raw.get("commState"),
"funForm": raw.get("funForm"),
"schePhase": raw.get("schePhase"),
# Plug fields # Plug fields
"inPw": raw.get("inPw"), "inPw": raw.get("inPw"),
"outPw": raw.get("outPw"), "outPw": raw.get("outPw"),
"sysSwitch": raw.get("sysSwitch") if raw.get("sysSwitch") is not None else raw.get("switchSta"), "sysSwitch": raw.get("sysSwitch") if raw.get("sysSwitch") is not None else raw.get("switchSta"),
"socketPri": raw.get("socketPri"),
"totalEgy": raw.get("totalEgy"), "totalEgy": raw.get("totalEgy"),
"AphasePw": raw.get("AphasePw") or raw.get("aPhasePw"), # CT Fields
"BphasePw": raw.get("BphasePw") or raw.get("bPhasePw"), "TphasePw": raw.get("TphasePw"),
"CphasePw": raw.get("CphasePw") or raw.get("cPhasePw"),
"TphasePw": raw.get("TphasePw") or raw.get("tPhasePw"),
"AnphasePw": raw.get("AnphasePw") or raw.get("anPhasePw"),
"BnphasePw": raw.get("BnphasePw") or raw.get("bnPhasePw"),
"CnphasePw": raw.get("CnphasePw") or raw.get("cnPhasePw"),
"TnphasePw": raw.get("TnphasePw") or raw.get("tnPhasePw"),
"AphaseEgy": raw.get("AphaseEgy"),
"BphaseEgy": raw.get("BphaseEgy"),
"CphaseEgy": raw.get("CphaseEgy"),
"TphaseEgy": raw.get("TphaseEgy"), "TphaseEgy": raw.get("TphaseEgy"),
"AnphaseEgy": raw.get("AnphaseEgy"),
"BnphaseEgy": raw.get("BnphaseEgy"),
"CnphaseEgy": raw.get("CnphaseEgy"),
"TnphaseEgy": raw.get("TnphaseEgy"), "TnphaseEgy": raw.get("TnphaseEgy"),
} }

View File

@@ -87,7 +87,7 @@ class JackeryPlugSwitch(SwitchEntity):
self._coordinator = coordinator self._coordinator = coordinator
self._raw_data = {} self._raw_data = {}
self._attr_name = f"Plug {plug_sn} Switch" self._attr_name = "Switch"
self._attr_unique_id = f"jackery_plug_{plug_sn}_switch" self._attr_unique_id = f"jackery_plug_{plug_sn}_switch"
self._attr_has_entity_name = True self._attr_has_entity_name = True