diff --git a/custom_components/senec/__init__.py b/custom_components/senec/__init__.py index b0b2c94..3f5f624 100644 --- a/custom_components/senec/__init__.py +++ b/custom_components/senec/__init__.py @@ -26,6 +26,7 @@ SENEC_SECTION_PM1OBJ2, SENEC_SECTION_PV1, SENEC_SECTION_PWR_UNIT, + SENEC_SECTION_SOCKETS, SENEC_SECTION_TEMPMEASURE, SENEC_SECTION_WALLBOX ) @@ -56,9 +57,12 @@ MAIN_SENSOR_TYPES, MAIN_BIN_SENSOR_TYPES, + MAIN_SWITCH_TYPES, + MAIN_NUMBER_SENSOR_TYPES, QUERY_BMS_KEY, QUERY_FANDATA_KEY, QUERY_WALLBOX_KEY, + QUERY_SOCKETS_KEY, QUERY_SPARE_CAPACITY_KEY, QUERY_PEAK_SHAVING_KEY, IGNORE_SYSTEM_STATE_KEY, @@ -134,6 +138,31 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): return True +def check_for_options(registry, sluged_title:str, opt:dict, sensor_type:str, entity_description_list:list) -> dict: + for description in entity_description_list: + if not opt[QUERY_WALLBOX_KEY] and SENEC_SECTION_WALLBOX == description.senec_lala_section: + a_sensor_id = f"{sensor_type}.{sluged_title}_{description.key}".lower() + a_entity = registry.async_get(a_sensor_id) + if a_entity is not None and a_entity.disabled_by is None: + _LOGGER.info("***** QUERY_WALLBOX-DATA ********") + opt[QUERY_WALLBOX_KEY] = True + + if not opt[QUERY_FANDATA_KEY] and SENEC_SECTION_FAN_SPEED == description.senec_lala_section: + a_sensor_id = f"{sensor_type}.{sluged_title}_{description.key}".lower() + a_entity = registry.async_get(a_sensor_id) + if a_entity is not None and a_entity.disabled_by is None: + _LOGGER.info("***** QUERY_FANSPEED-DATA ********") + opt[QUERY_FANDATA_KEY] = True + + if not opt[QUERY_SOCKETS_KEY] and SENEC_SECTION_SOCKETS == description.senec_lala_section: + a_sensor_id = f"{sensor_type}.{sluged_title}_{description.key}".lower() + a_entity = registry.async_get(a_sensor_id) + if a_entity is not None and a_entity.disabled_by is None: + _LOGGER.info("***** QUERY_SOCKETS-DATA ********") + opt[QUERY_SOCKETS_KEY] = True + + return opt + class SenecDataUpdateCoordinator(DataUpdateCoordinator): """Define an object to hold Senec data.""" @@ -210,7 +239,8 @@ def __init__(self, hass: HomeAssistant, config_entry): IGNORE_SYSTEM_STATE_KEY: config_entry.options.get(CONF_IGNORE_SYSTEM_STATE, False), QUERY_WALLBOX_KEY: False, QUERY_BMS_KEY: False, - QUERY_FANDATA_KEY: False + QUERY_FANDATA_KEY: False, + QUERY_SOCKETS_KEY: False } # check if any of the wallbox-sensors is enabled... and only THEN # we will include the 'WALLBOX' in our POST to the lala.cgi @@ -218,44 +248,10 @@ def __init__(self, hass: HomeAssistant, config_entry): registry = entity_registry.async_get(hass) if registry is not None: sluged_title = slugify(config_entry.title) - for description in MAIN_SENSOR_TYPES: - if not opt[QUERY_WALLBOX_KEY] and SENEC_SECTION_WALLBOX == description.senec_lala_section: - a_sensor_id = f"sensor.{sluged_title}_{description.key}".lower() - a_entity = registry.async_get(a_sensor_id) - if a_entity is not None and a_entity.disabled_by is None: - _LOGGER.info("***** QUERY_WALLBOX-DATA ********") - opt[QUERY_WALLBOX_KEY] = True - - if not opt[QUERY_BMS_KEY] and SENEC_SECTION_BMS == description.senec_lala_section: - a_sensor_id = f"sensor.{sluged_title}_{description.key}".lower() - a_entity = registry.async_get(a_sensor_id) - if a_entity is not None and a_entity.disabled_by is None: - _LOGGER.info("***** QUERY_BMS-DATA ********") - opt[QUERY_BMS_KEY] = True - - # yes - currently only the 'MAIN_BIN_SENSOR's will contain the SENEC_SECTION_FAN_SPEED but - # I want to have here the complete code/overview 'what should be checked' - if not opt[QUERY_FANDATA_KEY] and SENEC_SECTION_FAN_SPEED == description.senec_lala_section: - a_sensor_id = f"sensor.{sluged_title}_{description.key}".lower() - a_entity = registry.async_get(a_sensor_id) - if a_entity is not None and a_entity.disabled_by is None: - _LOGGER.info("***** QUERY_FANSPEED-DATA ********") - opt[QUERY_FANDATA_KEY] = True - - for description in MAIN_BIN_SENSOR_TYPES: - if not opt[QUERY_WALLBOX_KEY] and SENEC_SECTION_WALLBOX == description.senec_lala_section: - a_sensor_id = f"binary_sensor.{sluged_title}_{description.key}".lower() - a_entity = registry.async_get(a_sensor_id) - if a_entity is not None and a_entity.disabled_by is None: - _LOGGER.info("***** QUERY_WALLBOX-DATA ********") - opt[QUERY_WALLBOX_KEY] = True - - if not opt[QUERY_FANDATA_KEY] and SENEC_SECTION_FAN_SPEED == description.senec_lala_section: - a_sensor_id = f"binary_sensor.{sluged_title}_{description.key}".lower() - a_entity = registry.async_get(a_sensor_id) - if a_entity is not None and a_entity.disabled_by is None: - _LOGGER.info("***** QUERY_FANSPEED-DATA ********") - opt[QUERY_FANDATA_KEY] = True + opt = check_for_options(registry, sluged_title, opt, "sensor", MAIN_SENSOR_TYPES) + opt = check_for_options(registry, sluged_title, opt, "binary_sensor", MAIN_BIN_SENSOR_TYPES) + opt = check_for_options(registry, sluged_title, opt, "switch", MAIN_SWITCH_TYPES) + opt = check_for_options(registry, sluged_title, opt, "number", MAIN_NUMBER_SENSOR_TYPES) self.senec = Senec(host=self._host, use_https=self._use_https, web_session=async_get_clientsession(hass), lang=hass.config.language.lower(), options=opt) @@ -290,6 +286,13 @@ async def _async_switch_to_state(self, switch_key, state): except UpdateFailed as exception: raise UpdateFailed() from exception + async def _async_switch_array_to_state(self, switch_array_key, array_pos, state): + try: + await self.senec.switch_array(switch_array_key, array_pos, state) + return self.senec + except UpdateFailed as exception: + raise UpdateFailed() from exception + async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Unload Senec config entry.""" diff --git a/custom_components/senec/binary_sensor.py b/custom_components/senec/binary_sensor.py index 98f94ea..748933b 100644 --- a/custom_components/senec/binary_sensor.py +++ b/custom_components/senec/binary_sensor.py @@ -59,7 +59,10 @@ def is_on(self) -> bool | None: """Return true if the binary_sensor is on.""" # return self.coordinator.data.get("title", "") == "foo" try: - value = getattr(self.coordinator.senec, self.entity_description.key) + if self.entity_description.array_key is not None: + value = getattr(self.coordinator.senec, self.entity_description.array_key)[self.entity_description.array_pos] == 1 + else: + value = getattr(self.coordinator.senec, self.entity_description.key) if value is None or value == "": value = None else: diff --git a/custom_components/senec/const.py b/custom_components/senec/const.py index 73dc2f4..ccfc02c 100644 --- a/custom_components/senec/const.py +++ b/custom_components/senec/const.py @@ -20,7 +20,9 @@ POWER_KILO_WATT, TEMP_CELSIUS, UnitOfElectricPotential, - UnitOfElectricCurrent, UnitOfFrequency, + UnitOfElectricCurrent, + UnitOfFrequency, + UnitOfTime, ) from homeassistant.helpers.entity import EntityCategory @@ -33,6 +35,7 @@ SENEC_SECTION_PM1OBJ2, SENEC_SECTION_PV1, SENEC_SECTION_PWR_UNIT, + SENEC_SECTION_SOCKETS, SENEC_SECTION_TEMPMEASURE, SENEC_SECTION_WALLBOX ) @@ -94,6 +97,7 @@ QUERY_BMS_KEY = "query_bms_data" QUERY_FANDATA_KEY = "query_fan_data" QUERY_WALLBOX_KEY = "query_wallbox_data" +QUERY_SOCKETS_KEY = "query_sockets_data" QUERY_SPARE_CAPACITY_KEY = "query_spare_capacity" QUERY_PEAK_SHAVING_KEY = "query_peak_shaving" IGNORE_SYSTEM_STATE_KEY = CONF_IGNORE_SYSTEM_STATE @@ -108,21 +112,32 @@ class ExtSensorEntityDescription(SensorEntityDescription): controls: list[str] | None = None senec_lala_section: str | None = None - + array_key: str | None = None + array_pos: int = -1 @dataclass class ExtBinarySensorEntityDescription(BinarySensorEntityDescription): icon_off: str | None = None senec_lala_section: str | None = None - + array_key: str | None = None + array_pos: int = -1 @dataclass class ExtSwitchEntityDescription(SwitchEntityDescription): update_after_switch_delay_in_sec: int = 0 + senec_lala_section: str | None = None + array_key: str | None = None + array_pos: int = -1 + +@dataclass +class ExtNumberEntityDescription(NumberEntityDescription): + update_after_switch_delay_in_sec: int = 0 + senec_lala_section: str | None = None + array_key: str | None = None + array_pos: int = -1 -"""Supported number implementations""" -WEB_NUMBER_SENYOR_TYPES = [ +WEB_NUMBER_SENSOR_TYPES = [ NumberEntityDescription( entity_registry_enabled_default=False, key="spare_capacity", @@ -137,6 +152,355 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): ), ] +WEB_SENSOR_TYPES = [ + + ExtSensorEntityDescription( + key="consumption_total", + name="House consumed", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:home-import-outline", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + ExtSensorEntityDescription( + key="powergenerated_total", + name="Solar generated", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:solar-power", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + ExtSensorEntityDescription( + key="accuimport_total", + name="Battery discharged", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:home-battery-outline", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + ExtSensorEntityDescription( + key="accuexport_total", + name="Battery charged", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:home-battery", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + ExtSensorEntityDescription( + key="gridimport_total", + name="Grid Imported", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:transmission-tower-export", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + ExtSensorEntityDescription( + key="gridexport_total", + name="Grid Exported", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + icon="mdi:transmission-tower-import", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL, + controls=("only_increasing"), + ), + + # accuimport_today + # accuexport_today + # gridimport_today + # gridexport_today + # powergenerated_today + # consumption_today + + SensorEntityDescription( + key="powergenerated_now", + name="Solar Generated Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:solar-power", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="consumption_now", + name="House Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:home-import-outline", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="accuimport_now", + name="Battery Discharge Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:home-battery", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="accuexport_now", + name="Battery Charge Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:home-battery-outline", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="acculevel_now", + name="Battery Charge Percent", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:home-battery", + # device_class=SensorDeviceClass.BATTERY, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="gridimport_now", + name="Grid Imported Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:transmission-tower-export", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="gridexport_now", + name="Grid Exported Power", + native_unit_of_measurement=POWER_KILO_WATT, + icon="mdi:transmission-tower-import", + device_class=SensorDeviceClass.POWER, + suggested_display_precision=3, + state_class=SensorStateClass.MEASUREMENT, + ), + # Peak Shaving + SensorEntityDescription( + entity_registry_enabled_default=False, + key="gridexport_limit", + name="Grid Exported Limit", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:transmission-tower-off", + device_class=SensorDeviceClass.POWER_FACTOR, + suggested_display_precision=0, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + entity_registry_enabled_default=False, + key="peakshaving_mode", + name="Peak Shaving Mode", + icon="mdi:toggle-switch", + device_class=SensorDeviceClass.ENUM, + options=PEAK_SHAVING_OPTIONS + ), + SensorEntityDescription( + entity_registry_enabled_default=False, + key="peakshaving_capacitylimit", + name="Peak Shaving Capacity Limit", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:battery-lock", + device_class=SensorDeviceClass.BATTERY, + suggested_display_precision=0, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + entity_registry_enabled_default=False, + key="peakshaving_enddate", + name="Peak Shaving End Time", + icon="mdi:calendar-clock", + device_class=SensorDeviceClass.TIMESTAMP, + ), +] + +INVERTER_SENSOR_TYPES = [ + ExtSensorEntityDescription( + key="ac_voltage", + name="AC Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + icon="mdi:lightning-bolt", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + key="ac_current", + name="AC Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + icon="mdi:current-ac", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + key="ac_power", + name="AC Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:solar-power", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + key="ac_power_fast", + name="AC Power (fast)", + native_unit_of_measurement=POWER_WATT, + icon="mdi:solar-power", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + key="ac_frequency", + name="AC Frequency", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + icon="mdi:meter-electric", + device_class=SensorDeviceClass.FREQUENCY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_bat_voltage", + name="BDC Battery Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + icon="mdi:lightning-bolt", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_bat_current", + name="BDC Battery Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + icon="mdi:current-dc", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_bat_power", + name="BDC Battery Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:battery-charging-100", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_link_voltage", + name="BDC Link Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + icon="mdi:lightning-bolt", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_link_current", + name="BDC Link Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + icon="mdi:current-dc", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + controls=("bdc_only"), + key="bdc_link_power", + name="BDC Link Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:power-plug-outline", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + key="dc_voltage1", + name="DC Voltage 1", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + icon="mdi:lightning-bolt", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + key="dc_voltage2", + name="DC Voltage 2", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + icon="mdi:lightning-bolt", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + key="dc_current1", + name="DC Current 1", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + icon="mdi:current-dc", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + entity_registry_enabled_default=False, + key="dc_current2", + name="DC Current 2", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + icon="mdi:current-dc", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.MEASUREMENT, + ), + + ExtSensorEntityDescription( + entity_registry_enabled_default=False, + key="gridpower", + name="Grid Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:transmission-tower", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + entity_registry_enabled_default=False, + key="gridconsumedpower", + name="Grid consumed Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:transmission-tower-import", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + entity_registry_enabled_default=False, + key="gridinjectedpower", + name="Grid injected Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:transmission-tower-export", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + entity_registry_enabled_default=False, + key="ownconsumedpower", + name="Own consumed Power", + native_unit_of_measurement=POWER_WATT, + icon="mdi:home-import-outline", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + ExtSensorEntityDescription( + key="derating", + name="Derating", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:arrow-down-thin-circle-outline", + state_class=SensorStateClass.MEASUREMENT, + ), +] + """Supported main unit switch types.""" MAIN_SWITCH_TYPES = [ ExtSwitchEntityDescription( @@ -152,6 +516,63 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): icon="mdi:solar-power", update_after_switch_delay_in_sec=2, ), + # PERMANENT ON + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_force_on", + array_pos=0, + key="sockets_1_force_on", + name="sockets_1_force_on", + icon="mdi:toggle-switch", + ), + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_force_on", + array_pos=1, + key="sockets_2_force_on", + name="sockets_2_force_on", + icon="mdi:toggle-switch", + ), + # Automatic ON + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_enable", + array_pos=0, + key="sockets_1_enable", + name="sockets_1_enable", + icon="mdi:toggle-switch" + ), + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_enable", + array_pos=1, + key="sockets_2_enable", + name="sockets_2_enable", + icon="mdi:toggle-switch" + ), + # Time Controlled ON + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_use_time", + array_pos=0, + key="sockets_1_use_time", + name="sockets_1_use_time", + icon="mdi:toggle-switch", + ), + ExtSwitchEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_use_time", + array_pos=1, + key="sockets_2_use_time", + name="sockets_2_use_time", + icon="mdi:toggle-switch", + ) ] """Supported main unit binary_sensor types.""" @@ -252,7 +673,6 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): icon="mdi:car-electric", entity_category=EntityCategory.DIAGNOSTIC, ), - ExtBinarySensorEntityDescription( senec_lala_section=SENEC_SECTION_FAN_SPEED, entity_registry_enabled_default=False, @@ -271,170 +691,227 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): icon_off="mdi:fan-off", entity_category=EntityCategory.DIAGNOSTIC, ), -] - -WEB_SENSOR_TYPES = [ - - ExtSensorEntityDescription( - key="consumption_total", - name="House consumed", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:home-import-outline", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - ExtSensorEntityDescription( - key="powergenerated_total", - name="Solar generated", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:solar-power", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - ExtSensorEntityDescription( - key="accuimport_total", - name="Battery discharged", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:home-battery-outline", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - ExtSensorEntityDescription( - key="accuexport_total", - name="Battery charged", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:home-battery", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - ExtSensorEntityDescription( - key="gridimport_total", - name="Grid Imported", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:transmission-tower-export", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - ExtSensorEntityDescription( - key="gridexport_total", - name="Grid Exported", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - icon="mdi:transmission-tower-import", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.TOTAL, - controls=("only_increasing"), - ), - - # accuimport_today - # accuexport_today - # gridimport_today - # gridexport_today - # powergenerated_today - # consumption_today - - SensorEntityDescription( - key="powergenerated_now", - name="Solar Generated Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:solar-power", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key="consumption_now", - name="House Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:home-import-outline", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, + ExtBinarySensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_power_on", + array_pos=0, + key="sockets_1_power_on", + name="sockets_1_power_on", + icon="mdi:toggle-switch", + icon_off="mdi:toggle-switch-off", + entity_category=EntityCategory.DIAGNOSTIC, ), - SensorEntityDescription( - key="accuimport_now", - name="Battery Discharge Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:home-battery", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, + ExtBinarySensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_power_on", + array_pos=1, + key="sockets_2_power_on", + name="sockets_2_power_on", + icon="mdi:toggle-switch", + icon_off="mdi:toggle-switch-off", + entity_category=EntityCategory.DIAGNOSTIC, ), - SensorEntityDescription( - key="accuexport_now", - name="Battery Charge Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:home-battery-outline", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, + ExtBinarySensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_already_switched", + array_pos=0, + key="sockets_1_already_switched", + name="sockets_1_already_switched", + icon="mdi:toggle-switch", + icon_off="mdi:toggle-switch-off", + entity_category=EntityCategory.DIAGNOSTIC, ), - SensorEntityDescription( - key="acculevel_now", - name="Battery Charge Percent", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:home-battery", - # device_class=SensorDeviceClass.BATTERY, - state_class=SensorStateClass.MEASUREMENT, + ExtBinarySensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_already_switched", + array_pos=1, + key="sockets_2_already_switched", + name="sockets_2_already_switched", + icon="mdi:toggle-switch", + icon_off="mdi:toggle-switch-off", + entity_category=EntityCategory.DIAGNOSTIC, ), - SensorEntityDescription( - key="gridimport_now", - name="Grid Imported Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:transmission-tower-export", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, +] + +"""Supported main unit number implementations""" +MAIN_NUMBER_SENSOR_TYPES = [ + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_lower_limit", + array_pos=0, + key="sockets_1_lower_limit", + name="sockets_1_lower_limit", + icon="mdi:lightning-bolt-outline", + device_class=NumberDeviceClass.ENERGY, + mode=NumberMode.BOX, + native_max_value=65535, + native_min_value=0, + native_step=1, + native_unit_of_measurement=POWER_WATT, ), - SensorEntityDescription( - key="gridexport_now", - name="Grid Exported Power", - native_unit_of_measurement=POWER_KILO_WATT, - icon="mdi:transmission-tower-import", - device_class=SensorDeviceClass.POWER, - suggested_display_precision=3, - state_class=SensorStateClass.MEASUREMENT, + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_upper_limit", + array_pos=0, + key="sockets_1_upper_limit", + name="sockets_1_upper_limit", + icon="mdi:lightning-bolt", + device_class=NumberDeviceClass.ENERGY, + mode=NumberMode.BOX, + native_max_value=65535, + native_min_value=0, + native_step=1, + native_unit_of_measurement=POWER_WATT, ), - # Peak Shaving - SensorEntityDescription( + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="gridexport_limit", - name="Grid Exported Limit", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:transmission-tower-off", - device_class=SensorDeviceClass.POWER_FACTOR, - suggested_display_precision=0, - state_class=SensorStateClass.MEASUREMENT, + array_key="sockets_power_on_time", + array_pos=0, + key="sockets_1_power_on_time", + name="sockets_1_power_on_time", + icon="mdi:power-socket-eu", + mode=NumberMode.SLIDER, + native_max_value=1440, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, + ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_switch_on_hour", + array_pos=0, + key="sockets_1_switch_on_hour", + name="sockets_1_switch_on_hour", + icon="mdi:timeline-clock-outline", + mode=NumberMode.BOX, + native_max_value=23, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.HOURS, + ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_switch_on_minute", + array_pos=0, + key="sockets_1_switch_on_minute", + name="sockets_1_switch_on_minute", + icon="mdi:timeline-clock-outline", + mode=NumberMode.BOX, + native_max_value=59, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, ), - SensorEntityDescription( + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="peakshaving_mode", - name="Peak Shaving Mode", - icon="mdi:toggle-switch", - device_class=SensorDeviceClass.ENUM, - options=PEAK_SHAVING_OPTIONS + array_key="sockets_time_limit", + array_pos=0, + key="sockets_1_time_limit", + name="sockets_1_time_limit", + icon="mdi:solar-power", + mode=NumberMode.BOX, + native_max_value=1440, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, + ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_lower_limit", + array_pos=1, + key="sockets_2_lower_limit", + name="sockets_2_lower_limit", + icon="mdi:lightning-bolt-outline", + device_class=NumberDeviceClass.ENERGY, + mode=NumberMode.BOX, + native_max_value=65535, + native_min_value=0, + native_step=1, + native_unit_of_measurement=POWER_WATT, ), - SensorEntityDescription( + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="peakshaving_capacitylimit", - name="Peak Shaving Capacity Limit", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:battery-lock", - device_class=SensorDeviceClass.BATTERY, - suggested_display_precision=0, - state_class=SensorStateClass.MEASUREMENT, + array_key="sockets_upper_limit", + array_pos=1, + key="sockets_2_upper_limit", + name="sockets_2_upper_limit", + icon="mdi:lightning-bolt", + device_class=NumberDeviceClass.ENERGY, + mode=NumberMode.BOX, + native_max_value=65535, + native_min_value=0, + native_step=1, + native_unit_of_measurement=POWER_WATT, ), - SensorEntityDescription( + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="peakshaving_enddate", - name="Peak Shaving End Time", - icon="mdi:calendar-clock", - device_class=SensorDeviceClass.TIMESTAMP, + array_key="sockets_power_on_time", + array_pos=1, + key="sockets_2_power_on_time", + name="sockets_2_power_on_time", + icon="mdi:power-socket-eu", + mode=NumberMode.SLIDER, + native_max_value=1440, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, + ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_switch_on_hour", + array_pos=1, + key="sockets_2_switch_on_hour", + name="sockets_2_switch_on_hour", + icon="mdi:timeline-clock-outline", + mode=NumberMode.BOX, + native_max_value=23, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.HOURS, + ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_switch_on_minute", + array_pos=1, + key="sockets_2_switch_on_minute", + name="sockets_2_switch_on_minute", + icon="mdi:timeline-clock-outline", + mode=NumberMode.BOX, + native_max_value=59, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, ), + ExtNumberEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, + entity_registry_enabled_default=False, + array_key="sockets_time_limit", + array_pos=1, + key="sockets_2_time_limit", + name="sockets_2_time_limit", + icon="mdi:solar-power", + mode=NumberMode.BOX, + native_max_value=1440, + native_min_value=0, + native_step=1, + native_unit_of_measurement=UnitOfTime.MINUTES, + ), + ] """Supported main unit sensor types.""" @@ -2189,189 +2666,45 @@ class ExtSwitchEntityDescription(SwitchEntityDescription): device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.MEASUREMENT, ), -] - -INVERTER_SENSOR_TYPES = [ - ExtSensorEntityDescription( - key="ac_voltage", - name="AC Voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - icon="mdi:lightning-bolt", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - key="ac_current", - name="AC Current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - icon="mdi:current-ac", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - key="ac_power", - name="AC Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:solar-power", - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - key="ac_power_fast", - name="AC Power (fast)", - native_unit_of_measurement=POWER_WATT, - icon="mdi:solar-power", - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - key="ac_frequency", - name="AC Frequency", - native_unit_of_measurement=UnitOfFrequency.HERTZ, - icon="mdi:meter-electric", - device_class=SensorDeviceClass.FREQUENCY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_bat_voltage", - name="BDC Battery Voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - icon="mdi:lightning-bolt", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_bat_current", - name="BDC Battery Current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - icon="mdi:current-dc", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_bat_power", - name="BDC Battery Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:battery-charging-100", - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_link_voltage", - name="BDC Link Voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - icon="mdi:lightning-bolt", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_link_current", - name="BDC Link Current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - icon="mdi:current-dc", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - controls=("bdc_only"), - key="bdc_link_power", - name="BDC Link Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:power-plug-outline", - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), - - ExtSensorEntityDescription( - key="dc_voltage1", - name="DC Voltage 1", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - icon="mdi:lightning-bolt", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - ExtSensorEntityDescription( - key="dc_voltage2", - name="DC Voltage 2", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - icon="mdi:lightning-bolt", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - ExtSensorEntityDescription( - key="dc_current1", - name="DC Current 1", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - icon="mdi:current-dc", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - ExtSensorEntityDescription( - entity_registry_enabled_default=False, - key="dc_current2", - name="DC Current 2", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - icon="mdi:current-dc", - device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, - ), - ExtSensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="gridpower", - name="Grid Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:transmission-tower", - device_class=SensorDeviceClass.POWER, + array_key="sockets_priority", + array_pos=0, + key="sockets_1_priority", + name="sockets_1_priority", + icon="mdi:counter", state_class=SensorStateClass.MEASUREMENT, ), ExtSensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="gridconsumedpower", - name="Grid consumed Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:transmission-tower-import", - device_class=SensorDeviceClass.POWER, + array_key="sockets_priority", + array_pos=1, + key="sockets_2_priority", + name="sockets_2_priority", + icon="mdi:counter", state_class=SensorStateClass.MEASUREMENT, ), ExtSensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="gridinjectedpower", - name="Grid injected Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:transmission-tower-export", - device_class=SensorDeviceClass.POWER, + array_key="sockets_time_rem", + array_pos=0, + key="sockets_1_time_rem", + name="sockets_1_time_rem", + icon="mdi:counter", state_class=SensorStateClass.MEASUREMENT, ), ExtSensorEntityDescription( + senec_lala_section=SENEC_SECTION_SOCKETS, entity_registry_enabled_default=False, - key="ownconsumedpower", - name="Own consumed Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:home-import-outline", - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), - ExtSensorEntityDescription( - key="derating", - name="Derating", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:arrow-down-thin-circle-outline", + array_key="sockets_time_rem", + array_pos=1, + key="sockets_2_time_rem", + name="sockets_2_time_rem", + icon="mdi:counter", state_class=SensorStateClass.MEASUREMENT, ), ] + diff --git a/custom_components/senec/manifest.json b/custom_components/senec/manifest.json index 612fe67..368bd04 100644 --- a/custom_components/senec/manifest.json +++ b/custom_components/senec/manifest.json @@ -13,5 +13,5 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/marq24/ha-senec-v3/issues", "requirements": ["xmltodict>=0.12.0", "packaging>=21.0"], - "version": "3.1.4" + "version": "3.1.5" } diff --git a/custom_components/senec/number.py b/custom_components/senec/number.py index 7b309d4..8a7e05d 100644 --- a/custom_components/senec/number.py +++ b/custom_components/senec/number.py @@ -10,8 +10,10 @@ from . import SenecDataUpdateCoordinator, SenecEntity from .const import ( DOMAIN, - WEB_NUMBER_SENYOR_TYPES, - CONF_SYSTYPE_WEB + MAIN_NUMBER_SENSOR_TYPES, + WEB_NUMBER_SENSOR_TYPES, + CONF_SYSTYPE_WEB, + CONF_SYSTYPE_INVERTER ) _LOGGER = logging.getLogger(__name__) @@ -21,15 +23,21 @@ async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry, """Initialize sensor platform from config entry.""" coordinator = hass.data[DOMAIN][config_entry.entry_id] entities = [] - # Take care that CONF_TYPE = CONF_SYSTEYPE_WEB, since the implementation works with the web API - if CONF_TYPE in config_entry.data and config_entry.data[CONF_TYPE] == CONF_SYSTYPE_WEB: - for description in WEB_NUMBER_SENYOR_TYPES: + + if CONF_TYPE in config_entry.data and config_entry.data[CONF_TYPE] == CONF_SYSTYPE_INVERTER: + _LOGGER.info("No numbers for Inverters...") + elif CONF_TYPE in config_entry.data and config_entry.data[CONF_TYPE] == CONF_SYSTYPE_WEB: + for description in WEB_NUMBER_SENSOR_TYPES: + entity = SenecNumber(coordinator, description) + entities.append(entity) + else: + for description in MAIN_NUMBER_SENSOR_TYPES: entity = SenecNumber(coordinator, description) entities.append(entity) + async_add_entities(entities) -"""Implementation for the spare capacity of the senec device""" class SenecNumber(SenecEntity, NumberEntity): def __init__( @@ -55,13 +63,22 @@ def __init__( @property def state(self) -> int: - number = self.entity_description.key - value = getattr(self.coordinator.senec, number) + if self.entity_description.array_key is not None: + value = getattr(self.coordinator.senec, self.entity_description.array_key)[self.entity_description.array_pos] + else: + value = getattr(self.coordinator.senec, self.entity_description.key) + return int(value) async def async_set_native_value(self, value: int) -> None: """Update the current value.""" - updated_spare_capacity = int(value) api = self.coordinator.senec - await api.set_spare_capacity(updated_spare_capacity) + # this is quite an ugly hack - but it's xmas! + if self.entity_description.key == 'spare_capacity': + await api.set_spare_capacity(int(value)) + else: + if self.entity_description.array_key is not None: + await api.set_number_value_array(self.entity_description.array_key, self.entity_description.array_pos, int(value)) + else: + await api.set_number_value(self.entity_description.key, int(value)) self.async_schedule_update_ha_state(force_refresh=True) diff --git a/custom_components/senec/pysenec_ha/__init__.py b/custom_components/senec/pysenec_ha/__init__.py index 369b87c..827182d 100644 --- a/custom_components/senec/pysenec_ha/__init__.py +++ b/custom_components/senec/pysenec_ha/__init__.py @@ -23,6 +23,7 @@ QUERY_BMS_KEY, QUERY_FANDATA_KEY, QUERY_WALLBOX_KEY, + QUERY_SOCKETS_KEY, QUERY_SPARE_CAPACITY_KEY, QUERY_PEAK_SHAVING_KEY, IGNORE_SYSTEM_STATE_KEY, @@ -43,6 +44,7 @@ SENEC_SECTION_PV1, SENEC_SECTION_PM1OBJ1, SENEC_SECTION_PM1OBJ2, + SENEC_SECTION_SOCKETS, SENEC_SECTION_WALLBOX, SENEC_SECTION_FACTORY, @@ -95,12 +97,16 @@ def __init__(self, host, use_https, web_session, lang: str = "en", options: dict else: self._QUERY_FANDATA = False + if options is not None and QUERY_SOCKETS_KEY in options: + self._QUERY_SOCKETSDATA = options[QUERY_SOCKETS_KEY] + else: + self._QUERY_SOCKETSDATA = False + if options is not None and IGNORE_SYSTEM_STATE_KEY in options: self._IGNORE_SYSTEM_STATUS = options[IGNORE_SYSTEM_STATE_KEY] else: self._IGNORE_SYSTEM_STATUS = False - self.host = host self.web_session: aiohttp.websession = web_session @@ -112,8 +118,19 @@ def __init__(self, host, use_https, web_session, lang: str = "en", options: dict # evil HACK - since SENEC does not switch the property fast enough... # so for five seconds after the switch take place we will return # the 'faked' value - self._LI_STORAGE_MODE_RUNNING_OVERWRITE_TS = 0 - self._SAFE_CHARGE_RUNNING_OVERWRITE_TS = 0 + self._OVERWRITES = { + "LI_STORAGE_MODE_RUNNING": { "TS": 0, "VALUE": False}, + "SAFE_CHARGE_RUNNING": { "TS": 0, "VALUE": False}, + "SOCKETS_FORCE_ON": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_ENABLE": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_USE_TIME": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_LOWER_LIMIT": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_UPPER_LIMIT": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_POWER_ON_TIME": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_SWITCH_ON_HOUR": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_SWITCH_ON_MINUTE": { "TS": 0, "VALUE": [0, 0]}, + "SOCKETS_TIME_LIMIT": { "TS": 0, "VALUE": [0, 0]}, + } @property def device_id(self) -> str: @@ -1389,6 +1406,26 @@ def fan_inv_hv(self) -> bool: SENEC_SECTION_FAN_SPEED]: return self._raw[SENEC_SECTION_FAN_SPEED]["INV_HV"] == 1 + @property + def sockets_already_switched(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "ALREADY_SWITCHED" in self._raw[SENEC_SECTION_SOCKETS]: + return self._raw[SENEC_SECTION_SOCKETS]["ALREADY_SWITCHED"] + + @property + def sockets_power_on(self) -> [float]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "POWER_ON" in self._raw[SENEC_SECTION_SOCKETS]: + return self._raw[SENEC_SECTION_SOCKETS]["POWER_ON"] + + @property + def sockets_priority(self) -> [float]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "PRIORITY" in self._raw[SENEC_SECTION_SOCKETS]: + return self._raw[SENEC_SECTION_SOCKETS]["PRIORITY"] + + @property + def sockets_time_rem(self) -> [float]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "TIME_REM" in self._raw[SENEC_SECTION_SOCKETS]: + return self._raw[SENEC_SECTION_SOCKETS]["TIME_REM"] + async def update(self): await self.read_senec_lala_with_retry(retry=True) @@ -1469,6 +1506,9 @@ async def read_senec_lala(self): if self._QUERY_FANDATA: form.update({SENEC_SECTION_FAN_SPEED: {}}) + if self._QUERY_SOCKETSDATA: + form.update({SENEC_SECTION_SOCKETS: {}}) + if self._QUERY_BMS: form.update({SENEC_SECTION_BMS: { "CELL_TEMPERATURES_MODULE_A": "", @@ -1526,7 +1566,6 @@ async def read_senec_energy(self): except JSONDecodeError as exc: _LOGGER.warning(f"JSONDecodeError while 'await res.json()' {exc}") - ## LADEN... ## {"ENERGY":{"SAFE_CHARGE_FORCE":"u8_01","SAFE_CHARGE_PROHIBIT":"","SAFE_CHARGE_RUNNING":"","LI_STORAGE_MODE_START":"","LI_STORAGE_MODE_STOP":"","LI_STORAGE_MODE_RUNNING":""}} @@ -1552,14 +1591,14 @@ def safe_charge(self) -> bool: if hasattr(self, '_raw'): # if it just has been switched on/off we provide a FAKE value for 5 sec... # since senec unit do not react 'instant' on some requests... - if self._SAFE_CHARGE_RUNNING_OVERWRITE_TS + 5 > time(): - return self._SAFE_CHARGE_RUNNING_OVERWRITE_VALUE + if self._OVERWRITES["SAFE_CHARGE_RUNNING"]["TS"] + 5 > time(): + return self._OVERWRITES["SAFE_CHARGE_RUNNING"]["VALUE"] else: return self._raw[SENEC_SECTION_ENERGY]["SAFE_CHARGE_RUNNING"] == 1 async def switch_safe_charge(self, value: bool): - self._SAFE_CHARGE_RUNNING_OVERWRITE_VALUE = value - self._SAFE_CHARGE_RUNNING_OVERWRITE_TS = time() + self._OVERWRITES["SAFE_CHARGE_RUNNING"].update({"VALUE": value}) + self._OVERWRITES["SAFE_CHARGE_RUNNING"].update({"TS": time()}) post_data = {} if (value): self._raw[SENEC_SECTION_ENERGY]["SAFE_CHARGE_RUNNING"] = 1 @@ -1581,14 +1620,14 @@ def li_storage_mode(self) -> bool: if hasattr(self, '_raw'): # if it just has been switched on/off we provide a FAKE value for 5 sec... # since senec unit do not react 'instant' on some requests... - if self._LI_STORAGE_MODE_RUNNING_OVERWRITE_TS + 5 > time(): - return self._LI_STORAGE_MODE_RUNNING_OVERWRITE_VALUE + if self._OVERWRITES["LI_STORAGE_MODE_RUNNING"]["TS"] + 5 > time(): + return self._OVERWRITES["LI_STORAGE_MODE_RUNNING"]["VALUE"] else: return self._raw[SENEC_SECTION_ENERGY]["LI_STORAGE_MODE_RUNNING"] == 1 async def switch_li_storage_mode(self, value: bool): - self._LI_STORAGE_MODE_RUNNING_OVERWRITE_VALUE = value - self._LI_STORAGE_MODE_RUNNING_OVERWRITE_TS = time() + self._OVERWRITES["LI_STORAGE_MODE_RUNNING"].update({"VALUE": value}) + self._OVERWRITES["LI_STORAGE_MODE_RUNNING"].update({"TS": time()}) post_data = {} if (value): self._raw[SENEC_SECTION_ENERGY]["LI_STORAGE_MODE_RUNNING"] = 1 @@ -1608,6 +1647,159 @@ async def switch_li_storage_mode(self, value: bool): async def switch(self, switch_key, value): return await getattr(self, 'switch_' + str(switch_key))(value) + """SWITCH ARRAY FROM HERE...""" + + @property + def sockets_enable(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "ENABLE" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_ENABLE"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_ENABLE"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["ENABLE"] + + async def switch_array_sockets_enable(self, pos: int, value: bool): + await self.switch_array_data("ENABLE", pos, value); + + @property + def sockets_force_on(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "FORCE_ON" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_FORCE_ON"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_FORCE_ON"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["FORCE_ON"] + + async def switch_array_sockets_force_on(self, pos: int, value: bool): + await self.switch_array_data("FORCE_ON", pos, value); + + @property + def sockets_use_time(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "USE_TIME" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_USE_TIME"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_USE_TIME"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["USE_TIME"] + + async def switch_array_sockets_use_time(self, pos: int, value: bool): + await self.switch_array_data("USE_TIME", pos, value); + + async def switch_array_data(self, upper_key: str, pos: int, value: bool): + self._OVERWRITES["SOCKETS_" + upper_key].update({"VALUE": self._raw[SENEC_SECTION_SOCKETS][upper_key]}) + self._OVERWRITES["SOCKETS_" + upper_key]["VALUE"][pos] = 1 if value else 0 + self._OVERWRITES["SOCKETS_" + upper_key]["TS"] = time() + value_data = ["", ""] + if (value): + self._raw[SENEC_SECTION_SOCKETS][upper_key][pos] = 1 + value_data[pos] = "u8_01" + else: + self._raw[SENEC_SECTION_SOCKETS][upper_key][pos] = 0 + value_data[pos] = "u8_00" + + post_data = { + SENEC_SECTION_SOCKETS: { + upper_key: value_data + } + } + await self.write(post_data) + + async def switch_array(self, switch_array_key, array_pos, value): + return await getattr(self, 'switch_array_' + str(switch_array_key))(array_pos, value) + + """NUMBER ARRAY VALUES FROM HERE...""" + + @property + def sockets_lower_limit(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "LOWER_LIMIT" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_LOWER_LIMIT"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_LOWER_LIMIT"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["LOWER_LIMIT"] + + async def set_number_value_array_sockets_lower_limit(self, pos: int, value: int): + await self.set_number_array_value_data("LOWER_LIMIT", pos, 4, value) + + @property + def sockets_upper_limit(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "UPPER_LIMIT" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_UPPER_LIMIT"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_UPPER_LIMIT"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["UPPER_LIMIT"] + + async def set_number_value_array_sockets_upper_limit(self, pos: int, value: int): + await self.set_number_array_value_data("UPPER_LIMIT", pos, 4, value) + + @property + def sockets_power_on_time(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "POWER_ON_TIME" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_POWER_ON_TIME"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_POWER_ON_TIME"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["POWER_ON_TIME"] + + async def set_number_value_array_sockets_power_on_time(self, pos: int, value: int): + await self.set_number_array_value_data("POWER_ON_TIME", pos, 4, value) + + @property + def sockets_switch_on_hour(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "SWITCH_ON_HOUR" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_SWITCH_ON_HOUR"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_SWITCH_ON_HOUR"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["SWITCH_ON_HOUR"] + + async def set_number_value_array_sockets_switch_on_hour(self, pos: int, value: int): + await self.set_number_array_value_data("SWITCH_ON_HOUR", pos, 2, value) + + @property + def sockets_switch_on_minute(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "SWITCH_ON_MINUTE" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_SWITCH_ON_MINUTE"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_SWITCH_ON_MINUTE"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["SWITCH_ON_MINUTE"] + + async def set_number_value_array_sockets_switch_on_minute(self, pos: int, value: int): + await self.set_number_array_value_data("SWITCH_ON_MINUTE", pos, 2, value) + + @property + def sockets_time_limit(self) -> [int]: + if hasattr(self, '_raw') and SENEC_SECTION_SOCKETS in self._raw and "TIME_LIMIT" in self._raw[SENEC_SECTION_SOCKETS]: + if self._OVERWRITES["SOCKETS_TIME_LIMIT"]["TS"] + 5 > time(): + return self._OVERWRITES["SOCKETS_TIME_LIMIT"]["VALUE"] + else: + return self._raw[SENEC_SECTION_SOCKETS]["TIME_LIMIT"] + + async def set_number_value_array_sockets_time_limit(self, pos: int, value: int): + await self.set_number_array_value_data("TIME_LIMIT", pos, 4, value) + + async def set_number_array_value_data(self, upper_key: str, pos: int, data_len: int, value: int): + self._OVERWRITES["SOCKETS_" + upper_key].update({"VALUE": self._raw[SENEC_SECTION_SOCKETS][upper_key]}) + self._OVERWRITES["SOCKETS_" + upper_key]["VALUE"][pos] = value + self._OVERWRITES["SOCKETS_" + upper_key]["TS"] = time() + + value_data = ["", ""] + self._raw[SENEC_SECTION_SOCKETS][upper_key][pos] = value + if data_len == 4: + value_data[pos] = "u1_"+util.get_int_as_hex(value, data_len) + else: + value_data[pos] = "u8_"+util.get_int_as_hex(value, data_len) + + post_data = { + SENEC_SECTION_SOCKETS: { + upper_key: value_data + } + } + await self.write(post_data) + + async def set_number_value_array(self, array_key: str, array_pos: int, value: int): + return await getattr(self, 'set_number_value_array_' + str(array_key))(array_pos, value) + + """NORMAL NUMBER HANDLING... currently no 'none-array' entities are implemented""" + + async def set_number_value(self, array_key: str, value: int): + # this will cause a method not found exception... + return await getattr(self, 'set_number_value_' + str(array_key))(value) + async def write(self, data): await self.write_senec_v31(data) @@ -2085,10 +2277,13 @@ async def update_peak_shaving(self): try: r_json = await res.json() # GET Data from JSON - self._peak_shaving_entities["einspeisebegrenzungKwpInPercent"] = r_json["einspeisebegrenzungKwpInPercent"] + self._peak_shaving_entities["einspeisebegrenzungKwpInPercent"] = r_json[ + "einspeisebegrenzungKwpInPercent"] self._peak_shaving_entities["peakShavingMode"] = r_json["peakShavingMode"].lower() - self._peak_shaving_entities["peakShavingCapacityLimitInPercent"] = r_json["peakShavingCapacityLimitInPercent"] - self._peak_shaving_entities["peakShavingEndDate"] = datetime.fromtimestamp(r_json["peakShavingEndDate"] / 1000) # from miliseconds to seconds + self._peak_shaving_entities["peakShavingCapacityLimitInPercent"] = r_json[ + "peakShavingCapacityLimitInPercent"] + self._peak_shaving_entities["peakShavingEndDate"] = datetime.fromtimestamp( + r_json["peakShavingEndDate"] / 1000) # from miliseconds to seconds self._QUERY_PEAK_SHAVING_TS = time() # Update timer, that the next update takes place in 24 hours except JSONDecodeError as exc: _LOGGER.warning(f"JSONDecodeError while 'await res.json()' {exc}") @@ -2487,7 +2682,8 @@ def peakshaving_mode(self) -> int: @property def peakshaving_capacitylimit(self) -> int: - if hasattr(self,"_peak_shaving_entities") and "peakShavingCapacityLimitInPercent" in self._peak_shaving_entities: + if hasattr(self, + "_peak_shaving_entities") and "peakShavingCapacityLimitInPercent" in self._peak_shaving_entities: return self._peak_shaving_entities["peakShavingCapacityLimitInPercent"] @property @@ -2504,9 +2700,11 @@ def clear_jar(self): def _require_lib_patch() -> bool: need_patch = version.parse(aiohttp.__version__) < version.parse("3.9.0") if need_patch: - _LOGGER.info(f"aiohttp version is below 3.9.0 (current version is: {aiohttp.__version__}) - CookieJar.filter_cookies(...) need to be patched") + _LOGGER.info( + f"aiohttp version is below 3.9.0 (current version is: {aiohttp.__version__}) - CookieJar.filter_cookies(...) need to be patched") return need_patch + class MySenecCookieJar(aiohttp.CookieJar): # Overwriting the default 'filter_cookies' impl - since the original will always return the last stored @@ -2556,4 +2754,4 @@ def filter_cookies(self, request_url: URL = URL()) -> Union["BaseCookie[str]", " mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) filtered[name] = mrsl_val - return filtered \ No newline at end of file + return filtered diff --git a/custom_components/senec/pysenec_ha/util.py b/custom_components/senec/pysenec_ha/util.py index 9601bd6..d67741e 100644 --- a/custom_components/senec/pysenec_ha/util.py +++ b/custom_components/senec/pysenec_ha/util.py @@ -7,6 +7,10 @@ def parse_value(value: str): key, value = value.split("_") except ValueError: return value + + #if key == "u8": + # return unpack(">B", bytes.fromhex(value))[0] + #el if key.startswith("u") or key.startswith("i"): # Unsigned and signed int return int(value, 16) @@ -29,3 +33,10 @@ def parse(raw: dict): elif isinstance(v, list): raw[k] = [parse_value(i) for i in v] return raw + +def get_int_as_hex(input: int, length:int) -> str: + out = f'{input:X}' + while len(out) < length: + out = '0' + out + + return out; \ No newline at end of file diff --git a/custom_components/senec/sensor.py b/custom_components/senec/sensor.py index 8de09ab..051da9c 100644 --- a/custom_components/senec/sensor.py +++ b/custom_components/senec/sensor.py @@ -97,12 +97,20 @@ def __init__( @property def state(self): """Return the current state.""" - sensor = self.entity_description.key - value = getattr(self.coordinator.senec, sensor) + if self.entity_description.array_key is not None: + #_LOGGER.debug(f"{self.entity_description.array_key} {self.entity_description.array_pos}") + #value = 0 + value = getattr(self.coordinator.senec, self.entity_description.array_key)[self.entity_description.array_pos] + else: + value = getattr(self.coordinator.senec, self.entity_description.key) + # _LOGGER.debug( str(sensor)+' '+ str(type(value)) +' '+str(value)) if isinstance(value, bool): return value + if isinstance(value, int): + return value + # always try to parse sensor value as float try: value = round(float(value), 2) diff --git a/custom_components/senec/strings.json b/custom_components/senec/strings.json index 1cebf21..220e8b6 100644 --- a/custom_components/senec/strings.json +++ b/custom_components/senec/strings.json @@ -34,16 +34,74 @@ } }, "entity": { - "sensor": { - "spare_capacity": { - "name": "Spare Capacity" - }, + "switch": { "safe_charge": { "name": "Load Battery" }, "li_storage_mode": { "name": "Lithium Storage Mode - PV OFF" }, + "sockets_1_force_on": { + "name": "Socket1 permanent on" + }, + "sockets_2_force_on": { + "name": "Socket2 permanent on" + }, + "sockets_1_enable": { + "name": "Socket1 automatic enable" + }, + "sockets_2_enable": { + "name": "Socket2 automatic enable" + }, + "sockets_1_use_time": { + "name": "Socket1 use timer" + }, + "sockets_2_use_time": { + "name": "Socket2 use timer" + } + }, + "number": { + "spare_capacity": { + "name": "Spare Capacity" + }, + "sockets_1_lower_limit": { + "name": "Socket1 lower limit (W)" + }, + "sockets_2_lower_limit": { + "name": "Socket2 lower limit (W)" + }, + "sockets_1_upper_limit": { + "name": "Socket1 upper limit (W)" + }, + "sockets_2_upper_limit": { + "name": "Socket2 upper limit (W)" + }, + "sockets_1_time_limit": { + "name": "Socket1 time limit" + }, + "sockets_2_time_limit": { + "name": "Socket2 time limit" + }, + "sockets_1_switch_on_hour": { + "name": "Socket1 switch on hour" + }, + "sockets_2_switch_on_hour": { + "name": "Socket2 switch on hour" + }, + "sockets_1_switch_on_minute": { + "name": "Socket1 switch on minute" + }, + "sockets_2_switch_on_minute": { + "name": "Socket2 switch on minute" + }, + "sockets_1_power_on_time": { + "name": "Socket1 power on time" + }, + "sockets_2_power_on_time": { + "name": "Socket2 power on time" + } + }, + "binary_sensor": { "wallbox_l1_used": { "name": "Wallbox L1 used" }, @@ -86,6 +144,37 @@ "fan_inv_hv": { "name": "Fan HV-Inverter" }, + "sockets_1_already_switched": { + "name": "Socket1 already switched today" + }, + "sockets_2_already_switched": { + "name": "Socket2 already switched today" + }, + "sockets_1_power_on": { + "name": "Socket1 power on" + }, + "sockets_2_power_on": { + "name": "Socket2 power on" + } + }, + "sensor": { + "gridexport_limit": { + "name": "Grid Export limit" + }, + "peakshaving_mode": { + "name": "Peak Shaving Mode", + "state": { + "deactivated": "Deactivated", + "manual": "Manual", + "auto": "Automatic" + } + }, + "peakshaving_capacitylimit": { + "name": "Peak Shaving battery capacity limit" + }, + "peakshaving_enddate": { + "name": "Peak Shaving end time" + }, "consumption_total": { "name": "House consumed" }, @@ -736,6 +825,18 @@ }, "wallbox_4_set_icmax": { "name": "Wallbox IV set ICMAX" + }, + "sockets_1_priority": { + "name": "Socket1 priority" + }, + "sockets_2_priority": { + "name": "Socket2 priority" + }, + "sockets_1_time_rem": { + "name": "Socket1 time remaining (min)" + }, + "sockets_2_time_rem": { + "name": "Socket2 time remaining (min)" } } } diff --git a/custom_components/senec/switch.py b/custom_components/senec/switch.py index 4865343..8cba627 100644 --- a/custom_components/senec/switch.py +++ b/custom_components/senec/switch.py @@ -56,7 +56,10 @@ def __init__( async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument """Turn on the switch.""" try: - await self.coordinator._async_switch_to_state(self.entity_description.key, True) + if self.entity_description.array_key is not None: + await self.coordinator._async_switch_array_to_state(self.entity_description.array_key, self.entity_description.array_pos, True) + else: + await self.coordinator._async_switch_to_state(self.entity_description.key, True) self.async_schedule_update_ha_state(force_refresh=True) if hasattr(self.entity_description, 'update_after_switch_delay_in_sec') and self.entity_description.update_after_switch_delay_in_sec > 0: await asyncio.sleep(self.entity_description.update_after_switch_delay_in_sec) @@ -69,7 +72,10 @@ async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument """Turn off the switch.""" try: - await self.coordinator._async_switch_to_state(self.entity_description.key, False) + if self.entity_description.array_key is not None: + await self.coordinator._async_switch_array_to_state(self.entity_description.array_key, self.entity_description.array_pos, False) + else: + await self.coordinator._async_switch_to_state(self.entity_description.key, False) self.async_schedule_update_ha_state(force_refresh=True) if hasattr(self.entity_description, 'update_after_switch_delay_in_sec') and self.entity_description.update_after_switch_delay_in_sec > 0: await asyncio.sleep(self.entity_description.update_after_switch_delay_in_sec) @@ -83,7 +89,10 @@ def is_on(self) -> bool | None: """Return true if the binary_sensor is on.""" # return self.coordinator.data.get("title", "") == "foo" try: - value = getattr(self.coordinator.senec, self.entity_description.key) + if self.entity_description.array_key is not None: + value = getattr(self.coordinator.senec, self.entity_description.array_key)[self.entity_description.array_pos] == 1 + else: + value = getattr(self.coordinator.senec, self.entity_description.key) if value is None or value == "": value = None else: diff --git a/custom_components/senec/translations/de.json b/custom_components/senec/translations/de.json index bfb648a..8b8e7a0 100644 --- a/custom_components/senec/translations/de.json +++ b/custom_components/senec/translations/de.json @@ -119,33 +119,74 @@ } }, "entity": { - "sensor": { - "gridexport_limit": { - "name": "Einspeisebegrenzung" + "switch": { + "safe_charge": { + "name": "Akku Volladung" }, - "peakshaving_mode": { - "name": "Peak Shaving Modus", - "state": { - "deactivated": "Deaktiviert", - "manual": "Manuell", - "auto": "Automatisch" - } + "li_storage_mode": { + "name": "Lithium Storage Mode - PV OFF" }, - "peakshaving_capacitylimit": { - "name": "Peak Shaving Akku Begrenzung" + "sockets_1_force_on": { + "name": "Kontakt1 permanent ein" }, - "peakshaving_enddate": { - "name": "Peak Shaving Endzeit" + "sockets_2_force_on": { + "name": "Kontakt2 permanent ein" + }, + "sockets_1_enable": { + "name": "Kontakt1 Automatik aktiviert" + }, + "sockets_2_enable": { + "name": "Kontakt2 Automatik aktiviert" + }, + "sockets_1_use_time": { + "name": "Kontakt1 Schaltzeit aktivieren" }, + "sockets_2_use_time": { + "name": "Kontakt2 Schaltzeit aktivieren" + } + }, + "number": { "spare_capacity": { "name": "Reservekapazität" }, - "safe_charge": { - "name": "Akku Volladung" + "sockets_1_lower_limit": { + "name": "Kontakt1 Einschaltschwelle (W)" }, - "li_storage_mode": { - "name": "Lithium Storage Mode - PV OFF" + "sockets_2_lower_limit": { + "name": "Kontakt2 Einschaltschwelle (W)" + }, + "sockets_1_upper_limit": { + "name": "Kontakt1 Abschaltschwelle (W)" + }, + "sockets_2_upper_limit": { + "name": "Kontakt2 Abschaltschwelle (W)" + }, + "sockets_1_time_limit": { + "name": "Kontakt1 Dauer Leistungsüberschuss (min)" + }, + "sockets_2_time_limit": { + "name": "Kontakt2 Dauer Leistungsüberschuss (min)" + }, + "sockets_1_switch_on_hour": { + "name": "Kontakt1 Einschaltzeit Stunde" + }, + "sockets_2_switch_on_hour": { + "name": "Kontakt2 Einschaltzeit Stunde" }, + "sockets_1_switch_on_minute": { + "name": "Kontakt1 Einschaltzeit Minute" + }, + "sockets_2_switch_on_minute": { + "name": "Kontakt2 Einschaltzeit Minute" + }, + "sockets_1_power_on_time": { + "name": "Kontakt1 Dauer Steckdose ein (min)" + }, + "sockets_2_power_on_time": { + "name": "Kontakt2 Dauer Steckdose ein (min)" + } + }, + "binary_sensor": { "wallbox_l1_used": { "name": "Wallbox L1 in Verwendung" }, @@ -188,6 +229,37 @@ "fan_inv_hv": { "name": "Lüfter HV-Inverter" }, + "sockets_1_already_switched": { + "name": "Kontakt1 heute bereits geschaltet" + }, + "sockets_2_already_switched": { + "name": "Kontakt2 heute bereits geschaltet" + }, + "sockets_1_power_on": { + "name": "Kontakt1 Power ein" + }, + "sockets_2_power_on": { + "name": "Kontakt2 Power ein" + } + }, + "sensor": { + "gridexport_limit": { + "name": "Einspeisebegrenzung" + }, + "peakshaving_mode": { + "name": "Peak Shaving Modus", + "state": { + "deactivated": "Deaktiviert", + "manual": "Manuell", + "auto": "Automatisch" + } + }, + "peakshaving_capacitylimit": { + "name": "Peak Shaving Akku Begrenzung" + }, + "peakshaving_enddate": { + "name": "Peak Shaving Endzeit" + }, "consumption_total": { "name": "Hausverbrauch" }, @@ -838,6 +910,18 @@ }, "wallbox_4_set_icmax": { "name": "Wallbox IV set ICMAX" + }, + "sockets_1_priority": { + "name": "Kontakt1 Priorität" + }, + "sockets_2_priority": { + "name": "Kontakt2 Priorität" + }, + "sockets_1_time_rem": { + "name": "Kontakt1 verbleibende Zeit (min)" + }, + "sockets_2_time_rem": { + "name": "Kontakt2 verbleibende Zeit (min)" } } } diff --git a/custom_components/senec/translations/en.json b/custom_components/senec/translations/en.json index 4fd2b22..bc3171c 100644 --- a/custom_components/senec/translations/en.json +++ b/custom_components/senec/translations/en.json @@ -119,33 +119,74 @@ } }, "entity": { - "sensor": { - "gridexport_limit": { - "name": "Grid Export limit" + "switch": { + "safe_charge": { + "name": "Load Battery" }, - "peakshaving_mode": { - "name": "Peak Shaving Mode", - "state": { - "deactivated": "Deactivated", - "manual": "Manual", - "auto": "Automatic" - } + "li_storage_mode": { + "name": "Lithium Storage Mode - PV OFF" }, - "peakshaving_capacitylimit": { - "name": "Peak Shaving battery capacity limit" + "sockets_1_force_on": { + "name": "Socket1 permanent on" }, - "peakshaving_enddate": { - "name": "Peak Shaving end time" + "sockets_2_force_on": { + "name": "Socket2 permanent on" + }, + "sockets_1_enable": { + "name": "Socket1 automatic enable" + }, + "sockets_2_enable": { + "name": "Socket2 automatic enable" + }, + "sockets_1_use_time": { + "name": "Socket1 use timer" }, + "sockets_2_use_time": { + "name": "Socket2 use timer" + } + }, + "number": { "spare_capacity": { "name": "Spare Capacity" }, - "safe_charge": { - "name": "Load Battery" + "sockets_1_lower_limit": { + "name": "Socket1 lower limit (W)" }, - "li_storage_mode": { - "name": "Lithium Storage Mode - PV OFF" + "sockets_2_lower_limit": { + "name": "Socket2 lower limit (W)" + }, + "sockets_1_upper_limit": { + "name": "Socket1 upper limit (W)" + }, + "sockets_2_upper_limit": { + "name": "Socket2 upper limit (W)" + }, + "sockets_1_time_limit": { + "name": "Socket1 time limit" + }, + "sockets_2_time_limit": { + "name": "Socket2 time limit" + }, + "sockets_1_switch_on_hour": { + "name": "Socket1 switch on hour" + }, + "sockets_2_switch_on_hour": { + "name": "Socket2 switch on hour" }, + "sockets_1_switch_on_minute": { + "name": "Socket1 switch on minute" + }, + "sockets_2_switch_on_minute": { + "name": "Socket2 switch on minute" + }, + "sockets_1_power_on_time": { + "name": "Socket1 power on time" + }, + "sockets_2_power_on_time": { + "name": "Socket2 power on time" + } + }, + "binary_sensor": { "wallbox_l1_used": { "name": "Wallbox L1 used" }, @@ -188,6 +229,37 @@ "fan_inv_hv": { "name": "Fan HV-Inverter" }, + "sockets_1_already_switched": { + "name": "Socket1 already switched today" + }, + "sockets_2_already_switched": { + "name": "Socket2 already switched today" + }, + "sockets_1_power_on": { + "name": "Socket1 power on" + }, + "sockets_2_power_on": { + "name": "Socket2 power on" + } + }, + "sensor": { + "gridexport_limit": { + "name": "Grid Export limit" + }, + "peakshaving_mode": { + "name": "Peak Shaving Mode", + "state": { + "deactivated": "Deactivated", + "manual": "Manual", + "auto": "Automatic" + } + }, + "peakshaving_capacitylimit": { + "name": "Peak Shaving battery capacity limit" + }, + "peakshaving_enddate": { + "name": "Peak Shaving end time" + }, "consumption_total": { "name": "House consumed" }, @@ -838,6 +910,18 @@ }, "wallbox_4_set_icmax": { "name": "Wallbox IV set ICMAX" + }, + "sockets_1_priority": { + "name": "Socket1 priority" + }, + "sockets_2_priority": { + "name": "Socket2 priority" + }, + "sockets_1_time_rem": { + "name": "Socket1 time remaining (min)" + }, + "sockets_2_time_rem": { + "name": "Socket2 time remaining (min)" } } }