diff --git a/custom_components/lock_code_manager/__init__.py b/custom_components/lock_code_manager/__init__.py index 7fd1be29..5c51e751 100644 --- a/custom_components/lock_code_manager/__init__.py +++ b/custom_components/lock_code_manager/__init__.py @@ -129,6 +129,7 @@ PER_LOCK_ISSUE_KEYS, build_pin_deobfuscation_map, deobfuscate_pins, + lock_display_name, per_lock_issue_id, ) from .entity import build_slot_device_info @@ -307,6 +308,7 @@ async def async_migrate_entry( len(renamed), ", ".join(sorted(renamed, key=int)), ) + _async_remove_hub_device(hass, config_entry) # Last, and part of the same version bump: from here on a slot # leaving the configuration takes its credential with it, so the # codes earlier versions left behind are offered up once, measured @@ -731,7 +733,6 @@ async def async_setup_entry( ) -> bool: """Set up a config entry.""" ent_reg = er.async_get(hass) - entry_id = config_entry.entry_id try: entity_id = next( entity_id @@ -753,14 +754,6 @@ async def async_setup_entry( config=EntryConfig.from_entry(config_entry), ) - dev_reg = dr.async_get(hass) - dev_reg.async_get_or_create( - config_entry_id=entry_id, - identifiers={(DOMAIN, entry_id)}, - manufacturer="Lock Code Manager", - name=config_entry.title, - serial_number=entry_id, - ) _async_reclaim_entities_from_foreign_devices(hass, config_entry) _async_prune_orphaned_slot_devices(hass, config_entry) @@ -1081,6 +1074,34 @@ def _is_ours(device: dr.DeviceEntry) -> bool: dev_reg.async_remove_device(device.id) +@callback +def _async_remove_hub_device( + hass: HomeAssistant, config_entry: LockCodeManagerConfigEntry +) -> None: + """ + Take away the config entry's own device. + + It never held an entity. It existed so the per-user devices could name + it in ``via_device`` and be drawn beneath it, which bought a line on a + device page and cost a device on every page that lists them. The users + are the devices worth having. + + The ``via_device`` goes with it, and had to: Home Assistant reports a + ``via_device`` naming a device that is not there as a use it intends to + break, so leaving it behind would log on every registration. + """ + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_device(identifiers={(DOMAIN, config_entry.entry_id)}) + if device is None: + return + _LOGGER.debug( + "%s (%s): Removing the config entry's own device; it holds no entities", + config_entry.entry_id, + config_entry.title, + ) + dev_reg.async_remove_device(device.id) + + @callback def _async_rename_event_unique_ids( hass: HomeAssistant, config_entry: LockCodeManagerConfigEntry @@ -1123,16 +1144,6 @@ def _lock_of(entry_id: str, unique_id: str) -> str | None: return parts[3] if unique_id.startswith(f"{entry_id}|") and len(parts) > 3 else None -def _lock_display_name(ent_reg: er.EntityRegistry, lock_entity_id: str) -> str: - """Name a lock the way its own integration does.""" - entity = ent_reg.async_get(lock_entity_id) - if entity and (display := entity.name or entity.original_name): - return display - # No registry row to ask, so fall back to the object id, which is what - # the lock's own entity id was slugged from in the first place. - return lock_entity_id.split(".", 1)[-1].replace("_", " ") - - @callback def _async_purge_dropped_slots( hass: HomeAssistant, @@ -1199,7 +1210,7 @@ def _async_rename_slot_entity_ids( # was created, which on an upgraded install predates this shape. suggested = ( f"{config_entry.title} {name} " - f"{_lock_display_name(ent_reg, lock_entity_id)} " + f"{lock_display_name(hass, lock_entity_id)} " f"{PER_LOCK_ENTITY_SUFFIX[entity.unique_id.split('|')[2]]}" ) elif entity.original_name: @@ -1210,17 +1221,20 @@ def _async_rename_slot_entity_ids( # on the entry's title, which may have changed since. suggested = f"{config_entry.title} {name} {entity.original_name}" else: - # The event entity has no name of its own, and a registry written - # by an older Home Assistant may not have kept one. Swap the - # device slug instead, which needs the ID to still look like one - # this integration generated. + # A registry row written by an older Home Assistant may have kept + # no name at all, so there is nothing to append the way the branch + # above does. The key inside the unique ID says what the entity is + # -- it is what the translated name is looked up by -- and unlike + # the old object id it is there even when the entity was created + # without a name, which is how the credential-used event was. + # + # Guarded on the ID still looking like one this integration + # generated, so a hand-renamed entity is left alone. old_prefix = slugify(f"{config_entry.title} Code slot {slot_num}") if object_id != old_prefix and not object_id.startswith(f"{old_prefix}_"): continue - suggested = ( - f"{slugify(f'{config_entry.title} {name}')}" - f"{object_id.removeprefix(old_prefix)}" - ) + key = entity.unique_id.split("|")[2] + suggested = f"{config_entry.title} {name} {key.replace('_', ' ')}" new_entity_id = ent_reg.async_get_available_entity_id( domain, suggested, current_entity_id=entity.entity_id ) @@ -1428,7 +1442,24 @@ async def _setup_one_lock(lock_entity_id: str) -> BaseLock: async def async_update_listener( hass: HomeAssistant, config_entry: LockCodeManagerConfigEntry ) -> None: - """Update listener.""" + """ + Update listener. + + Wraps the pass so ``runtime_data.settled`` is set however it ends, + including the early return and a failure. Anything waiting on it is + waiting to learn that the entry has finished reacting, and a pass that + raised has finished reacting as much as it is going to. + """ + try: + await _async_apply_entry_update(hass, config_entry) + finally: + config_entry.runtime_data.settled.set() + + +async def _async_apply_entry_update( + hass: HomeAssistant, config_entry: LockCodeManagerConfigEntry +) -> None: + """Bring entities, devices and locks into line with the entry.""" # Refresh the cached EntryConfig on EVERY update — including entity-driven # writes that go straight to data with empty options (e.g. a slot's name or # PIN being edited via its text entity). The early-return below skips the diff --git a/custom_components/lock_code_manager/domain/models.py b/custom_components/lock_code_manager/domain/models.py index 0168acd7..099652fc 100644 --- a/custom_components/lock_code_manager/domain/models.py +++ b/custom_components/lock_code_manager/domain/models.py @@ -156,6 +156,12 @@ class LockCodeManagerConfigEntryRuntimeData: # entry. Guards against stacking when _setup_entry_after_start runs more # than once (for example, a reload racing with EVENT_HOMEASSISTANT_STARTED). update_listener_registered: bool = False + # Set whenever the update listener finishes a pass. Home Assistant runs + # update listeners as a task rather than awaiting them, so a caller that + # writes to the entry returns before the entry has reacted -- before the + # entities for a user it just added exist. Anything that must not return + # early clears this, writes, and waits for it. + settled: asyncio.Event = field(default_factory=asyncio.Event) # (lock, slot) pairs whose credential is to be left on the lock when the # slot leaves the configuration, set by the delete-user service and drained # by the update listener. A hand-off cannot be expressed in the new diff --git a/custom_components/lock_code_manager/domain/services.py b/custom_components/lock_code_manager/domain/services.py index 836f8bf2..3c1a2d1a 100644 --- a/custom_components/lock_code_manager/domain/services.py +++ b/custom_components/lock_code_manager/domain/services.py @@ -2,8 +2,11 @@ from __future__ import annotations +import asyncio +import logging from typing import Any +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CONDITION, CONF_ENABLED, CONF_PIN from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError @@ -16,6 +19,50 @@ from .names import identity, name_error, normalize_name from .queries import get_entry_config, get_loaded_config_entry +_LOGGER = logging.getLogger(__name__) + +# How long to wait for the entry to react to a write before returning +# anyway. Generous, because the pass can set up a lock it has never +# spoken to; a caller stuck here is waiting on that, not on this. +_SETTLE_TIMEOUT = 30 + + +async def _async_write_and_settle( + hass: HomeAssistant, config_entry: ConfigEntry, options: dict[str, Any] +) -> None: + """ + Write the entry, and wait for it to have finished reacting. + + ``async_update_entry`` schedules the update listener rather than + awaiting it, so without this a service returns before the entities for + the users it changed exist. A script that adds a user and then sets + their PIN through the new text entity would find nothing to set. + + Waiting on the entry's settle event rather than on the specific + entities keeps this honest about what it can promise: another write + landing at the same time can release the wait early. That is no worse + than not waiting, which is the alternative. + """ + runtime_data = config_entry.runtime_data + runtime_data.settled.clear() + if not hass.config_entries.async_update_entry(config_entry, options=options): + # Nothing changed, so no listener will run and nothing will set the + # event. Waiting here would burn the whole timeout for no reason. + return + try: + async with asyncio.timeout(_SETTLE_TIMEOUT): + await runtime_data.settled.wait() + except TimeoutError: + # The write itself is durable, so this is not a failure to report to + # the caller -- the entities will appear when the pass finishes. + _LOGGER.warning( + "%s (%s): entry did not finish updating within %ss; entities for " + "this change may appear late", + config_entry.entry_id, + config_entry.title, + _SETTLE_TIMEOUT, + ) + async def async_set_usercode( hass: HomeAssistant, lock_entity_id: str, code_slot: int, usercode: str @@ -71,7 +118,7 @@ async def async_set_slot_condition( _async_validate_condition(hass, entity_id) new_config = config.with_slot_field_set(slot, CONF_CONDITION, entity_id) - hass.config_entries.async_update_entry(config_entry, options=new_config.to_dict()) + await _async_write_and_settle(hass, config_entry, new_config.to_dict()) async def async_clear_slot_condition( @@ -88,7 +135,7 @@ async def async_clear_slot_condition( raise ServiceValidationError(f"Slot {slot} not found in config entry") new_config = config.with_slot_field_removed(slot, CONF_CONDITION) - hass.config_entries.async_update_entry(config_entry, options=new_config.to_dict()) + await _async_write_and_settle(hass, config_entry, new_config.to_dict()) async def async_add_user( @@ -153,9 +200,10 @@ async def async_add_user( assignment = config.assignment.reconcile( [*config.users, name], start=1, unavailable=unavailable ) - hass.config_entries.async_update_entry( + await _async_write_and_settle( + hass, config_entry, - options=EntryConfig( + EntryConfig( locks=config.locks, users={**config.users, name: user}, assignment=assignment, @@ -201,9 +249,10 @@ async def async_delete_user( } # No unavailable set and so no lock read: a departure issues no numbers, # and everyone remaining keeps theirs by tenure. - hass.config_entries.async_update_entry( + await _async_write_and_settle( + hass, config_entry, - options=EntryConfig( + EntryConfig( locks=config.locks, users=remaining, assignment=config.assignment.reconcile(remaining, start=1), diff --git a/custom_components/lock_code_manager/domain/util.py b/custom_components/lock_code_manager/domain/util.py index 2b01dd0c..e39b55ee 100644 --- a/custom_components/lock_code_manager/domain/util.py +++ b/custom_components/lock_code_manager/domain/util.py @@ -10,14 +10,54 @@ from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_OFF from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_ENTITY_ID, CONF_ENABLED, CONF_PIN -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + CONF_ENABLED, + CONF_PIN, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from ..const import DOMAIN from .config import EntryConfig, build_slot_unique_id + +@callback +def lock_display_name(hass: HomeAssistant, lock_entity_id: str) -> str: + """ + Name a lock the way Home Assistant does. + + The friendly name first, because that is the string on screen and the + one a person would use to say which lock they mean. Everything after + it reconstructs that name for a lock with no state yet, in the order + Home Assistant composes it. + + The device step is not a nicety. A lock entity that sets + ``has_entity_name`` with no name of its own -- which is how Z-Wave, + Matter and most modern integrations do it -- carries an empty + ``original_name`` and takes its whole visible name from its device. + Stopping at the registry row names such a lock ``lock.front_door``. + """ + if (state := hass.states.get(lock_entity_id)) and ( + friendly_name := state.attributes.get(ATTR_FRIENDLY_NAME) + ): + return str(friendly_name) + if entity := er.async_get(hass).async_get(lock_entity_id): + if name := entity.name or entity.original_name: + return name + if ( + entity.device_id + and (device := dr.async_get(hass).async_get(entity.device_id)) + and (device_name := device.name_by_user or device.name) + ): + return device_name + # Nothing left to ask, so fall back to the object id, which is what the + # lock's own entity id was slugged from in the first place. + return lock_entity_id.split(".", 1)[-1].replace("_", " ") + + _LOGGER = logging.getLogger(__name__) # Every repair issue keyed on a lock entity id. Creation sites live in diff --git a/custom_components/lock_code_manager/entity.py b/custom_components/lock_code_manager/entity.py index fa9b0577..be25d6af 100644 --- a/custom_components/lock_code_manager/entity.py +++ b/custom_components/lock_code_manager/entity.py @@ -61,7 +61,6 @@ def build_slot_device_info(config_entry: ConfigEntry, slot_num: int) -> DeviceIn name=f"{config_entry.title} {name}", manufacturer="Lock Code Manager", model="User", - via_device=(DOMAIN, config_entry.entry_id), ) diff --git a/custom_components/lock_code_manager/event.py b/custom_components/lock_code_manager/event.py index 0afdbaf3..f5dee0a3 100644 --- a/custom_components/lock_code_manager/event.py +++ b/custom_components/lock_code_manager/event.py @@ -72,7 +72,6 @@ def __init__( BaseLockCodeManagerEntity.__init__( self, hass, ent_reg, config_entry, slot_num, key ) - self._attr_name = None def _get_supported_locks(self) -> list[BaseLock]: """Get locks that support code slot events.""" diff --git a/custom_components/lock_code_manager/providers/_base.py b/custom_components/lock_code_manager/providers/_base.py index 8d34588d..3c64a54f 100644 --- a/custom_components/lock_code_manager/providers/_base.py +++ b/custom_components/lock_code_manager/providers/_base.py @@ -72,7 +72,7 @@ ) from ..domain.models import SlotCredential from ..domain.queries import find_entry_for_lock_slot, get_managed_slots -from ..domain.util import mask_pin, per_lock_issue_id +from ..domain.util import lock_display_name, mask_pin, per_lock_issue_id from ._util import make_tagged_name, parse_tag from .const import LOGGER @@ -401,7 +401,7 @@ def _raise_not_implemented(self, method_name: str, guidance: str = "") -> NoRetu @property def display_name(self) -> str: """Return a human-readable name for this lock.""" - return self.lock.name or self.lock.original_name or self.lock.entity_id + return lock_display_name(self.hass, self.lock.entity_id) def mask_pin(self, pin: str | None, code_slot: int | str = 0) -> str: """Return a masked representation of a PIN for logging.""" diff --git a/custom_components/lock_code_manager/strings.json b/custom_components/lock_code_manager/strings.json index 99cc0101..570cd475 100644 --- a/custom_components/lock_code_manager/strings.json +++ b/custom_components/lock_code_manager/strings.json @@ -90,6 +90,7 @@ }, "event": { "credential_used": { + "name": "Credential used", "state_attributes": { "event_type": { "state": { diff --git a/custom_components/lock_code_manager/translations/en.json b/custom_components/lock_code_manager/translations/en.json index 99cc0101..570cd475 100644 --- a/custom_components/lock_code_manager/translations/en.json +++ b/custom_components/lock_code_manager/translations/en.json @@ -90,6 +90,7 @@ }, "event": { "credential_used": { + "name": "Credential used", "state_attributes": { "event_type": { "state": { diff --git a/custom_components/lock_code_manager/www/generated/lock-code-manager.js b/custom_components/lock_code_manager/www/generated/lock-code-manager.js index 28c5aa3d..156026a9 100644 --- a/custom_components/lock_code_manager/www/generated/lock-code-manager.js +++ b/custom_components/lock_code_manager/www/generated/lock-code-manager.js @@ -1 +1 @@ -var n,e,t,i,o,r,a,s,l,c,d,h,u,p,v,g,_,f,m,y,b,w,x,k,C,E,S,A,$,L,P,M,z,N,O,R,H,T,I,D,U,j,V,B,F,W,q,Z,K,G,Y,X,J,Q,nn,en,tn,on,rn,an,sn,ln,cn,dn,hn,un,pn,vn,gn,_n,fn,mn,yn,bn,wn,xn,kn,Cn,En,Sn,An,$n,Ln,Pn,Mn,zn,Nn,On,Rn,Hn,Tn,In,Dn,Un,jn,Vn=["config_entry_title"],Bn=["slot","user_entity_id"];function Fn(n,e){if(null==n)return{};var t,i,o=function(n,e){if(null==n)return{};var t={};for(var i in n)if({}.hasOwnProperty.call(n,i)){if(-1!==e.indexOf(i))continue;t[i]=n[i]}return t}(n,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(n);for(i=0;in.length)&&(e=n.length);for(var t=0,i=Array(e);t1?e-1:0),i=1;ie+(n=>{if(!0===n._$cssResult$)return n.cssText;if("number"==typeof n)return n;throw Error("Value passed to 'css' function must be a 'css' function result: "+n+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(t)+n[i+1]),n[0]);return new ie(o,n,ee)},re=ne?n=>n:n=>n instanceof CSSStyleSheet?(n=>{var e="";for(var t of n.cssRules)e+=t.cssText;return(n=>new ie("string"==typeof n?n:n+"",void 0,ee))(e)})(n):n,ae=Object.is,se=Object.defineProperty,le=Object.getOwnPropertyDescriptor,ce=Object.getOwnPropertyNames,de=Object.getOwnPropertySymbols,he=Object.getPrototypeOf,ue=globalThis,pe=ue.trustedTypes,ve=pe?pe.emptyScript:"",ge=ue.reactiveElementPolyfillSupport,_e=(n,e)=>n,fe={toAttribute(n,e){switch(e){case Boolean:n=n?ve:null;break;case Object:case Array:n=null==n?n:JSON.stringify(n)}return n},fromAttribute(n,e){var t=n;switch(e){case Boolean:t=null!==n;break;case Number:t=null===n?null:Number(n);break;case Object:case Array:try{t=JSON.parse(n)}catch(n){t=null}}return t}},me=(n,e)=>!ae(n,e),ye={attribute:!0,type:String,converter:fe,reflect:!1,useDefault:!1,hasChanged:me};null!==(n=Symbol.metadata)&&void 0!==n||(Symbol.metadata=Symbol("metadata")),null!==(e=ue.litPropertyMetadata)&&void 0!==e||(ue.litPropertyMetadata=new WeakMap);var be=class extends HTMLElement{static addInitializer(n){var e;this._$Ei(),(null!==(e=this.l)&&void 0!==e?e:this.l=[]).push(n)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:ye;if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(n)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(n,e),!e.noAccessor){var t=Symbol(),i=this.getPropertyDescriptor(n,t,e);void 0!==i&&se(this.prototype,n,i)}}static getPropertyDescriptor(n,e,t){var i,o=null!==(i=le(this.prototype,n))&&void 0!==i?i:{get(){return this[e]},set(n){this[e]=n}},r=o.get,a=o.set;return{get:r,set(e){var i=null==r?void 0:r.call(this);null!=a&&a.call(this,e),this.requestUpdate(n,i,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(n){var e;return null!==(e=this.elementProperties.get(n))&&void 0!==e?e:ye}static _$Ei(){if(!this.hasOwnProperty(_e("elementProperties"))){var n=he(this);n.finalize(),void 0!==n.l&&(this.l=[...n.l]),this.elementProperties=new Map(n.elementProperties)}}static finalize(){if(!this.hasOwnProperty(_e("finalized"))){if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(_e("properties"))){var n=this.properties,e=[...ce(n),...de(n)];for(var t of e)this.createProperty(t,n[t])}var i=this[Symbol.metadata];if(null!==i){var o=litPropertyMetadata.get(i);if(void 0!==o)for(var r of o){var a=Xn(r,2),s=a[0],l=a[1];this.elementProperties.set(s,l)}}for(var c of(this._$Eh=new Map,this.elementProperties)){var d=Xn(c,2),h=d[0],u=d[1],p=this._$Eu(h,u);void 0!==p&&this._$Eh.set(p,h)}this.elementStyles=this.finalizeStyles(this.styles)}}static finalizeStyles(n){var e=[];if(Array.isArray(n)){var t=new Set(n.flat(1/0).reverse());for(var i of t)e.unshift(re(i))}else void 0!==n&&e.push(re(n));return e}static _$Eu(n,e){var t=e.attribute;return!1===t?void 0:"string"==typeof t?t:"string"==typeof n?n.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){var n;this._$ES=new Promise((n=>this.enableUpdating=n)),this._$AL=new Map,this._$E_(),this.requestUpdate(),null===(n=this.constructor.l)||void 0===n||n.forEach((n=>n(this)))}addController(n){var e,t;(null!==(e=this._$EO)&&void 0!==e?e:this._$EO=new Set).add(n),void 0!==this.renderRoot&&this.isConnected&&(null===(t=n.hostConnected)||void 0===t||t.call(n))}removeController(n){var e;null===(e=this._$EO)||void 0===e||e.delete(n)}_$E_(){var n=new Map,e=this.constructor.elementProperties;for(var t of e.keys())this.hasOwnProperty(t)&&(n.set(t,this[t]),delete this[t]);n.size>0&&(this._$Ep=n)}createRenderRoot(){var n,e=null!==(n=this.shadowRoot)&&void 0!==n?n:this.attachShadow(this.constructor.shadowRootOptions);return((n,e)=>{if(ne)n.adoptedStyleSheets=e.map((n=>n instanceof CSSStyleSheet?n:n.styleSheet));else for(var t of e){var i=document.createElement("style"),o=Qn.litNonce;void 0!==o&&i.setAttribute("nonce",o),i.textContent=t.cssText,n.appendChild(i)}})(e,this.constructor.elementStyles),e}connectedCallback(){var n,e;null!==(n=this.renderRoot)&&void 0!==n||(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$EO)||void 0===e||e.forEach((n=>{var e;return null===(e=n.hostConnected)||void 0===e?void 0:e.call(n)}))}enableUpdating(n){}disconnectedCallback(){var n;null===(n=this._$EO)||void 0===n||n.forEach((n=>{var e;return null===(e=n.hostDisconnected)||void 0===e?void 0:e.call(n)}))}attributeChangedCallback(n,e,t){this._$AK(n,t)}_$ET(n,e){var t=this.constructor.elementProperties.get(n),i=this.constructor._$Eu(n,t);if(void 0!==i&&!0===t.reflect){var o,r=(void 0!==(null===(o=t.converter)||void 0===o?void 0:o.toAttribute)?t.converter:fe).toAttribute(e,t.type);this._$Em=n,null==r?this.removeAttribute(i):this.setAttribute(i,r),this._$Em=null}}_$AK(n,e){var t=this.constructor,i=t._$Eh.get(n);if(void 0!==i&&this._$Em!==i){var o,r,a,s=t.getPropertyOptions(i),l="function"==typeof s.converter?{fromAttribute:s.converter}:void 0!==(null===(o=s.converter)||void 0===o?void 0:o.fromAttribute)?s.converter:fe;this._$Em=i;var c=l.fromAttribute(e,s.type);this[i]=null!==(r=null!=c?c:null===(a=this._$Ej)||void 0===a?void 0:a.get(i))&&void 0!==r?r:c,this._$Em=null}}requestUpdate(n,e,t){if(void 0!==n){var i,o,r=this.constructor,a=this[n];if(null!=t||(t=r.getPropertyOptions(n)),!((null!==(i=t.hasChanged)&&void 0!==i?i:me)(a,e)||t.useDefault&&t.reflect&&a===(null===(o=this._$Ej)||void 0===o?void 0:o.get(n))&&!this.hasAttribute(r._$Eu(n,t))))return;this.C(n,e,t)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(n,e,t,i){var o,r,a,s=t.useDefault,l=t.reflect,c=t.wrapped;s&&!(null!==(o=this._$Ej)&&void 0!==o?o:this._$Ej=new Map).has(n)&&(this._$Ej.set(n,null!==(r=null!=i?i:e)&&void 0!==r?r:this[n]),!0!==c||void 0!==i)||(this._$AL.has(n)||(this.hasUpdated||s||(e=void 0),this._$AL.set(n,e)),!0===l&&this._$Em!==n&&(null!==(a=this._$Eq)&&void 0!==a?a:this._$Eq=new Set).add(n))}_$EP(){var n=this;return Yn((function*(){n.isUpdatePending=!0;try{yield n._$ES}catch(e){Promise.reject(e)}var e=n.scheduleUpdate();return null!=e&&(yield e),!n.isUpdatePending}))()}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(this.isUpdatePending){if(!this.hasUpdated){var n;if(null!==(n=this.renderRoot)&&void 0!==n||(this.renderRoot=this.createRenderRoot()),this._$Ep){for(var e of this._$Ep){var t=Xn(e,2),i=t[0],o=t[1];this[i]=o}this._$Ep=void 0}var r=this.constructor.elementProperties;if(r.size>0)for(var a of r){var s=Xn(a,2),l=s[0],c=s[1],d=c.wrapped,h=this[l];!0!==d||this._$AL.has(l)||void 0===h||this.C(l,void 0,c,h)}}var u=!1,p=this._$AL;try{var v;(u=this.shouldUpdate(p))?(this.willUpdate(p),null!==(v=this._$EO)&&void 0!==v&&v.forEach((n=>{var e;return null===(e=n.hostUpdate)||void 0===e?void 0:e.call(n)})),this.update(p)):this._$EM()}catch(p){throw u=!1,this._$EM(),p}u&&this._$AE(p)}}willUpdate(n){}_$AE(n){var e;null!==(e=this._$EO)&&void 0!==e&&e.forEach((n=>{var e;return null===(e=n.hostUpdated)||void 0===e?void 0:e.call(n)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(n)),this.updated(n)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(n){return!0}update(n){this._$Eq&&(this._$Eq=this._$Eq.forEach((n=>this._$ET(n,this[n])))),this._$EM()}updated(n){}firstUpdated(n){}};be.elementStyles=[],be.shadowRootOptions={mode:"open"},be[_e("elementProperties")]=new Map,be[_e("finalized")]=new Map,null!=ge&&ge({ReactiveElement:be}),(null!==(t=ue.reactiveElementVersions)&&void 0!==t?t:ue.reactiveElementVersions=[]).push("2.1.1");var we=globalThis,xe=we.trustedTypes,ke=xe?xe.createPolicy("lit-html",{createHTML:n=>n}):void 0,Ce="$lit$",Ee="lit$".concat(Math.random().toFixed(9).slice(2),"$"),Se="?"+Ee,Ae="<".concat(Se,">"),$e=document,Le=()=>$e.createComment(""),Pe=n=>null===n||"object"!=typeof n&&"function"!=typeof n,Me=Array.isArray,ze="[ \t\n\f\r]",Ne=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Oe=/-->/g,Re=/>/g,He=RegExp(">|".concat(ze,"(?:([^\\s\"'>=/]+)(").concat(ze,"*=").concat(ze,"*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)"),"g"),Te=/'/g,Ie=/"/g,De=/^(?:script|style|textarea|title)$/i,Ue=(n=>function(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),o=1;o{for(var t,i=n.length-1,o=[],r=2===e?"":3===e?"":"",a=Ne,s=0;s"===d[0]?(a=null!=t?t:Ne,h=-1):void 0===d[1]?h=-2:(h=a.lastIndex-d[2].length,c=d[1],a=void 0===d[3]?He:'"'===d[3]?Ie:Te):a===Ie||a===Te?a=He:a===Oe||a===Re?a=Ne:(a=He,t=void 0);var p=a===He&&n[s+1].startsWith("/>")?" ":"";r+=a===Ne?l+Ae:h>=0?(o.push(c),l.slice(0,h)+Ce+l.slice(h)+Ee+p):l+Ee+(-2===h?s:p)}return[We(n,r+(n[i]||"")+(2===e?"":3===e?"":"")),o]};class Ze{constructor(n,e){var t,i=n.strings,o=n._$litType$;this.parts=[];var r=0,a=0,s=i.length-1,l=this.parts,c=Xn(qe(i,o),2),d=c[0],h=c[1];if(this.el=Ze.createElement(d,e),Fe.currentNode=this.el.content,2===o||3===o){var u=this.el.content.firstChild;u.replaceWith(...u.childNodes)}for(;null!==(t=Fe.nextNode())&&l.length0){t.textContent=xe?xe.emptyScript:"";for(var y=0;y2&&void 0!==arguments[2]?arguments[2]:n,l=arguments.length>3?arguments[3]:void 0;if(e===je)return e;var c=void 0!==l?null===(t=s._$Co)||void 0===t?void 0:t[l]:s._$Cl,d=Pe(e)?void 0:e._$litDirective$;return(null===(i=c)||void 0===i?void 0:i.constructor)!==d&&(null!==(o=c)&&void 0!==o&&null!==(r=o._$AO)&&void 0!==r&&r.call(o,!1),void 0===d?c=void 0:(c=new d(n))._$AT(n,s,l),void 0!==l?(null!==(a=s._$Co)&&void 0!==a?a:s._$Co=[])[l]=c:s._$Cl=c),void 0!==c&&(e=Ke(n,c._$AS(n,e.values),c,l)),e}class Ge{constructor(n,e){this._$AV=[],this._$AN=void 0,this._$AD=n,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(n){var e,t=this._$AD,i=t.el.content,o=t.parts,r=(null!==(e=null==n?void 0:n.creationScope)&&void 0!==e?e:$e).importNode(i,!0);Fe.currentNode=r;for(var a=Fe.nextNode(),s=0,l=0,c=o[0];void 0!==c;){var d;if(s===c.index){var h=void 0;2===c.type?h=new Ye(a,a.nextSibling,this,n):1===c.type?h=new c.ctor(a,c.name,c.strings,this,n):6===c.type&&(h=new et(a,this,n)),this._$AV.push(h),c=o[++l]}s!==(null===(d=c)||void 0===d?void 0:d.index)&&(a=Fe.nextNode(),s++)}return Fe.currentNode=$e,r}p(n){var e=0;for(var t of this._$AV)void 0!==t&&(void 0!==t.strings?(t._$AI(n,t,e),e+=t.strings.length-2):t._$AI(n[e])),e++}}class Ye{get _$AU(){var n,e;return null!==(n=null===(e=this._$AM)||void 0===e?void 0:e._$AU)&&void 0!==n?n:this._$Cv}constructor(n,e,t,i){var o;this.type=2,this._$AH=Ve,this._$AN=void 0,this._$AA=n,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=null===(o=null==i?void 0:i.isConnected)||void 0===o||o}get parentNode(){var n,e=this._$AA.parentNode,t=this._$AM;return void 0!==t&&11===(null===(n=e)||void 0===n?void 0:n.nodeType)&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(n){n=Ke(this,n,arguments.length>1&&void 0!==arguments[1]?arguments[1]:this),Pe(n)?n===Ve||null==n||""===n?(this._$AH!==Ve&&this._$AR(),this._$AH=Ve):n!==this._$AH&&n!==je&&this._(n):void 0!==n._$litType$?this.$(n):void 0!==n.nodeType?this.T(n):(n=>Me(n)||"function"==typeof(null==n?void 0:n[Symbol.iterator]))(n)?this.k(n):this._(n)}O(n){return this._$AA.parentNode.insertBefore(n,this._$AB)}T(n){this._$AH!==n&&(this._$AR(),this._$AH=this.O(n))}_(n){this._$AH!==Ve&&Pe(this._$AH)?this._$AA.nextSibling.data=n:this.T($e.createTextNode(n)),this._$AH=n}$(n){var e,t=n.values,i=n._$litType$,o="number"==typeof i?this._$AC(n):(void 0===i.el&&(i.el=Ze.createElement(We(i.h,i.h[0]),this.options)),i);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===o)this._$AH.p(t);else{var r=new Ge(o,this),a=r.u(this.options);r.p(t),this.T(a),this._$AH=r}}_$AC(n){var e=Be.get(n.strings);return void 0===e&&Be.set(n.strings,e=new Ze(n)),e}k(n){Me(this._$AH)||(this._$AH=[],this._$AR());var e,t=this._$AH,i=0;for(var o of n)i===t.length?t.push(e=new Ye(this.O(Le()),this.O(Le()),this,this.options)):e=t[i],e._$AI(o),i++;i0&&void 0!==arguments[0]?arguments[0]:this._$AA.nextSibling,e=arguments.length>1?arguments[1]:void 0;for(null===(t=this._$AP)||void 0===t||t.call(this,!1,!0,e);n!==this._$AB;){var t,i=n.nextSibling;n.remove(),n=i}}setConnected(n){var e;void 0===this._$AM&&(this._$Cv=n,null===(e=this._$AP)||void 0===e||e.call(this,n))}}class Xe{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(n,e,t,i,o){this.type=1,this._$AH=Ve,this._$AN=void 0,this.element=n,this.name=e,this._$AM=i,this.options=o,t.length>2||""!==t[0]||""!==t[1]?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=Ve}_$AI(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this,t=arguments.length>2?arguments[2]:void 0,i=arguments.length>3?arguments[3]:void 0,o=this.strings,r=!1;if(void 0===o)n=Ke(this,n,e,0),(r=!Pe(n)||n!==this._$AH&&n!==je)&&(this._$AH=n);else{var a,s,l=n;for(n=o[0],a=0;a1&&void 0!==arguments[1]?arguments[1]:this,0))&&void 0!==e?e:Ve)!==je){var t=this._$AH,i=n===Ve&&t!==Ve||n.capture!==t.capture||n.once!==t.once||n.passive!==t.passive,o=n!==Ve&&(t===Ve||i);i&&this.element.removeEventListener(this.name,this,t),o&&this.element.addEventListener(this.name,this,n),this._$AH=n}}handleEvent(n){var e,t;"function"==typeof this._$AH?this._$AH.call(null!==(e=null===(t=this.options)||void 0===t?void 0:t.host)&&void 0!==e?e:this.element,n):this._$AH.handleEvent(n)}}class et{constructor(n,e,t){this.element=n,this.type=6,this._$AN=void 0,this._$AM=e,this.options=t}get _$AU(){return this._$AM._$AU}_$AI(n){Ke(this,n)}}var tt=we.litHtmlPolyfillSupport;null!=tt&&tt(Ze,Ye),(null!==(i=we.litHtmlVersions)&&void 0!==i?i:we.litHtmlVersions=[]).push("3.3.3");var it=globalThis,ot=class extends be{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var n,e,t=super.createRenderRoot();return null!==(e=(n=this.renderOptions).renderBefore)&&void 0!==e||(n.renderBefore=t.firstChild),t}update(n){var e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(n),this._$Do=((n,e,t)=>{var i,o=null!==(i=null==t?void 0:t.renderBefore)&&void 0!==i?i:e,r=o._$litPart$;if(void 0===r){var a,s=null!==(a=null==t?void 0:t.renderBefore)&&void 0!==a?a:null;o._$litPart$=r=new Ye(e.insertBefore(Le(),s),s,void 0,null!=t?t:{})}return r._$AI(n),r})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var n;super.connectedCallback(),null===(n=this._$Do)||void 0===n||n.setConnected(!0)}disconnectedCallback(){var n;super.disconnectedCallback(),null===(n=this._$Do)||void 0===n||n.setConnected(!1)}render(){return je}};ot._$litElement$=!0,ot.finalized=!0,null===(o=it.litElementHydrateSupport)||void 0===o||o.call(it,{LitElement:ot});var rt=it.litElementPolyfillSupport;null==rt||rt({LitElement:ot}),(null!==(r=it.litElementVersions)&&void 0!==r?r:it.litElementVersions=[]).push("4.2.1");var at="code",st="credential_used",lt="active",ct="in_sync",dt=["calendar","condition_entity"],ht={type:"divider"},ut="masked_with_reveal",pt=!0,vt=!0,gt=!0,_t=!0,ft=!0;function mt(n){var e,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"-",i="àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìıİłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·",o="aaaaaaaaaacccddeeeeeeeegghiiiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz".concat(t),r=new RegExp(i.split("").join("|"),"g");return""===n?e="":(e=n.toString().toLowerCase().replace(r,(n=>o.charAt(i.indexOf(n)))).replace(/(\d),(?=\d)/g,"$1").replace(/[^a-z0-9]+/g,t).replace(new RegExp("(".concat(t,")\\1+"),"g"),"$1").replace(new RegExp("^".concat(t,"+")),"").replace(new RegExp("".concat(t,"+$")),""),""===e&&(e="unknown")),e}function yt(n){return{cards:[{content:n,type:"markdown"}],title:arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Lock Code Manager"}}function bt(n,e,t,i){var o,r=arguments.length,a=r<3?e:null===i?i=Object.getOwnPropertyDescriptor(e,t):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(n,e,t,i);else for(var s=n.length-1;s>=0;s--)(o=n[s])&&(a=(r<3?o(a):r>3?o(e,t,a):o(e,t))||a);return r>3&&a&&Object.defineProperty(e,t,a),a}"function"==typeof SuppressedError&&SuppressedError;var wt="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z",xt="M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z",kt="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z",Ct="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z",Et="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z",St={attribute:!0,type:String,converter:fe,reflect:!1,hasChanged:me},At=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:St,e=arguments.length>1?arguments[1]:void 0,t=arguments.length>2?arguments[2]:void 0,i=t.kind,o=t.metadata,r=globalThis.litPropertyMetadata.get(o);if(void 0===r&&globalThis.litPropertyMetadata.set(o,r=new Map),"setter"===i&&((n=Object.create(n)).wrapped=!0),r.set(t.name,n),"accessor"===i){var a=t.name;return{set(t){var i=e.get.call(this);e.set.call(this,t),this.requestUpdate(a,i,n)},init(e){return void 0!==e&&this.C(a,void 0,n,e),e}}}if("setter"===i){var s=t.name;return function(t){var i=this[s];e.call(this,t),this.requestUpdate(s,i,n)}}throw Error("Unsupported decorator location: "+i)};function $t(n){return(e,t)=>"object"==typeof t?At(n,e,t):((n,e,t)=>{var i=e.hasOwnProperty(t);return e.constructor.createProperty(t,n),i?Object.getOwnPropertyDescriptor(e,t):void 0})(n,e,t)}function Lt(n){return $t(Zn(Zn({},n),{},{state:!0,attribute:!1}))}var Pt=n=>null!=n?n:Ve,Mt=oe(a||(a=Wn(["\n :host {\n /* Section backgrounds */\n --lcm-section-bg: rgba(var(--rgb-primary-text-color), 0.03);\n --lcm-section-bg-hover: rgba(var(--rgb-primary-text-color), 0.06);\n\n /* Active states */\n --lcm-active-bg: rgba(var(--rgb-primary-color), 0.1);\n --lcm-active-bg-gradient: linear-gradient(\n 135deg,\n rgba(var(--rgb-primary-color), 0.1),\n rgba(var(--rgb-primary-color), 0.04)\n );\n\n /* Status colors */\n --lcm-success-color: var(--success-color, #4caf50);\n --lcm-warning-color: var(--warning-color, #ff9800);\n --lcm-error-color: var(--error-color, #f44336);\n --lcm-disabled-color: var(--disabled-text-color, #9e9e9e);\n\n /* Badge styling */\n --lcm-badge-radius: 999px;\n --lcm-badge-font-size: 10px;\n --lcm-badge-font-weight: 600;\n --lcm-badge-letter-spacing: 0.02em;\n --lcm-badge-padding: 2px 6px;\n\n /* Section header typography */\n --lcm-section-header-size: 11px;\n --lcm-section-header-weight: 600;\n --lcm-section-header-spacing: 0.05em;\n\n /* Code/PIN typography */\n --lcm-code-font: 'Roboto Mono', monospace;\n --lcm-code-font-size: 16px;\n --lcm-code-font-weight: 600;\n --lcm-code-letter-spacing: 1px;\n\n /* Border colors */\n --lcm-border-color: rgba(var(--rgb-primary-text-color), 0.06);\n --lcm-border-color-strong: rgba(var(--rgb-primary-text-color), 0.12);\n }\n"]))),zt=oe(s||(s=Wn(["\n /* Base badge — used by identity tags (Managed/Unmanaged/Empty).\n Compact uppercase for category labels. */\n .lcm-badge {\n border-radius: var(--lcm-badge-radius);\n font-size: var(--lcm-badge-font-size);\n font-weight: var(--lcm-badge-font-weight);\n letter-spacing: var(--lcm-badge-letter-spacing);\n padding: var(--lcm-badge-padding);\n text-transform: uppercase;\n }\n\n /* State badges (active/inactive/disabled) align visually with the slot\n card's .state-chip: pill shape, sentence-case, optional colored dot\n prefix, 16% color tint. Same colors as the slot card so a state reads\n the same regardless of which card you're on. */\n .lcm-badge.active,\n .lcm-badge.inactive,\n .lcm-badge.disabled {\n align-items: center;\n border-radius: 12px;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n gap: 5px;\n letter-spacing: normal;\n padding: 3px 8px;\n text-transform: none;\n }\n .lcm-badge .dot {\n border-radius: 50%;\n flex-shrink: 0;\n height: 5px;\n width: 5px;\n }\n\n .lcm-badge.active {\n background: rgba(var(--rgb-success-color, 67, 160, 71), 0.16);\n color: var(--success-color, #43a047);\n }\n .lcm-badge.active .dot {\n background: var(--success-color, #43a047);\n }\n\n .lcm-badge.inactive {\n background: rgba(var(--rgb-warning-color, 255, 167, 38), 0.16);\n color: var(--warning-color, #ffa726);\n }\n .lcm-badge.inactive .dot {\n background: var(--warning-color, #ffa726);\n }\n\n .lcm-badge.disabled {\n background: rgba(var(--rgb-disabled-color, 117, 117, 117), 0.2);\n color: var(--secondary-text-color);\n }\n .lcm-badge.disabled .dot {\n background: var(--disabled-color, #757575);\n }\n\n .lcm-badge.empty {\n background: rgba(var(--rgb-primary-text-color), 0.08);\n color: var(--secondary-text-color);\n }\n\n .lcm-badge.managed {\n background: rgba(var(--rgb-primary-color), 0.16);\n color: var(--primary-color);\n }\n\n .lcm-badge.external {\n background: rgba(var(--rgb-primary-text-color), 0.08);\n color: var(--secondary-text-color);\n }\n"]))),Nt=oe(l||(l=Wn(["\n .lcm-sync-icon {\n --mdc-icon-size: 18px;\n }\n\n .lcm-sync-icon.synced {\n color: var(--lcm-success-color);\n }\n\n .lcm-sync-icon.pending {\n color: var(--lcm-warning-color);\n }\n\n .lcm-sync-icon.syncing {\n color: var(--lcm-warning-color);\n }\n\n .lcm-sync-icon.suspended {\n color: var(--lcm-error-color);\n }\n\n .lcm-sync-icon.unknown {\n color: var(--lcm-disabled-color);\n }\n"]))),Ot=oe(c||(c=Wn(["\n .lcm-code {\n color: var(--primary-text-color);\n font-family: var(--lcm-code-font);\n font-size: var(--lcm-code-font-size);\n font-weight: var(--lcm-code-font-weight);\n letter-spacing: var(--lcm-code-letter-spacing);\n }\n\n .lcm-code.masked {\n color: var(--secondary-text-color);\n }\n\n /* Slot disabled by user — PIN exists in config but is intentionally not on\n the lock. Heavily dimmed dots in a muted pill, no strikethrough. */\n .lcm-code.off {\n background: var(--lcm-section-bg, rgba(127, 127, 127, 0.05));\n border-radius: 6px;\n color: var(--disabled-text-color);\n padding: 2px 8px;\n }\n\n /* Slot enabled but lock doesn't have the code yet (out-of-sync, syncing, etc.).\n Dim dots with a clock-icon prefix. No strikethrough. */\n .lcm-code.pending {\n align-items: center;\n color: var(--secondary-text-color);\n display: inline-flex;\n gap: 4px;\n }\n\n .lcm-code.pending .lcm-code-pending-icon {\n --mdc-icon-size: 12px;\n color: var(--secondary-text-color);\n flex-shrink: 0;\n }\n\n .lcm-code.no-code {\n color: var(--disabled-text-color);\n font-family: inherit;\n font-size: 12px;\n font-style: italic;\n font-weight: 400;\n letter-spacing: normal;\n }\n\n /* Lock can't read the code back, but LCM manages it and it's in sync, so\n LCM's configured PIN stands in. Italic marks it as configured by LCM, not\n read from the lock; .masked still dims it while hidden. */\n .lcm-code.configured {\n font-style: italic;\n }\n\n /* Lock can't read the code back and LCM can't vouch for it (unmanaged or out\n of sync). Muted hollow dots (◦) read as \"a code exists but is unreadable\",\n distinct from privacy-masked dots; no reveal is offered. */\n .lcm-code.unreadable {\n color: var(--secondary-text-color);\n opacity: 0.7;\n }\n"]))),Rt=oe(d||(d=Wn(["\n .lcm-section {\n background: var(--lcm-section-bg);\n border-radius: 12px;\n padding: 16px;\n }\n\n .lcm-section-header {\n color: var(--secondary-text-color);\n font-size: var(--lcm-section-header-size);\n font-weight: var(--lcm-section-header-weight);\n letter-spacing: var(--lcm-section-header-spacing);\n margin-bottom: 12px;\n text-transform: uppercase;\n }\n"]))),Ht=oe(h||(h=Wn(["\n .lcm-reveal-button {\n /* 32px hit target — bumped from 28px to be a comfortable middle\n between the WCAG 2.2 SC 2.5.8 AA minimum (24px) and the\n SC 2.5.5 AAA recommendation (44px). */\n --mdc-icon-button-size: 32px;\n --mdc-icon-size: 16px;\n color: var(--secondary-text-color);\n }\n"]))),Tt=oe(u||(u=Wn(['\n .collapsible-section {\n background: var(--lcm-section-bg);\n border-radius: 12px;\n overflow: hidden;\n }\n\n .collapsible-header {\n align-items: center;\n cursor: pointer;\n display: flex;\n justify-content: space-between;\n padding: 12px 16px;\n user-select: none;\n }\n\n .collapsible-header:hover {\n background: var(--lcm-section-bg-hover);\n }\n\n .collapsible-title {\n align-items: center;\n color: var(--secondary-text-color);\n display: flex;\n font-size: var(--lcm-section-header-size);\n font-weight: var(--lcm-section-header-weight);\n gap: 8px;\n letter-spacing: var(--lcm-section-header-spacing);\n text-transform: uppercase;\n }\n\n .collapsible-badge {\n align-items: center;\n background: var(--lcm-active-bg);\n border-radius: 10px;\n color: var(--primary-color);\n display: inline-flex;\n font-size: var(--lcm-badge-font-size);\n gap: 4px;\n padding: 2px 8px;\n }\n\n /* Icon prefix on a collapsible badge — sized down to 12px so it pairs\n with the 10px badge text without dominating it. Color inherits from\n the badge color so success/warning modifiers carry through. */\n .collapsible-badge-icon {\n --mdc-icon-size: 12px;\n color: inherit;\n flex-shrink: 0;\n }\n\n .collapsible-badge.primary {\n background: var(--primary-color);\n color: var(--text-primary-color, #fff);\n }\n\n .collapsible-badge.warning {\n background: var(--warning-color, #ffa600);\n color: var(--text-primary-color, #fff);\n }\n\n /* Success modifier — used for the "allowing" condition summary so that\n allowing reads as green and blocking reads as warning everywhere\n across the cards. 16% follows the canonical chip/badge opacity stop. */\n .collapsible-badge.success {\n background: rgba(var(--rgb-success-color, 67, 160, 71), 0.16);\n color: var(--success-color, #43a047);\n }\n\n .collapsible-badge.muted {\n background: var(--lcm-section-bg);\n color: var(--secondary-text-color);\n }\n\n .collapsible-chevron {\n --mdc-icon-size: 20px;\n color: var(--secondary-text-color);\n transition: transform 0.2s ease;\n }\n\n .collapsible-content {\n max-height: 0;\n opacity: 0;\n overflow: hidden;\n padding: 0 16px;\n transition:\n max-height 0.3s ease,\n opacity 0.2s ease,\n padding 0.3s ease;\n }\n\n .collapsible-content.expanded {\n /* 1000px ceiling — was 500px, which clipped when many helpers +\n a calendar entity row stacked. A grid-rows transition to auto\n is the proper fix but more invasive; bumping the ceiling is\n the lower-risk shim. */\n max-height: 1000px;\n opacity: 1;\n padding: 0 16px 16px;\n }\n']))),It=oe(p||(p=Wn(["\n .editable {\n border-radius: 4px;\n cursor: pointer;\n margin: -4px -8px;\n padding: 4px 8px;\n text-decoration: underline dashed;\n text-decoration-color: var(--secondary-text-color);\n text-underline-offset: 3px;\n transition: background-color 0.2s;\n }\n\n .editable:hover {\n background: var(--lcm-active-bg);\n }\n\n .edit-input {\n background: var(--card-background-color, #fff);\n border: 1px solid var(--primary-color);\n border-radius: 4px;\n color: var(--primary-text-color);\n font-family: inherit;\n font-size: inherit;\n outline: none;\n padding: 4px 8px;\n width: 100%;\n }\n\n .edit-input:focus {\n box-shadow: 0 0 0 1px var(--primary-color);\n }\n\n .edit-help {\n color: var(--secondary-text-color);\n font-size: var(--lcm-section-header-size);\n margin-top: 4px;\n }\n"]))),Dt=oe(v||(v=Wn(["\n .visually-hidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n }\n"]))),Ut=oe(g||(g=Wn(["\n @media (prefers-reduced-motion: reduce) {\n .collapsible-content,\n .collapsible-chevron,\n .slot-chip.clickable,\n .editable,\n .hero-name-value.editable,\n .hero-pin-value.editable,\n .lcm-code.editable,\n .event-row {\n transition: none !important;\n }\n }\n"])));oe(_||(_=Wn(["\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n"])),Mt,zt,Nt,Ot,Rt,Ht,Tt,It,Dt,Ut);var jt=[Mt,zt,Ot,Ht,Dt,Ut,oe(f||(f=Wn(['\n :host {\n display: block;\n }\n\n ha-card {\n padding: 0;\n }\n\n .card-header {\n align-items: center;\n border-bottom: 1px solid var(--lcm-border-color);\n display: flex;\n gap: 12px;\n padding: 16px;\n }\n\n .header-icon {\n align-items: center;\n background: var(--lcm-active-bg);\n border-radius: 50%;\n color: var(--primary-color);\n display: flex;\n height: 40px;\n justify-content: center;\n width: 40px;\n }\n\n .header-icon ha-icon {\n --mdc-icon-size: 24px;\n }\n\n .card-header-title {\n color: var(--primary-text-color);\n font-size: 18px;\n font-weight: 500;\n margin: 0;\n }\n\n .card-content {\n padding: 16px;\n }\n\n .slots-grid {\n display: grid;\n gap: 10px;\n grid-template-columns: repeat(2, 1fr);\n }\n\n @media (max-width: 400px) {\n .slots-grid {\n grid-template-columns: 1fr;\n }\n }\n\n .slot-chip {\n background: var(--lcm-section-bg);\n border-radius: 12px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n overflow: hidden;\n padding: 12px 12px 14px;\n position: relative;\n }\n\n /* Active Lock Code Manager Managed: no special tint (inherits the\n chip\'s --lcm-section-bg). The state badge inside the chip carries\n the "active" signal — color the exception, not the norm. */\n\n /* Active Unmanaged (not Lock Code Manager): subtle warm-gray accent\n so an occupied unmanaged slot reads as different from an Empty\n chip (which uses the same --lcm-section-bg at 3%). 5% sits between\n the empty 3% and the inactive/disabled 6% tints. */\n .slot-chip.active.unmanaged {\n background: rgba(var(--rgb-primary-text-color), 0.05);\n }\n\n /* Inactive Lock Code Manager Managed: warning tint (orange) to match\n the slot card\'s inactive treatment — communicates "blocked by\n conditions" without shouting. 6% follows the canonical background\n opacity stop. */\n .slot-chip.inactive.managed {\n background: rgba(var(--rgb-warning-color, 255, 167, 38), 0.06);\n opacity: 0.9;\n }\n\n /* Disabled Lock Code Manager Managed: muted neutral tint, clear\n disabled state. 6% follows the canonical background opacity stop. */\n .slot-chip.disabled.managed {\n background: rgba(var(--rgb-primary-text-color), 0.06);\n opacity: 0.65;\n }\n\n .slot-chip.empty {\n background: var(--lcm-section-bg);\n opacity: 0.7;\n }\n\n .slot-chip.full-width {\n grid-column: 1 / -1;\n justify-self: center;\n max-width: 360px;\n width: 100%;\n }\n\n .slot-chip.clickable {\n cursor: pointer;\n transition:\n transform 0.1s ease,\n box-shadow 0.2s ease;\n }\n\n .slot-chip.clickable:hover {\n box-shadow: 0 2px 8px rgba(var(--rgb-primary-color), 0.25);\n transform: translateY(-1px);\n }\n\n .slot-chip.clickable:active {\n transform: translateY(0);\n }\n\n .slot-chip.clickable:focus-visible {\n outline: 2px solid var(--primary-color);\n outline-offset: 2px;\n }\n\n .slot-top {\n /* Align both children to the top so the slot label sits in a stable\n position regardless of how tall the badge stack grows. With\n align-items: center, a 2-badge stack would push the label down\n to sit between the badges; flex-start keeps it aligned with the\n first (top) badge — matching the empty-chip layout where the\n label and the single Empty badge share one row. */\n align-items: flex-start;\n display: flex;\n gap: 12px;\n justify-content: space-between;\n }\n\n .slot-badges {\n align-items: flex-end;\n display: inline-flex;\n flex-direction: column;\n flex-shrink: 0;\n gap: 4px;\n }\n\n .slot-label {\n color: var(--secondary-text-color);\n flex: 1;\n font-size: var(--lcm-section-header-size);\n font-weight: 500;\n letter-spacing: 0.03em;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n /* Config entry title appended to "Slot N · {entry}" — matches the slot-card kicker style. */\n .slot-entry-title {\n color: var(--secondary-text-color);\n font-weight: 500;\n }\n\n /* Two stacked rows per chip: name on top, PIN on the bottom. The chip is\n narrow enough that competing for one row truncated long names too\n aggressively (e.g. "Raman" → "R."). Stacking gives the name the full\n chip width and lets the PIN row anchor the eye icon to the right.\n This intentionally diverges from the slot-card hero, which uses a\n prominent single-line name; in the lock card the name is a label, not\n the focal point. */\n .slot-content-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 6px;\n min-width: 0;\n }\n\n .slot-name {\n color: var(--primary-text-color);\n font-size: 14px;\n font-weight: 400;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .slot-name.unnamed {\n color: var(--secondary-text-color);\n font-weight: 400;\n }\n\n /* Wrapper around the slot name; lays out the optional pending icon\n alongside the name with a small gap. Inline-flex so the row only takes\n the space it needs and the chip\'s column layout still controls width. */\n .slot-name-row {\n align-items: center;\n display: inline-flex;\n gap: 4px;\n max-width: 100%;\n min-width: 0;\n }\n\n /* Slot disabled by user — name shown in a muted pill, no strikethrough.\n Mirrors the .lcm-code.off treatment for visual consistency. */\n .slot-chip.disabled .slot-name {\n background: var(--lcm-section-bg, rgba(127, 127, 127, 0.05));\n border-radius: 6px;\n color: var(--disabled-text-color);\n padding: 2px 8px;\n }\n\n /* Slot enabled but lock doesn\'t have the code yet — clock-icon prefix on\n the name. Mirrors the .lcm-code.pending treatment. The disabled rule\'s\n pill background takes precedence if a slot is somehow both. */\n .slot-chip.pending .slot-name-pending-icon {\n --mdc-icon-size: 12px;\n color: var(--secondary-text-color);\n flex-shrink: 0;\n }\n\n .slot-chip.pending .slot-name {\n color: var(--secondary-text-color);\n }\n\n .slot-code-row {\n align-items: center;\n display: flex;\n gap: 8px;\n justify-content: space-between;\n }\n\n .slot-code-actions {\n display: inline-flex;\n }\n\n /* Editable code for unmanaged slots */\n .slot-code-edit {\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n }\n\n .slot-code-edit-row {\n align-items: center;\n display: flex;\n gap: 8px;\n }\n\n .slot-code-input {\n background: var(--card-background-color, #fff);\n border: 1px solid var(--primary-color);\n border-radius: 6px;\n color: var(--primary-text-color);\n flex: 1;\n font-family: var(--lcm-code-font);\n font-size: 14px;\n font-weight: 500;\n letter-spacing: var(--lcm-code-letter-spacing);\n min-width: 0;\n outline: none;\n padding: 6px 10px;\n }\n\n .slot-code-input:focus {\n box-shadow: 0 0 0 1px var(--primary-color);\n }\n\n .slot-code-input::placeholder {\n color: var(--secondary-text-color);\n font-weight: 400;\n letter-spacing: normal;\n }\n\n .slot-code-edit-buttons {\n display: flex;\n gap: 4px;\n }\n\n .slot-code-edit-buttons ha-icon-button {\n --mdc-icon-button-size: 32px;\n --mdc-icon-size: 18px;\n }\n\n .slot-edit-help {\n color: var(--secondary-text-color);\n font-size: 10px;\n }\n\n /* Editable code display (click to edit) */\n .lcm-code.editable {\n border-radius: 4px;\n cursor: pointer;\n margin: -2px -4px;\n padding: 2px 4px;\n transition: background-color 0.2s;\n }\n\n .lcm-code.editable:hover {\n background: var(--lcm-active-bg);\n }\n\n .empty-summary {\n align-items: center;\n background: var(--lcm-section-bg);\n border: 1px dashed var(--lcm-border-color-strong);\n border-radius: 10px;\n color: var(--secondary-text-color);\n display: flex;\n font-size: 12px;\n gap: 8px;\n grid-column: 1 / -1;\n padding: 8px 12px;\n }\n\n .empty-summary ha-icon {\n --mdc-icon-size: 16px;\n color: var(--secondary-text-color);\n }\n\n .empty-summary-label {\n color: var(--secondary-text-color);\n font-size: var(--lcm-section-header-size);\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n }\n\n .empty-summary-range {\n color: var(--primary-text-color);\n font-size: 13px;\n font-weight: 500;\n }\n\n .message {\n color: var(--secondary-text-color);\n font-style: italic;\n }\n\n /* Summary table */\n .summary-table {\n border-collapse: collapse;\n font-size: 12px;\n margin-top: 16px;\n table-layout: fixed;\n width: 100%;\n }\n\n .summary-table th,\n .summary-table td {\n overflow: hidden;\n padding: 6px 4px;\n text-align: center;\n text-overflow: ellipsis;\n }\n\n .summary-table th {\n background: rgba(var(--rgb-primary-text-color), 0.04);\n color: var(--secondary-text-color);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n }\n\n .summary-table th:first-child {\n border-radius: 6px 0 0 0;\n text-align: left;\n }\n\n .summary-table th:last-child {\n border-radius: 0 6px 0 0;\n }\n\n .summary-table td,\n .summary-table tbody th {\n border-top: 1px solid var(--lcm-border-color);\n color: var(--primary-text-color);\n font-weight: 500;\n }\n\n .summary-table tbody th[scope=\'row\'] {\n color: var(--secondary-text-color);\n font-size: var(--lcm-section-header-size);\n font-weight: 600;\n letter-spacing: 0.03em;\n text-align: left;\n text-transform: uppercase;\n }\n\n .summary-table tbody tr:last-child th[scope=\'row\'] {\n border-radius: 0 0 0 6px;\n }\n\n .summary-table tbody tr:last-child td:last-child {\n border-radius: 0 0 6px 0;\n }\n\n .summary-table .total-row td,\n .summary-table .total-row th {\n background: rgba(var(--rgb-primary-text-color), 0.02);\n border-top: 2px solid var(--lcm-border-color-strong);\n font-weight: 600;\n }\n\n .summary-cell-zero {\n color: var(--disabled-text-color) !important;\n font-weight: 400 !important;\n }\n\n ha-card.suspended {\n border: 1px solid var(--lcm-error-color);\n opacity: 0.85;\n }\n\n .suspended-banner {\n align-items: center;\n background: rgba(244, 67, 54, 0.08);\n border-bottom: 1px solid var(--lcm-border-color);\n color: var(--lcm-error-color);\n display: flex;\n font-size: 12px;\n font-weight: 500;\n gap: 8px;\n padding: 8px 16px;\n }\n\n .suspended-banner ha-icon {\n --mdc-icon-size: 16px;\n }\n'])))];function Vt(n){class e extends n{constructor(){super(...arguments),this._revealed=!1,this._isStub=!1,this._subscribing=!1}connectedCallback(){super.connectedCallback(),this._subscribe()}disconnectedCallback(){super.disconnectedCallback(),this._unsubscribe()}_formatSubscriptionError(n){return n instanceof Error?n.message:"object"==typeof n&&null!==n&&"message"in n?String(n.message):"Failed to subscribe: ".concat(JSON.stringify(n))}_shouldReveal(){var n,e,t=null!==(n=null===(e=this._config)||void 0===e?void 0:e.code_display)&&void 0!==n?n:this._getDefaultCodeDisplay();return"unmasked"===t||"masked_with_reveal"===t&&this._revealed}_subscribe(){var n=this;return Yn((function*(){var e;if(!n._isStub&&n._hass&&n._config&&!n._unsub&&!n._subscribing)if(null!==(e=n._hass.connection)&&void 0!==e&&e.subscribeMessage){n._subscribing=!0;try{var t=n._buildSubscribeMessage();n._unsub=yield n._hass.connection.subscribeMessage((e=>{n._handleSubscriptionData(e),n._error=void 0,n.requestUpdate()}),t)}catch(e){n._data=void 0,n._error=n._formatSubscriptionError(e),n.requestUpdate()}finally{n._subscribing=!1}}else n._error="Websocket connection unavailable"}))()}_toggleReveal(){this._revealed=!this._revealed,this._unsubscribe(),this._subscribe()}_unsubscribe(){this._unsub&&(this._unsub(),this._unsub=void 0)}}return bt([Lt()],e.prototype,"_revealed",void 0),e}var Bt="empty",Ft="unreadable_code";function Wt(n){return null===n||""===n||n===Bt}function qt(n,e){return n===Ft||(!Wt(n)||!!e)}var Zt="masked_with_reveal",Kt=Vt(ot);class Gt extends Kt{constructor(){super(...arguments),this._editingSlot=null,this._editValue="",this._saving=!1,this._wasRevealedBeforeEdit=!1}set hass(n){this._hass=n,this._subscribe()}static getConfigElement(){return document.createElement("lcm-lock-codes-editor")}static getStubConfig(n){return Yn((function*(){var e={lock_entity_id:"lock.stub",type:"custom:lcm-lock-codes"};try{return yield Promise.race([Yn((function*(){var t=yield n.callWS({domain:"lock_code_manager",type:"config_entries/get"});if(t.length>0){var i=yield n.callWS({config_entry_id:t[0].entry_id,type:"lock_code_manager/get_config_entry_data"});if(i.locks.length>0)return{lock_entity_id:i.locks[0].entity_id,type:"custom:lcm-lock-codes"}}return e}))(),new Promise((n=>setTimeout((()=>n(e)),2e3)))])}catch(n){return e}}))()}setConfig(n){var e;if(!n.lock_entity_id)throw new Error("lock_entity_id is required");null!==(e=this._config)&&void 0!==e&&e.lock_entity_id&&this._config.lock_entity_id!==n.lock_entity_id&&(this._unsubscribe(),this._data=void 0),this._config=n,this._isStub="lock.stub"===n.lock_entity_id,this._isStub||this._subscribe()}_getDefaultCodeDisplay(){return Zt}_buildSubscribeMessage(){if(!this._config)throw new Error("Config not set");return{lock_entity_id:this._config.lock_entity_id,reveal:this._shouldReveal(),type:"lock_code_manager/subscribe_lock_codes"}}_handleSubscriptionData(n){this._data=n}render(){var n,e,t,i,o,r,a,s,l,c,d,h;if(this._isStub)return Ue(m||(m=Wn(['\n
\n \n

Lock Code Manager Lock Codes

\n
\n
'])));var u=null===(n=this._hass)||void 0===n||null===(n=n.states[null!==(e=null===(t=this._config)||void 0===t?void 0:t.lock_entity_id)&&void 0!==e?e:""])||void 0===n||null===(n=n.attributes)||void 0===n?void 0:n.friendly_name,p=null!==(i=null!==(o=null!==(r=null===(a=this._data)||void 0===a?void 0:a.lock_name)&&void 0!==r?r:u)&&void 0!==o?o:null===(s=this._config)||void 0===s?void 0:s.lock_entity_id)&&void 0!==i?i:"",v=null!==(l=null!==(c=null===(d=this._config)||void 0===d?void 0:d.title)&&void 0!==c?c:p)&&void 0!==l?l:"Lock Codes",g="suspended"===(null===(h=this._data)||void 0===h?void 0:h.sync_status);return Ue(y||(y=Wn(['\n \n
\n \n

',"

\n
\n ",'\n
\n ',"\n ","\n
\n
\n "])),g?"suspended":"",v,g?Ue(b||(b=Wn(['
\n \n Sync suspended — lock is unreachable\n
']))):Ve,this._error?Ue(w||(w=Wn(['
',"
"])),this._error):this._renderSlots(),this._renderSummaryTable())}_startEditing(n,e){n.stopPropagation(),this._wasRevealedBeforeEdit=this._revealed,this._revealed||(this._revealed=!0,this._unsubscribe(),this._subscribe());var t=qt(e.code)&&e.code!==Ft?String(e.code):"";this._editValue=t,this._editingSlot=e.slot,this.updateComplete.then((()=>{var n,e=null===(n=this.shadowRoot)||void 0===n?void 0:n.querySelector(".slot-code-input");null==e||e.focus(),null==e||e.select()}))}_handleEditInput(n){var e=n.target;this._editValue=e.value}_handleEditKeydown(n){"Enter"===n.key&&null!==this._editingSlot?this._saveCode(this._editingSlot):"Escape"===n.key&&this._cancelEdit()}_cancelEdit(){this._editingSlot=null,this._editValue="",this._revealed!==this._wasRevealedBeforeEdit&&(this._revealed=this._wasRevealedBeforeEdit,this._unsubscribe(),this._subscribe())}_saveCode(n){var e=this;return Yn((function*(){if(e._hass&&e._config&&!e._saving){e._saving=!0;var t=e._editValue.trim();try{var i="string"==typeof n?parseInt(n,10):n;t?yield e._hass.connection.sendMessagePromise({code_slot:i,lock_entity_id:e._config.lock_entity_id,type:"lock_code_manager/set_usercode",usercode:t}):yield e._hass.connection.sendMessagePromise({code_slot:i,lock_entity_id:e._config.lock_entity_id,type:"lock_code_manager/clear_usercode"}),e._editingSlot=null,e._editValue=""}catch(n){console.error("Failed to set usercode:",n)}finally{e._saving=!1}}}))()}_navigateToSlot(n){if(n){var e="/config/integrations/integration/lock_code_manager#config_entry=".concat(n);history.pushState(null,"",e),window.dispatchEvent(new CustomEvent("location-changed"))}}_renderSlots(){var n,e,t=null!==(n=null===(e=this._data)||void 0===e?void 0:e.slots)&&void 0!==n?n:[];if(0===t.length)return Ue(x||(x=Wn(['
No codes reported
'])));var i=this._groupSlots(t),o=this._identifyBorrowedSlots(i),r=this._renderGroupsWithBorrowing(i,o);return Ue(k||(k=Wn(['
',"
"])),r)}_identifyBorrowedSlots(n){for(var e=new Set,t=0;t0?n[t-1]:null,s=t0?e.add(s.slots[0].slot):!r&&"empty"===(null==a?void 0:a.type)&&a.slots.length>0&&e.add(a.slots[a.slots.length-1].slot)}}return e}_renderGroupsWithBorrowing(n,e){for(var t=[],i=0;i0?n[i-1]:null,a=i0?(t.push(this._renderSlotChip(s,!1)),t.push(this._renderEmptySlotChip(a.slots[0]))):!l&&"empty"===(null==r?void 0:r.type)&&r.slots.length>0?(t.push(this._renderEmptySlotChip(r.slots[r.slots.length-1])),t.push(this._renderSlotChip(s,!1))):t.push(this._renderSlotChip(s,!0))}else for(var c of o.slots)t.push(this._renderSlotChip(c,!1));else{var d=o.slots.filter((n=>!e.has(n.slot)));d.length>0&&t.push(this._renderEmptySummary(Zn(Zn({},o),{},{rangeLabel:this._formatSlotRange(d),slots:d})))}}return t}_renderEmptySlotChip(n){return Ue(C||(C=Wn(['\n
\n
\n Slot ','\n
\n Empty\n
\n
\n
\n '])),n.slot)}_groupSlots(n){var e=[],t=[],i=[],o=()=>{t.length>0&&(e.push({rangeLabel:this._formatSlotRange(t),slots:t,type:"empty"}),t=[])},r=()=>{i.length>0&&(e.push({slots:i,type:"active"}),i=[])};for(var a of n){var s=!(!a.configured_code&&!a.configured_code_length),l=!0===a.managed&&(void 0!==a.enabled||void 0!==a.active);this._hasCode(a)||!0===a.managed&&(s||l)?(o(),i.push(a)):(r(),t.push(a))}return r(),o(),e}_formatSlotRange(n){if(0===n.length)return"";if(1===n.length)return"".concat(n[0].slot);var e=n.map((n=>Number(n.slot))).filter((n=>!isNaN(n)));if(e.length!==n.length)return"".concat(n.map((n=>n.slot)).join(", "));for(var t=[],i=Xn(e,1)[0],o=i,r=i,a=1;a\n
\n \n Slot\n ',"",'\n \n
\n \n ',"\n ","\n \n ",'\n
\n
\n
\n ',"\n ","\n
\n \n "])),r,y,f,m,e?"full-width":"",Pt(v?"Click to manage this slot":void 0),Pt(v?"button":void 0),Pt(v?"0":void 0),v?"Manage slot ".concat(n.slot).concat(n.config_entry_title?" · ".concat(n.config_entry_title):""):Ve,v?()=>this._navigateToSlot(n.config_entry_id):Ve,v?e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this._navigateToSlot(n.config_entry_id))}:Ve,n.slot,n.config_entry_title?Ue(S||(S=Wn([' ·\n ',""])),n.config_entry_title):Ve,s,"active"===s||"inactive"===s||"disabled"===s?Ue(A||(A=Wn(['']))):Ve,a,void 0===d?Ve:Ue($||($=Wn(['\n ',"\n "])),d?"managed":"external",d?"Managed":"Unmanaged"),p?Ue(L||(L=Wn(['\n ','\n \n ',"\n \n "])),_?Ue(P||(P=Wn(['