Refactor/drop grid charge margin pct - #505
Merged
Merged
Conversation
"Maximum contracted power" only limited charging. An excluded device is subtracted from the grid reading by design, so the PD controller never covers it and the whole load goes to the meter the breaker watches. _apply_icp_excluded_protection projects the physical grid with the same marginal model as the ICP charge clamp (the PD converges sensor_actual to the target, so physical = target + hidden) and hands back just the excess above the contracted power, clamped to the still-hidden excluded share so a peak-shaving add-back is not counted twice. Always on, no new setting. Scope is excluded devices only: cases gated by a discharge blocker (EV charger without telemetry, price blocks, time slots, manual mode) stay as they were. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into release/v1.5.0
) Vende energia reservada para consumo solo cuando cada kWh queda ligado 1:1 a un deficit posterior recomprable y el precio de exportacion supera estrictamente el maximo precio de importacion posterior mas el coste adicional. Todo lo incierto resuelve a "no vender". El horizonte protegido llega inyectado (PricingManager.energy_horizon_end); el planner no busca el cruce solar. Sin dependencia de Home Assistant. Trigger 1 queda fuera: depende del coste de la energia almacenada (#269), aun sin implementar. Tambien fuera el lease/TTL por driver (RF-051): la exportacion deliberada ya funciona hoy via set_setpoint_override sin lease. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adaptador del planner puro: construye el horizonte, cachea el plan (300 s) y convierte la asignación vigente en un setpoint override. - Frontera tz-aware en el módulo de runtime: la capa de precios sigue naive local y `_aware()` reengancha la zona solo al cruzar al planner. - Prioridad 4, por debajo de los dos overrides de curtailment: el riesgo manda sobre la oportunidad. - Un único enganche síncrono al final de `_refresh_operation_blockers`, con los blockers ya reconstruidos. - Reutiliza `consumption_by_slot`, `_profile_remaining_consumption` y `distribute_solar_forecast`; cualquier dato ausente retira el override. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Flujo de configuración y de opciones: activación, potencia máxima neta y coste adicional, en la sección de precios dinámicos. - Dos números declarativos en `CONFIG_NUMBER_DEFINITIONS` (presence-gated por la clave de activación, como sus hermanos) y sensor de diagnóstico `high_price_discharge_status` con el estado operativo y el plan. - Volcado de `get_status()` en diagnostics. - Paridad de traducciones: los 3 campos del flujo y las 3 entidades nuevas, con los seis estados traducidos, en strings.json y los 6 idiomas. - Panel: etiqueta en los 6 mapas I18N, fila de diagnóstico, allowlist `items:` y tonos por estado; el help sigue el patrón en-only del fichero. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La clave de activación se backfillea en el setup, así que las entradas anteriores a la función también obtienen sus entidades sin reabrir el flujo de opciones. Con el interruptor en el sistema (apagado por defecto) la función se activa y se configura desde el dashboard; al apagarlo el override cae en el siguiente ciclo por la puerta de ámbito, sin liberación explícita. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…de arbitraje (#270) `high_price_discharge_additional_cost` y `min_arbitrage_margin` preguntan lo mismo — ¿compensa el diferencial el viaje de ida y vuelta? — solo que desde lados opuestos de la operación. Dos campos por kWh únicamente permitían que las dos respuestas se contradijeran, así que se borra el propio y el runtime lee el que la carga ya usaba. Sin publicar, sin migración. Fuera: la clave, su número, los campos de los dos flujos, la fila del panel con sus 6 etiquetas y su ayuda, y 5 entradas en cada uno de los 7 JSON. La ayuda de `min_arbitrage_margin` y su sección de documentación dicen ahora que rige las dos direcciones, y el diagnóstico nombra la clave que de verdad lee. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rada (#270) `async_unload_entry` limpiaba explícitamente el runtime de anti-vertido y el de precio negativo, y dejaba este puesto. En la práctica las escrituras de apagado paran las baterías, pero RF-036 y el criterio 13 exigen la retirada explícita y la convención está escrita en ese mismo comentario. La retirada normal viaja en el siguiente `refresh_override`; en unload no hay siguiente ciclo, así que `clear_runtime` reutiliza `_release` con el nombre y la forma de sus hermanos del gestor de precios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La funcion tenia entidades y diagnosticos pero ningun control: el flag solo vivia en el flujo de opciones, su clave no estaba en el backfill de setup y el slider de ahorro minimo no estaba en la allowlist del panel. Anade el switch (presence-gated, como el de descarga por precio alto), las dos filas del panel y el nombre del switch en los 7 JSON. El binary sensor pasa a "... Status" para no compartir nombre con el switch, como ya hace la predescarga inteligente. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Unica funcion de precios sin documentacion de usuario: la retencion de excedente y el margen de arbitraje ya la tienen. Nueva subseccion en Price-based discharge control con la regla de recompra, los dos controles, los guards y el sensor de estado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cto (#270) DEFAULT_HIGH_PRICE_DISCHARGE_MAX_POWER = 0.0 combinado con el rechazo de _config() ante potencia <= 0 dejaba la funcion muerta en silencio cuando el usuario activaba el interruptor sin tocar el deslizador. El valor por defecto ahora es la potencia de descarga de la flota (default_high_price_discharge_max_power en const/integration_const.py), resuelto en los tres sitios donde antes se leia la constante estatica: el controlador (__init__.py, arranque y hot-reload), el config/options flow y number.py (valor mostrado en el panel). Un valor de 0 W puesto a mano por el usuario, o el caso de una flota sin baterias configuradas, se siguen respetando: solo cambia el fallback cuando la clave esta ausente de config_entry.data. El estado STATE_INVALID_CONFIGURATION / GUARD_NO_MAX_POWER ya existente cubre el caso legible que pedia la ficha, sin cambios de logica en control/high_price_discharge.py mas alla de documentar el origen del valor. Actualiza el texto de ayuda del deslizador (ya no dice "0 W deja la funcion inactiva") en strings.json + las 6 traducciones + SYS_HELP del panel. Test: tests/test_high_price_discharge_default_power.py Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…el panel (#270) La reserva de descarga por precio traia diagnostico (discharge_reserve_status) pero ningun control fuera del asistente de configuracion: era la unica de las seis funciones economicas de precio dinamico sin superficie en el dashboard. Se anade DischargeReserveSwitch (gemelo literal de SurplusPriceHoldSwitch) y la entrada de discharge_reserve_min_saving en CONFIG_NUMBER_DEFINITIONS, condicionada a la presencia (no al valor) de discharge_reserve_enabled, igual que su gemela surplus_hold_min_saving. Backfill del flag en entradas existentes para que el interruptor aparezca sin reabrir el asistente. El sensor de estado se renombra a "... Status" en las 7 traducciones para no compartir nombre con el nuevo interruptor, siguiendo el mismo patron ya aplicado a surplus_price_hold/surplus_price_hold_status. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…redictiva La tarjeta de carga predictiva mostraba 19 filas de golpe. Los items marcados `adv: true` en SYS_SECTIONS ahora se ocultan con display:none (mismo mecanismo que ya usa `gate` para las filas hijas de un interruptor) salvo que el usuario active "Ajustes avanzados" en la barra de reordenar; el estado se guarda en localStorage por navegador, sin entidad de HA. El mecanismo es generico: cualquier item de cualquier seccion puede marcarse `adv`, y una seccion que se quede sin filas visibles no dibuja su cabecera (misma regla que ya aplicaba cuando una seccion no tiene entidades vivas en el registro). Se marcan como avanzados los 13 deslizadores/umbrales de la carga predictiva por precio mas el par de T2 (solo el numero discharge_reserve_min_saving; el switch discharge_reserve queda visible, igual que el resto de interruptores de funcion). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Los modos Precio Dinamico y Precio en Tiempo Real son excluyentes, asi que dp_price_discharge_control y rt_price_discharge_control eran la misma clase con dos translation_key, dos unique_id y dos entity_id para el mismo comportamiento. Ahora hay una sola entidad price_discharge_control. El controlador expone dp_/rt_price_discharge_control como propiedades de solo lectura que la aliasan, de modo que pricing/engine.py -que lee la que corresponde al modo activo- no se toca. La migracion v12 -> v13 funde el valor de ambas claves (gana la del modo activo) y re-clava en el registro la entidad que corresponde al modo activo sobre el unique_id nuevo, dejando su entity_id -y con el su historial- intacto. Si un cambio de modo anterior dejo las dos entidades, la del modo inactivo se borra en vez de quedar huerfana: las huerfanas ya se han renderizado como controles duplicados en el panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…útil El margen porcentual inflaba el déficit ya calculado, así que cubría más cuanto menos sol había previsto: un seguro grande en días nublados y diminuto en los días soleados en los que una previsión optimista sale cara. El margen en kWh actúa donde está el riesgo (recorta la propia previsión solar) y además lo usan el anti-vertido, la absorción de excedente y la línea de tiempo solar. - Borrado predictive_grid_charge_margin_pct: entidad, clave de config, los dos pasos del flujo y el parámetro de calculate_planned_grid_charge_kwh. Se pierde a propósito el inflado de floor_required, que es un objetivo de SOC y no una previsión. - Migración v13 -> v14: quita la clave y borra la entidad del registro en vez de dejarla huérfana. Un margen puesto a mano no se reescribe. - predictive_safety_margin_kwh deja de tener 0 por defecto: ahora es el 5% de la capacidad de la flota, así que significa lo mismo en una instalación de 5 kWh que en una de 30. Solo para altas nuevas. - Corregido su texto de ayuda en los 6 idiomas y en el panel: decía que se sumaba a la previsión de consumo, y lo que hace es restarse de la previsión solar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ores Cada umbral, interruptor y limite que duplicaban ya es una entidad viva del panel escribiendo las mismas claves de entry.data, asi que Precios Dinamicos pasa de veintiun campos a cinco. Lo que queda es lo que una entidad no puede hacer: validar contra hass.states y recargar plataformas. Los textos de ayuda que vivian en el formulario se trasladan a SYS_HELP del panel en los seis idiomas, para que el ajuste siga explicandose donde ahora se toca. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…270) "Limite de exportacion por precio alto" y "Limite de exportacion de la predescarga" pedian lo mismo que el limite de descarga del sistema, que ya los acota: un deslizador por funcion solo podia contradecirlo. Ahora ambas usan la potencia de descarga de la flota y quien quiera exportar menos baja ese limite (o lo gobierna desde una automatizacion). La reserva de SOC de la predescarga pasa a 20% por defecto: con 0% el anticurtailment podia vaciar la flota hasta mi minimo de cada bateria por una prevision solar que puede no llegar. Migracion v14 -> v15: borra las dos claves, elimina sus entidades del registro y suelta la reserva 0% de las instalaciones que nunca activaron la predescarga, para que alcancen el nuevo defecto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…inamicos Descarga por precio alto, retencion de excedente por precio y reserva de descarga abortan si el modo predictivo no es Precios Dinamicos, pero sus claves se rellenan en toda entrada, asi que la presencia no dice nada del modo activo: en Franjas Horarias y Precio en Tiempo Real aparecian como interruptores, deslizadores y filas de estado muertos en el panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… cada tarjeta Era un boton global de la barra de organizar que reconstruia toda la pestana y escondia la tarjeta entera cuando todas sus filas eran avanzadas. Ahora cada tarjeta con filas `adv` lleva el suyo, con estado propio: las filas se quedan en el DOM y una clase `adv-off` las oculta por CSS, asi que alternar no recrea los deslizadores de las demas tarjetas bajo el raton. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cada item de diagPredictive lleva una etiqueta `grp` y el renderizador dibuja una linea divisoria cuando cambia entre dos filas vivas: base, precios de carga, predescarga, descarga por precio, estado y accion. El separador se oculta con la funcion apagada (gatedNodes) y se marca como avanzado cuando todo el grupo que abre lo es. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Las cuatro filas de estado estaban juntas al final; ahora cada una cierra el grupo de su funcion (interruptor + sliders + estado) y el antiguo grupo "export" se divide en excedente, precio alto y reserva. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El conmutador que cambia lo que muestra la tarjeta encabeza el grupo de iconos; el margen automatico pasa a el y la separacion se recalcula desde el nuevo orden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ecto El controlador caía al 5% de la capacidad de la flota cuando la clave no estaba en config, pero la entidad number seguía mostrando el "default" autorizado (0.0). En un alta nueva el panel decía 0.0 kWh mientras el motor recortaba la previsión con otra cifra: un valor oculto. - "default" admite un callable y native_value lo resuelve, igual que dynamic_bounds hace con min/max. - Un margen ya guardado (incluido un 0.0 explícito) sigue ganando: el defecto solo entra si la clave no está, así que ninguna configuración existente se reescribe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
187cf94 anadio la migracion v14 -> v15 pero dejo el `return` anticipado en `entry.version >= 14`: una instalacion ya en v14 salia antes de ejecutarla, se quedaba con las claves de potencia de exportacion y nunca llegaba a v15. Solo migraban las entradas por debajo de v14. Los dos tests de flujo HA seguian afirmando la version 14 (de ahi el fallo de pytest en el PR); el noop de la guarda ahora lee `MarstekVenusConfigFlow.VERSION` en vez de un numero fijo, que es lo que dejo pasar el despiste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ffunes
added a commit
that referenced
this pull request
Sep 22, 2026
* feat(icp): cover excluded-device load above the contracted power "Maximum contracted power" only limited charging. An excluded device is subtracted from the grid reading by design, so the PD controller never covers it and the whole load goes to the meter the breaker watches. _apply_icp_excluded_protection projects the physical grid with the same marginal model as the ICP charge clamp (the PD converges sensor_actual to the target, so physical = target + hidden) and hands back just the excess above the contracted power, clamped to the still-hidden excluded share so a peak-shaving add-back is not counted twice. Always on, no new setting. Scope is excluded devices only: cases gated by a discharge blocker (EV charger without telemetry, price blocks, time slots, manual mode) stay as they were. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(changelog): note the excluded-device contracted-power guard Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: update changelog * feat(pricing): planner puro de descarga por precio alto (trigger 2, #270) Vende energia reservada para consumo solo cuando cada kWh queda ligado 1:1 a un deficit posterior recomprable y el precio de exportacion supera estrictamente el maximo precio de importacion posterior mas el coste adicional. Todo lo incierto resuelve a "no vender". El horizonte protegido llega inyectado (PricingManager.energy_horizon_end); el planner no busca el cruce solar. Sin dependencia de Home Assistant. Trigger 1 queda fuera: depende del coste de la energia almacenada (#269), aun sin implementar. Tambien fuera el lease/TTL por driver (RF-051): la exportacion deliberada ya funciona hoy via set_setpoint_override sin lease. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(pricing): runtime de descarga por precios altos (trigger 2, #270) Adaptador del planner puro: construye el horizonte, cachea el plan (300 s) y convierte la asignación vigente en un setpoint override. - Frontera tz-aware en el módulo de runtime: la capa de precios sigue naive local y `_aware()` reengancha la zona solo al cruzar al planner. - Prioridad 4, por debajo de los dos overrides de curtailment: el riesgo manda sobre la oportunidad. - Un único enganche síncrono al final de `_refresh_operation_blockers`, con los blockers ya reconstruidos. - Reutiliza `consumption_by_slot`, `_profile_remaining_consumption` y `distribute_solar_forecast`; cualquier dato ausente retira el override. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(pricing): superficie de la descarga por precios altos (#270) - Flujo de configuración y de opciones: activación, potencia máxima neta y coste adicional, en la sección de precios dinámicos. - Dos números declarativos en `CONFIG_NUMBER_DEFINITIONS` (presence-gated por la clave de activación, como sus hermanos) y sensor de diagnóstico `high_price_discharge_status` con el estado operativo y el plan. - Volcado de `get_status()` en diagnostics. - Paridad de traducciones: los 3 campos del flujo y las 3 entidades nuevas, con los seis estados traducidos, en strings.json y los 6 idiomas. - Panel: etiqueta en los 6 mapas I18N, fila de diagnóstico, allowlist `items:` y tonos por estado; el help sigue el patrón en-only del fichero. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(pricing): toggle de descarga por precios altos en el panel (#270) La clave de activación se backfillea en el setup, así que las entradas anteriores a la función también obtienen sus entidades sin reabrir el flujo de opciones. Con el interruptor en el sistema (apagado por defecto) la función se activa y se configura desde el dashboard; al apagarlo el override cae en el siguiente ciclo por la puerta de ámbito, sin liberación explícita. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(pricing): la descarga por precios altos reutiliza el margen de arbitraje (#270) `high_price_discharge_additional_cost` y `min_arbitrage_margin` preguntan lo mismo — ¿compensa el diferencial el viaje de ida y vuelta? — solo que desde lados opuestos de la operación. Dos campos por kWh únicamente permitían que las dos respuestas se contradijeran, así que se borra el propio y el runtime lee el que la carga ya usaba. Sin publicar, sin migración. Fuera: la clave, su número, los campos de los dos flujos, la fila del panel con sus 6 etiquetas y su ayuda, y 5 entradas en cada uno de los 7 JSON. La ayuda de `min_arbitrage_margin` y su sección de documentación dicen ahora que rige las dos direcciones, y el diagnóstico nombra la clave que de verdad lee. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(pricing): soltar el override de precios altos al descargar la entrada (#270) `async_unload_entry` limpiaba explícitamente el runtime de anti-vertido y el de precio negativo, y dejaba este puesto. En la práctica las escrituras de apagado paran las baterías, pero RF-036 y el criterio 13 exigen la retirada explícita y la convención está escrita en ese mismo comentario. La retirada normal viaja en el siguiente `refresh_override`; en unload no hay siguiente ciclo, así que `clear_runtime` reutiliza `_release` con el nombre y la forma de sus hermanos del gestor de precios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(pricing): superficie de la retencion de excedente por precio La funcion tenia entidades y diagnosticos pero ningun control: el flag solo vivia en el flujo de opciones, su clave no estaba en el backfill de setup y el slider de ahorro minimo no estaba en la allowlist del panel. Anade el switch (presence-gated, como el de descarga por precio alto), las dos filas del panel y el nombre del switch en los 7 JSON. El binary sensor pasa a "... Status" para no compartir nombre con el switch, como ya hace la predescarga inteligente. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(pricing): documentar la descarga por precio alto (#270, RF-049) Unica funcion de precios sin documentacion de usuario: la retencion de excedente y el margen de arbitraje ya la tienen. Nueva subseccion en Price-based discharge control con la regla de recompra, los dos controles, los guards y el sensor de estado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(pricing): la descarga por precio alto ya no muere en 0 W por defecto (#270) DEFAULT_HIGH_PRICE_DISCHARGE_MAX_POWER = 0.0 combinado con el rechazo de _config() ante potencia <= 0 dejaba la funcion muerta en silencio cuando el usuario activaba el interruptor sin tocar el deslizador. El valor por defecto ahora es la potencia de descarga de la flota (default_high_price_discharge_max_power en const/integration_const.py), resuelto en los tres sitios donde antes se leia la constante estatica: el controlador (__init__.py, arranque y hot-reload), el config/options flow y number.py (valor mostrado en el panel). Un valor de 0 W puesto a mano por el usuario, o el caso de una flota sin baterias configuradas, se siguen respetando: solo cambia el fallback cuando la clave esta ausente de config_entry.data. El estado STATE_INVALID_CONFIGURATION / GUARD_NO_MAX_POWER ya existente cubre el caso legible que pedia la ficha, sin cambios de logica en control/high_price_discharge.py mas alla de documentar el origen del valor. Actualiza el texto de ayuda del deslizador (ya no dice "0 W deja la funcion inactiva") en strings.json + las 6 traducciones + SYS_HELP del panel. Test: tests/test_high_price_discharge_default_power.py Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(pricing): interruptor y deslizador de la reserva de descarga en el panel (#270) La reserva de descarga por precio traia diagnostico (discharge_reserve_status) pero ningun control fuera del asistente de configuracion: era la unica de las seis funciones economicas de precio dinamico sin superficie en el dashboard. Se anade DischargeReserveSwitch (gemelo literal de SurplusPriceHoldSwitch) y la entrada de discharge_reserve_min_saving en CONFIG_NUMBER_DEFINITIONS, condicionada a la presencia (no al valor) de discharge_reserve_enabled, igual que su gemela surplus_hold_min_saving. Backfill del flag en entradas existentes para que el interruptor aparezca sin reabrir el asistente. El sensor de estado se renombra a "... Status" en las 7 traducciones para no compartir nombre con el nuevo interruptor, siguiendo el mismo patron ya aplicado a surplus_price_hold/surplus_price_hold_status. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(panel): conmutador de Ajustes avanzados en la tarjeta de carga predictiva La tarjeta de carga predictiva mostraba 19 filas de golpe. Los items marcados `adv: true` en SYS_SECTIONS ahora se ocultan con display:none (mismo mecanismo que ya usa `gate` para las filas hijas de un interruptor) salvo que el usuario active "Ajustes avanzados" en la barra de reordenar; el estado se guarda en localStorage por navegador, sin entidad de HA. El mecanismo es generico: cualquier item de cualquier seccion puede marcarse `adv`, y una seccion que se quede sin filas visibles no dibuja su cabecera (misma regla que ya aplicaba cuando una seccion no tiene entidades vivas en el registro). Se marcan como avanzados los 13 deslizadores/umbrales de la carga predictiva por precio mas el par de T2 (solo el numero discharge_reserve_min_saving; el switch discharge_reserve queda visible, igual que el resto de interruptores de funcion). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(pricing): un solo interruptor de descarga segun precio (#270) Los modos Precio Dinamico y Precio en Tiempo Real son excluyentes, asi que dp_price_discharge_control y rt_price_discharge_control eran la misma clase con dos translation_key, dos unique_id y dos entity_id para el mismo comportamiento. Ahora hay una sola entidad price_discharge_control. El controlador expone dp_/rt_price_discharge_control como propiedades de solo lectura que la aliasan, de modo que pricing/engine.py -que lee la que corresponde al modo activo- no se toca. La migracion v12 -> v13 funde el valor de ambas claves (gana la del modo activo) y re-clava en el registro la entidad que corresponde al modo activo sobre el unique_id nuevo, dejando su entity_id -y con el su historial- intacto. Si un cambio de modo anterior dejo las dos entidades, la del modo inactivo se borra en vez de quedar huerfana: las huerfanas ya se han renderizado como controles duplicados en el panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(predictive): un solo margen de previsión solar, con defecto útil El margen porcentual inflaba el déficit ya calculado, así que cubría más cuanto menos sol había previsto: un seguro grande en días nublados y diminuto en los días soleados en los que una previsión optimista sale cara. El margen en kWh actúa donde está el riesgo (recorta la propia previsión solar) y además lo usan el anti-vertido, la absorción de excedente y la línea de tiempo solar. - Borrado predictive_grid_charge_margin_pct: entidad, clave de config, los dos pasos del flujo y el parámetro de calculate_planned_grid_charge_kwh. Se pierde a propósito el inflado de floor_required, que es un objetivo de SOC y no una previsión. - Migración v13 -> v14: quita la clave y borra la entidad del registro en vez de dejarla huérfana. Un margen puesto a mano no se reescribe. - predictive_safety_margin_kwh deja de tener 0 por defecto: ahora es el 5% de la capacidad de la flota, así que significa lo mismo en una instalación de 5 kWh que en una de 30. Solo para altas nuevas. - Corregido su texto de ayuda en los 6 idiomas y en el panel: decía que se sumaba a la previsión de consumo, y lo que hace es restarse de la previsión solar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(config): los formularios de carga predictiva solo piden sensores Cada umbral, interruptor y limite que duplicaban ya es una entidad viva del panel escribiendo las mismas claves de entry.data, asi que Precios Dinamicos pasa de veintiun campos a cinco. Lo que queda es lo que una entidad no puede hacer: validar contra hass.states y recargar plataformas. Los textos de ayuda que vivian en el formulario se trasladan a SYS_HELP del panel en los seis idiomas, para que el ajuste siga explicandose donde ahora se toca. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(pricing): fuera los deslizadores de potencia de exportacion (#270) "Limite de exportacion por precio alto" y "Limite de exportacion de la predescarga" pedian lo mismo que el limite de descarga del sistema, que ya los acota: un deslizador por funcion solo podia contradecirlo. Ahora ambas usan la potencia de descarga de la flota y quien quiera exportar menos baja ese limite (o lo gobierna desde una automatizacion). La reserva de SOC de la predescarga pasa a 20% por defecto: con 0% el anticurtailment podia vaciar la flota hasta mi minimo de cada bateria por una prevision solar que puede no llegar. Migracion v14 -> v15: borra las dos claves, elimina sus entidades del registro y suelta la reserva 0% de las instalaciones que nunca activaron la predescarga, para que alcancen el nuevo defecto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(pricing): las funciones de precio solo se registran con precios dinamicos Descarga por precio alto, retencion de excedente por precio y reserva de descarga abortan si el modo predictivo no es Precios Dinamicos, pero sus claves se rellenan en toda entrada, asi que la presencia no dice nada del modo activo: en Franjas Horarias y Precio en Tiempo Real aparecian como interruptores, deslizadores y filas de estado muertos en el panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(panel): el conmutador de Ajustes avanzados pasa a la cabecera de cada tarjeta Era un boton global de la barra de organizar que reconstruia toda la pestana y escondia la tarjeta entera cuando todas sus filas eran avanzadas. Ahora cada tarjeta con filas `adv` lleva el suyo, con estado propio: las filas se quedan en el DOM y una clase `adv-off` las oculta por CSS, asi que alternar no recrea los deslizadores de las demas tarjetas bajo el raton. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: ignora .claude/settings.json Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(panel): separa por grupos la tarjeta de carga predictiva Cada item de diagPredictive lleva una etiqueta `grp` y el renderizador dibuja una linea divisoria cuando cambia entre dos filas vivas: base, precios de carga, predescarga, descarga por precio, estado y accion. El separador se oculta con la funcion apagada (gatedNodes) y se marca como avanzado cuando todo el grupo que abre lo es. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(panel): cada estado vuelve al grupo de su funcion Las cuatro filas de estado estaban juntas al final; ahora cada una cierra el grupo de su funcion (interruptor + sliders + estado) y el antiguo grupo "export" se divide en excedente, precio alto y reserva. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(panel): el boton de ajustes avanzados va antes que el de info El conmutador que cambia lo que muestra la tarjeta encabeza el grupo de iconos; el margen automatico pasa a el y la separacion se recalcula desde el nuevo orden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(predictive): el deslizador del margen solar muestra su propio defecto El controlador caía al 5% de la capacidad de la flota cuando la clave no estaba en config, pero la entidad number seguía mostrando el "default" autorizado (0.0). En un alta nueva el panel decía 0.0 kWh mientras el motor recortaba la previsión con otra cifra: un valor oculto. - "default" admite un callable y native_value lo resuelve, igual que dynamic_bounds hace con min/max. - Un margen ya guardado (incluido un 0.0 explícito) sigue ganando: el defecto solo entra si la clave no está, así que ninguna configuración existente se reescribe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(changelog): el 5% por defecto se ve en el deslizador Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(migration): la guarda de salida anticipada se quedo en la v14 edaed0d anadio la migracion v14 -> v15 pero dejo el `return` anticipado en `entry.version >= 14`: una instalacion ya en v14 salia antes de ejecutarla, se quedaba con las claves de potencia de exportacion y nunca llegaba a v15. Solo migraban las entradas por debajo de v14. Los dos tests de flujo HA seguian afirmando la version 14 (de ahi el fallo de pytest en el PR); el noop de la guarda ahora lee `MarstekVenusConfigFlow.VERSION` en vez de un numero fijo, que es lo que dejo pasar el despiste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Approved roadmap item
Discussion / issue: #
Tests