Skip to content

Commit

Permalink
fix: Use name + platform as entity key
Browse files Browse the repository at this point in the history
  • Loading branch information
davidrapan committed Dec 14, 2024
1 parent eb38110 commit 206de0e
Show file tree
Hide file tree
Showing 12 changed files with 47 additions and 42 deletions.
2 changes: 1 addition & 1 deletion custom_components/solarman/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanBinarySensorEntity(SolarmanEntity, BinarySensorEntity):
def __init__(self, coordinator, sensor):
SolarmanEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanEntity.__init__(self, coordinator, sensor)
self._sensor_inverted = False
if "inverted" in sensor and (inverted := sensor["inverted"]):
self._sensor_inverted = inverted
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanButtonEntity(SolarmanWritableEntity, ButtonEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)

self._value = 1
self._value_bit = None
Expand Down
4 changes: 4 additions & 0 deletions custom_components/solarman/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from typing import Any

from homeassistant.util import slugify
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo, format_mac

Expand Down Expand Up @@ -161,6 +162,9 @@ def modify(source: dict):
elif isinstance(source[i], dict):
modify(source[i])

if not "platform" in item:
item["platform"] = "sensor" if not "configurable" in item else "number"
item["key"] = slugify('_'.join(filter(None, (item["name"], item["platform"]))))
bulk_inherit(item, group, *(REQUEST_UPDATE_INTERVAL, REQUEST_CODE) if "registers" in item else REQUEST_UPDATE_INTERVAL)
if not REQUEST_CODE in item and (r := item.get("registers")) is not None and (addr := min(r)) is not None:
item[REQUEST_CODE] = table.get(addr, code)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

ATTR_ = { CONF_MOD: CONF_MOD, CONF_MPPT: CONF_MPPT, CONF_PHASE: "l" }

STATE_SENSORS = [{"name": "Connection", "artificial": "state", "platform": "binary_sensor"}, {"name": "Update Interval", "artificial": "interval"}]
STATE_SENSORS = [{"key": "Connection binary_sensor", "name": "Connection", "artificial": "state", "platform": "binary_sensor"}, {"key": "Update Interval sensor", "name": "Update Interval", "artificial": "interval", "platform": "sensor"}]

CONTROL_CODE = types.SimpleNamespace()
CONTROL_CODE.REQUEST = struct.pack("<H", 0x4510)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanDateTimeEntity(SolarmanWritableEntity, DateTimeEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)

self._time_zone = ZoneInfo(self.coordinator.hass.config.time_zone)
self._multiple_registers = len(self.registers) > 3 and self.registers[3] == self.registers[0] + 3
Expand Down
13 changes: 7 additions & 6 deletions custom_components/solarman/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import logging

from typing import Any
from datetime import date
from decimal import Decimal
from datetime import date, datetime, time

from homeassistant.util import slugify
from homeassistant.core import split_entity_id, callback
Expand Down Expand Up @@ -75,21 +75,22 @@ def set_state(self, state, value = None) -> bool:
return True

def update(self):
if (data := self.coordinator.data.get(self._attr_name)) and self.set_state(*data) and self.attributes:
if (data := self.coordinator.data.get(self._attr_key)) and self.set_state(*data) and self.attributes:
if "inverse" in self.attributes and self._attr_native_value:
self._attr_extra_state_attributes["−x"] = -self._attr_native_value
for attr in filter(lambda a: a in self.coordinator.data, self.attributes):
self._attr_extra_state_attributes[attr.replace(f"{self._attr_name} ", "")] = get_tuple(self.coordinator.data.get(attr))

class SolarmanEntity(SolarmanCoordinatorEntity):
def __init__(self, coordinator, sensor, platform):
def __init__(self, coordinator, sensor):
super().__init__(coordinator)

self._attr_key = sensor["key"]
self._attr_name = sensor["name"]
self._attr_has_entity_name = True
self._attr_device_class = sensor.get("class") or sensor.get("device_class")
self._attr_translation_key = sensor.get("translation_key") or slugify(self._attr_name)
self._attr_unique_id = slugify('_'.join(filter(None, (self.device_name, str(self.coordinator.inverter.config.serial), self._attr_name, platform))))
self._attr_unique_id = slugify('_'.join(filter(None, (self.device_name, str(self.coordinator.inverter.config.serial), self._attr_key))))
self._attr_entity_category = sensor.get("category") or sensor.get("entity_category")
self._attr_entity_registry_enabled_default = not "disabled" in sensor
self._attr_entity_registry_visible_default = not "hidden" in sensor
Expand Down Expand Up @@ -126,8 +127,8 @@ def _friendly_name_internal(self) -> str | None:
return f"{device_name} {name}"

class SolarmanWritableEntity(SolarmanEntity):
def __init__(self, coordinator, sensor, platform):
super().__init__(coordinator, sensor, platform)
def __init__(self, coordinator, sensor):
super().__init__(coordinator, sensor)

#self._write_lock = "locked" in sensor

Expand Down
4 changes: 2 additions & 2 deletions custom_components/solarman/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn

_LOGGER.debug(f"async_setup_entry: async_add_entities")

async_add_entities(create_entity(lambda x: SolarmanNumberEntity(coordinator, x), d) for d in descriptions if is_platform(d, _PLATFORM) or "configurable" in d)
async_add_entities(create_entity(lambda x: SolarmanNumberEntity(coordinator, x), d) for d in descriptions if is_platform(d, _PLATFORM))

return True

Expand All @@ -36,7 +36,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanNumberEntity(SolarmanWritableEntity, NumberEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)

if "mode" in sensor and (mode := sensor["mode"]):
self._attr_mode = mode
Expand Down
18 changes: 9 additions & 9 deletions custom_components/solarman/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def schedule_requests(self, runtime = 0):

for i in self._items:
if self.is_requestable(i) and self.is_scheduled(i, runtime):
self.set_state(i["name"], self.default_from_unit_of_measurement(i))
self.set_state(i["key"], self.default_from_unit_of_measurement(i))
if "registers" in i:
for r in sorted(i["registers"]):
if (register := (get_code(i, "read"), r)) and not register in registers:
Expand Down Expand Up @@ -270,7 +270,7 @@ def try_parse_unsigned(self, data, definition):
if "uint" in definition and value < 0:
value = 0

key = definition["name"]
key = definition["key"]

if "lookup" in definition:
self.set_state(key, lookup_value(value, definition["lookup"]), int(value))
Expand All @@ -293,7 +293,7 @@ def try_parse_signed(self, data, definition):
if definition.get("inverted"):
value = -value

key = definition["name"]
key = definition["key"]

if (validation := definition.get("validation")) is not None and not self.do_validate(key, value, validation):
if not "default" in validation:
Expand All @@ -312,7 +312,7 @@ def try_parse_ascii(self, data, definition):

value += chr(temp >> 8) + chr(temp & 0xFF)

self.set_state(definition["name"], value)
self.set_state(definition["key"], value)

def try_parse_bits(self, data, definition):
code = get_code(definition, "read")
Expand All @@ -324,7 +324,7 @@ def try_parse_bits(self, data, definition):

value.append(hex(temp))

self.set_state(definition["name"], value)
self.set_state(definition["key"], value)

def try_parse_version(self, data, definition):
code = get_code(definition, "read")
Expand All @@ -339,7 +339,7 @@ def try_parse_version(self, data, definition):
if (remove := definition.get("remove")) is not None:
value = value.replace(remove, "")

self.set_state(definition["name"], value)
self.set_state(definition["key"], value)

def try_parse_datetime(self, data, definition):
code = get_code(definition, "read")
Expand Down Expand Up @@ -376,7 +376,7 @@ def try_parse_datetime(self, data, definition):
try:
if not "platform" in definition:
value = datetime.strptime(value, DATETIME_FORMAT)
self.set_state(definition["name"], value)
self.set_state(definition["key"], value)
except Exception as e:
_LOGGER.debug(f"ParameterParser.try_parse_datetime: data: {data}, definition: {definition} [{format_exception(e)}]")

Expand Down Expand Up @@ -406,7 +406,7 @@ def try_parse_time(self, data, definition):
if i == 0 or (i == 1 and registers_count > 2):
value += ":"

self.set_state(definition["name"], value)
self.set_state(definition["key"], value)

def try_parse_raw(self, data, definition):
code = get_code(definition, "read")
Expand All @@ -418,4 +418,4 @@ def try_parse_raw(self, data, definition):

value.append(temp)

self.set_state(definition["name"], value)
self.set_state(definition["key"], value)
2 changes: 1 addition & 1 deletion custom_components/solarman/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanSelectEntity(SolarmanWritableEntity, SelectEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)

self.mask = display.get("mask") if (display := sensor.get("display")) else None

Expand Down
36 changes: 18 additions & 18 deletions custom_components/solarman/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn

_LOGGER.debug(f"async_setup_entry: async_add_entities")

async_add_entities(create_entity(lambda x: _create_entity(coordinator, x, config_entry.options), d) for d in descriptions if (is_platform(d, _PLATFORM) and not "configurable" in d))
async_add_entities(create_entity(lambda x: _create_entity(coordinator, x, config_entry.options), d) for d in descriptions if is_platform(d, _PLATFORM))

return True

Expand All @@ -66,7 +66,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanSensorEntity(SolarmanEntity, SensorEntity):
def __init__(self, coordinator, sensor):
super().__init__(coordinator, sensor, _PLATFORM)
super().__init__(coordinator, sensor)
if "state_class" in sensor and (state_class := sensor["state_class"]):
self._attr_state_class = state_class

Expand Down Expand Up @@ -125,45 +125,45 @@ def __init__(self, coordinator, sensor, battery_nominal_voltage, battery_life_cy
def update(self):
#super().update()
c = len(self.coordinator.data)
if c > 1 or (c == 1 and self._attr_name in self.coordinator.data):
match self._attr_name:
case "Battery SOH":
total_battery_charge = get_tuple(self.coordinator.data.get("Total Battery Charge"))
if c > 1 or (c == 1 and self._attr_key in self.coordinator.data):
match self._attr_key:
case "battery_soh_sensor":
total_battery_charge = get_tuple(self.coordinator.data.get("total_battery_charge_sensor"))
if total_battery_charge == 0:
self.set_state(100)
return
battery_capacity = get_tuple(self.coordinator.data.get("Battery Capacity"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("Battery Corrected Capacity"))
battery_capacity = get_tuple(self.coordinator.data.get("battery_capacity_number"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("battery_corrected_capacity_sensor"))
if battery_capacity and battery_corrected_capacity:
battery_capacity_5 = battery_capacity / 100 * 5
if battery_capacity - battery_capacity_5 <= battery_corrected_capacity <= battery_capacity + battery_capacity_5:
battery_capacity = battery_corrected_capacity
if total_battery_charge and battery_capacity and self._battery_nominal_voltage and self._battery_life_cycle_rating:
self.set_state(get_number(100 - total_battery_charge / get_battery_power_capacity(battery_capacity, self._battery_nominal_voltage) / (self._battery_life_cycle_rating * 0.05), self._digits))
case "Battery State":
battery_power = get_tuple(self.coordinator.data.get("Battery Power"))
case "battery_state_sensor":
battery_power = get_tuple(self.coordinator.data.get("battery_power_sensor"))
if battery_power:
self.set_state("discharging" if battery_power > 50 else "charging" if battery_power < -50 else "idle")
case "Today Battery Life Cycles":
today_battery_charge = get_tuple(self.coordinator.data.get("Today Battery Charge"))
case "today_battery_life_cycles_sensor":
today_battery_charge = get_tuple(self.coordinator.data.get("today_battery_charge_sensor"))
if today_battery_charge == 0:
self.set_state(0)
return
battery_capacity = get_tuple(self.coordinator.data.get("Battery Capacity"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("Battery Corrected Capacity"))
battery_capacity = get_tuple(self.coordinator.data.get("battery_capacity_number"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("battery_corrected_capacity_sensor"))
if battery_capacity and battery_corrected_capacity:
battery_capacity_5 = battery_capacity / 100 * 5
if battery_capacity - battery_capacity_5 <= battery_corrected_capacity <= battery_capacity + battery_capacity_5:
battery_capacity = battery_corrected_capacity
if today_battery_charge and battery_capacity and self._battery_nominal_voltage:
self.set_state(get_number(get_battery_cycles(today_battery_charge, battery_capacity, self._battery_nominal_voltage), self._digits))
case "Total Battery Life Cycles":
total_battery_charge = get_tuple(self.coordinator.data.get("Total Battery Charge"))
case "total_battery_life_cycles_sensor":
total_battery_charge = get_tuple(self.coordinator.data.get("total_battery_charge_sensor"))
if total_battery_charge == 0:
self.set_state(0)
return
battery_capacity = get_tuple(self.coordinator.data.get("Battery Capacity"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("Battery Corrected Capacity"))
battery_capacity = get_tuple(self.coordinator.data.get("battery_capacity_number"))
battery_corrected_capacity = get_tuple(self.coordinator.data.get("battery_corrected_capacity_sensor"))
if battery_capacity and battery_corrected_capacity:
battery_capacity_5 = battery_capacity / 100 * 5
if battery_capacity - battery_capacity_5 <= battery_corrected_capacity <= battery_capacity + battery_capacity_5:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanSwitchEntity(SolarmanWritableEntity, SwitchEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)
self._attr_device_class = SwitchDeviceClass.SWITCH

self._value_on = 1
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solarman/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def async_unload_entry(_: HomeAssistant, config_entry: ConfigEntry) -> boo

class SolarmanTimeEntity(SolarmanWritableEntity, TimeEntity):
def __init__(self, coordinator, sensor):
SolarmanWritableEntity.__init__(self, coordinator, sensor, _PLATFORM)
SolarmanWritableEntity.__init__(self, coordinator, sensor)

self._multiple_registers = len(self.registers) > 1 and self.registers[1] == self.registers[0] + 1
self._hex = "hex" in sensor
Expand Down

0 comments on commit 206de0e

Please sign in to comment.