From c8dde3852ff43958cf335259902a480b013d2457 Mon Sep 17 00:00:00 2001 From: raman325 <7243222+raman325@users.noreply.github.com> Date: Thu, 20 Aug 2026 01:06:51 -0400 Subject: [PATCH 1/6] feat: address a slot by the user holding it, over the websocket The three commands a card sends -- subscribe_code_slot, set_slot_condition, clear_slot_condition -- took a slot number and nothing else. They now take a name as well, which is what every other surface moved to. The slot number stays so existing callers keep working, and is expected to go. Names are matched slugified, the way a config entry title already is: a caller holding only the slug an entity id was built from can still name its user. Slugifying collapses more than the name rules do, though -- "Ada-Lovelace" and "Ada Lovelace" are two users under those rules and one slug under this. Where that happens the command is refused and names both, rather than picking one and writing somebody else's credential. A user the allocator has not numbered yet is refused for the same reason: there is nothing on any lock to address. Resolution happens at the websocket boundary rather than deeper down: a slot number is still what the coordinator, the entities and the provider key on, so this is where the two vocabularies meet. clear_slot_condition gained a failure path it did not have before, so it gained the error handling to match. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: 0f359e698aca --- .../lock_code_manager/websocket.py | 62 +++++- tests/test_websocket.py | 178 ++++++++++++++++++ 2 files changed, 231 insertions(+), 9 deletions(-) diff --git a/custom_components/lock_code_manager/websocket.py b/custom_components/lock_code_manager/websocket.py index b2449674b..677364472 100644 --- a/custom_components/lock_code_manager/websocket.py +++ b/custom_components/lock_code_manager/websocket.py @@ -47,7 +47,7 @@ from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.event import async_track_state_change_event -from homeassistant.util import dt as dt_util +from homeassistant.util import dt as dt_util, slugify from .const import ( ATTR_ACTIVE, @@ -244,6 +244,44 @@ def _get_last_changed( return None +def _slot_from(config_entry: ConfigEntry, msg: dict[str, Any]) -> int: + """ + Resolve the slot a message names, by number or by whoever holds it. + + The slot number is on its way out of every user-facing surface, so these + commands take a name as well. Resolution happens here rather than deeper + down because a slot number is still what the coordinator, the entities + and the provider all key on -- this is the boundary where the two + vocabularies meet. + """ + if (slot := msg.get(ATTR_SLOT)) is not None: + return slot + if not (name := msg.get(CONF_NAME)): + raise ServiceValidationError("Neither name nor slot provided") + + # Matched slugified, the way a config entry title is, so a caller that + # has only the slug an entity id was built from can still name its user. + config = get_entry_config(config_entry) + wanted = slugify(name) + matches = [known for known in config.users if slugify(known) == wanted] + if not matches: + raise ServiceValidationError(f"No user named {name!r} in this config entry") + if len(matches) > 1: + # Slugifying collapses more than the name rules do -- "Ada-Lovelace" + # and "Ada Lovelace" are two users but one slug. Picking either would + # write somebody else's credential. + raise ServiceValidationError( + f"{name!r} matches more than one user in this config entry: " + f"{', '.join(sorted(matches))}" + ) + if (resolved := config.assignment.slot(matches[0])) is None: + # A user can exist in the configuration without a number, while + # allocation is still deciding where they go. There is nothing on any + # lock to address yet. + raise ServiceValidationError(f"{matches[0]!r} has not been given a slot yet") + return resolved + + def _get_slot_condition_entity_id( config_entry: ConfigEntry, slot_num: int ) -> str | None: @@ -1007,7 +1045,8 @@ def _serialize_slot_card_data( vol.Required("type"): "lock_code_manager/subscribe_code_slot", vol.Exclusive("config_entry_title", "entry"): str, vol.Exclusive("config_entry_id", "entry"): str, - vol.Required(ATTR_SLOT): int, + vol.Exclusive(ATTR_SLOT, "user"): int, + vol.Exclusive(CONF_NAME, "user"): str, vol.Optional("reveal", default=False): bool, } ) @@ -1028,7 +1067,7 @@ async def subscribe_code_slot( - In-sync status changes - Calendar entity state changes (for event-based access control) """ - slot_num = msg[ATTR_SLOT] + slot_num = _slot_from(config_entry, msg) reveal = msg["reveal"] if not get_entry_config(config_entry).has_slot(slot_num): @@ -1198,7 +1237,8 @@ async def ws_clear_usercode( vol.Required("type"): "lock_code_manager/set_slot_condition", vol.Exclusive("config_entry_title", "entry"): str, vol.Exclusive("config_entry_id", "entry"): str, - vol.Required(ATTR_SLOT): int, + vol.Exclusive(ATTR_SLOT, "user"): int, + vol.Exclusive(CONF_NAME, "user"): str, vol.Required(CONF_ENTITY_ID): cv.entity_domain(CONDITION_ENTITY_DOMAINS), } ) @@ -1219,7 +1259,7 @@ async def ws_set_slot_condition( try: await async_set_slot_condition( hass, - msg[ATTR_SLOT], + _slot_from(config_entry, msg), msg[CONF_ENTITY_ID], config_entry_id=config_entry.entry_id, ) @@ -1244,7 +1284,8 @@ async def ws_set_slot_condition( vol.Required("type"): "lock_code_manager/clear_slot_condition", vol.Exclusive("config_entry_title", "entry"): str, vol.Exclusive("config_entry_id", "entry"): str, - vol.Required(ATTR_SLOT): int, + vol.Exclusive(ATTR_SLOT, "user"): int, + vol.Exclusive(CONF_NAME, "user"): str, } ) @websocket_api.async_response @@ -1257,7 +1298,10 @@ async def ws_clear_slot_condition( config_entry: ConfigEntry, ) -> None: """Clear the condition entity from a slot.""" - await async_clear_slot_condition( - hass, msg[ATTR_SLOT], config_entry_id=config_entry.entry_id - ) + try: + slot = _slot_from(config_entry, msg) + except ServiceValidationError as err: + connection.send_error(msg["id"], websocket_api.const.ERR_NOT_FOUND, str(err)) + return + await async_clear_slot_condition(hass, slot, config_entry_id=config_entry.entry_id) connection.send_result(msg["id"], {"success": True}) diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 89bb315ae..d495cdd7e 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -16,6 +16,7 @@ from homeassistant.const import ( ATTR_ENTITY_ID, CONF_CONDITION, + CONF_ENTITY_ID, CONF_NAME, STATE_OFF, STATE_ON, @@ -66,10 +67,14 @@ CONF_LOCKS, CONF_PIN, CONF_SLOTS, + CONF_USERS, ) from custom_components.lock_code_manager.domain.exceptions import DuplicateCodeError from custom_components.lock_code_manager.domain.models import SlotCode, SlotCredential from custom_components.lock_code_manager.domain.queries import get_entry_config +from custom_components.lock_code_manager.domain.slot_assignment import ( + CONF_SLOT_ASSIGNMENT, +) from custom_components.lock_code_manager.providers import BaseLock from custom_components.lock_code_manager.websocket import ( SlotEntities, @@ -83,6 +88,7 @@ _get_text_state, _serialize_slot, _slot_code_payload, + _slot_from, find_config_entry_by_title, ) @@ -3234,3 +3240,175 @@ async def test_subscribe_code_slot_receives_coordinator_updates( ) assert lock_1_data is not None assert lock_1_data[ATTR_CODE] == "new_code" + + +class TestAddressingASlotByUser: + """The commands a card sends name a user, not a slot number.""" + + async def test_subscribe_accepts_a_name( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """A card that knows only who it is showing can still subscribe.""" + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/subscribe_code_slot", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "test1", + } + ) + assert (await client.receive_json())["success"] + + async def test_a_name_is_matched_slugified( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """ + Matched the way a config entry title is. + + The slug is what a caller holding only an entity id has to work + from, so it has to be enough to name the user. + """ + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/subscribe_code_slot", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "TEST1", + } + ) + assert (await client.receive_json())["success"] + + async def test_an_unknown_name_is_refused( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """Naming nobody says so, rather than falling through to a slot.""" + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/subscribe_code_slot", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "nobody", + } + ) + result = await client.receive_json() + assert not result["success"] + assert "nobody" in result["error"]["message"] + + async def test_naming_neither_is_refused( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """A message that names no user and no slot addresses nothing.""" + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/subscribe_code_slot", + "config_entry_id": lock_code_manager_config_entry.entry_id, + } + ) + assert not (await client.receive_json())["success"] + + async def test_setting_a_condition_by_name( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """The write path takes a name too, not just the read path.""" + hass.states.async_set("binary_sensor.by_name", STATE_ON) + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/set_slot_condition", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "test1", + CONF_ENTITY_ID: "binary_sensor.by_name", + } + ) + assert (await client.receive_json())["success"] + assert ( + get_entry_config(lock_code_manager_config_entry).slot(1)[CONF_CONDITION] + == "binary_sensor.by_name" + ) + + await client.send_json_auto_id( + { + "type": "lock_code_manager/clear_slot_condition", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "test1", + } + ) + assert (await client.receive_json())["success"] + assert CONF_CONDITION not in get_entry_config( + lock_code_manager_config_entry + ).slot(1) + + def test_an_ambiguous_slug_is_refused(self, hass: HomeAssistant) -> None: + """ + Two users, one slug: refuse rather than write somebody else's code. + + The name rules keep "Ada-Lovelace" and "Ada Lovelace" apart, but + slugifying collapses them together. Picking either would put a PIN + on the wrong person. + """ + + class _Entry: + runtime_data = None + data = { + CONF_USERS: { + "Ada-Lovelace": {CONF_ENABLED: True}, + "Ada Lovelace": {CONF_ENABLED: True}, + }, + CONF_SLOT_ASSIGNMENT: {"ada-lovelace": 1, "ada lovelace": 2}, + } + options: dict = {} + + with pytest.raises(ServiceValidationError, match="more than one user"): + _slot_from(_Entry(), {CONF_NAME: "ada lovelace"}) + + async def test_clearing_a_condition_for_an_unknown_user_is_refused( + self, + hass: HomeAssistant, + hass_ws_client, + mock_lock_config_entry, + lock_code_manager_config_entry, + ) -> None: + """Naming nobody must not fall through to clearing something else.""" + client = await hass_ws_client(hass) + await client.send_json_auto_id( + { + "type": "lock_code_manager/clear_slot_condition", + "config_entry_id": lock_code_manager_config_entry.entry_id, + CONF_NAME: "nobody", + } + ) + result = await client.receive_json() + assert not result["success"] + assert result["error"]["code"] == "not_found" + + def test_a_user_with_no_slot_yet_is_refused(self, hass: HomeAssistant) -> None: + """A user allocation has not numbered has nothing to address.""" + + class _Entry: + runtime_data = None + data = {CONF_USERS: {"Pending": {CONF_ENABLED: False}}} + options: dict = {} + + with pytest.raises(ServiceValidationError, match="not been given a slot"): + _slot_from(_Entry(), {CONF_NAME: "pending"}) From f823ce51c8f643a2651d4d5a8a6a55ccb360c67b Mon Sep 17 00:00:00 2001 From: raman325 <7243222+raman325@users.noreply.github.com> Date: Thu, 20 Aug 2026 09:29:25 -0400 Subject: [PATCH 2/6] feat: the card shows a user, and says so The slot card was the last place a slot number faced the user. It is registered as custom:lcm-user now, takes `name` instead of `slot`, and the editor asks for a user rather than a number. Not a second card. The old one was 1,450 lines of which `slot` appeared in three -- all message payloads, none of them rendering -- so a separate implementation would have been three payloads' worth of difference and 3,500 lines of duplication to maintain. custom:lcm-slot stays registered as a subclass that warns and defers, so dashboards holding one keep working until it is removed. A card carrying both a name and a slot number would keep showing whoever held that number regardless of the name, so the editor drops the number when a name is set, and the card sends one or the other from a single place rather than deciding per command. The strategy passes the user's name down to the section, which passes it to the card; where a user has no name yet the slot number still goes through, because something has to address them. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: cf120f3d01cf --- .../www/generated/lock-code-manager.js | 2 +- ts/generate-view.test.ts | 27 ++-- ts/generate-view.ts | 9 +- ts/slot-card-editor.test.ts | 33 +++-- ts/slot-card-editor.ts | 27 ++-- ts/slot-card.integration.test.ts | 128 ++++++++++++------ ts/slot-card.ts | 76 ++++++++--- ts/slot-section-strategy.ts | 5 +- ts/types.ts | 16 ++- 9 files changed, 224 insertions(+), 99 deletions(-) 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 44c1dd366..682db36e0 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,p,u,v,g,_,f,m,y,b,x,w,k,C,E,S,A,$,L,P,M,z,N,O,R,H,T,I,D,U,j,V,F,B,W,q,Z,K,G,Y,X,J,Q,nn,en,tn,on,rn,an,sn,ln,cn,dn,hn,pn,un,vn,gn,_n,fn,mn,yn,bn,xn,wn,kn,Cn,En,Sn,An,$n,Ln,Pn,Mn,zn,Nn,On,Rn,Hn,Tn,In,Dn,Un,jn,Vn,Fn=["config_entry_title"];function Bn(n,e){return e||(e=n.slice(0)),Object.freeze(Object.defineProperties(n,{raw:{value:Object.freeze(e)}}))}function Wn(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(n);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.push.apply(t,i)}return t}function qn(n){for(var e=1;en.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 te(o,n,ne)},oe=Qn?n=>n:n=>n instanceof CSSStyleSheet?(n=>{var e="";for(var t of n.cssRules)e+=t.cssText;return(n=>new te("string"==typeof n?n:n+"",void 0,ne))(e)})(n):n,re=Object.is,ae=Object.defineProperty,se=Object.getOwnPropertyDescriptor,le=Object.getOwnPropertyNames,ce=Object.getOwnPropertySymbols,de=Object.getPrototypeOf,he=globalThis,pe=he.trustedTypes,ue=pe?pe.emptyScript:"",ve=he.reactiveElementPolyfillSupport,ge=(n,e)=>n,_e={toAttribute(n,e){switch(e){case Boolean:n=n?ue: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}},fe=(n,e)=>!re(n,e),me={attribute:!0,type:String,converter:_e,reflect:!1,useDefault:!1,hasChanged:fe};null!==(n=Symbol.metadata)&&void 0!==n||(Symbol.metadata=Symbol("metadata")),null!==(e=he.litPropertyMetadata)&&void 0!==e||(he.litPropertyMetadata=new WeakMap);var ye=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]:me;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&&ae(this.prototype,n,i)}}static getPropertyDescriptor(n,e,t){var i,o=null!==(i=se(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:me}static _$Ei(){if(!this.hasOwnProperty(ge("elementProperties"))){var n=de(this);n.finalize(),void 0!==n.l&&(this.l=[...n.l]),this.elementProperties=new Map(n.elementProperties)}}static finalize(){if(!this.hasOwnProperty(ge("finalized"))){if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(ge("properties"))){var n=this.properties,e=[...le(n),...ce(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=Yn(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=Yn(c,2),h=d[0],p=d[1],u=this._$Eu(h,p);void 0!==u&&this._$Eh.set(u,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(oe(i))}else void 0!==n&&e.push(oe(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(Qn)n.adoptedStyleSheets=e.map((n=>n instanceof CSSStyleSheet?n:n.styleSheet));else for(var t of e){var i=document.createElement("style"),o=Jn.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:_e).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:_e;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:fe)(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 Gn((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=Yn(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=Yn(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 p=!1,u=this._$AL;try{var v;(p=this.shouldUpdate(u))?(this.willUpdate(u),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(u)):this._$EM()}catch(u){throw p=!1,this._$EM(),u}p&&this._$AE(u)}}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){}};ye.elementStyles=[],ye.shadowRootOptions={mode:"open"},ye[ge("elementProperties")]=new Map,ye[ge("finalized")]=new Map,null!=ve&&ve({ReactiveElement:ye}),(null!==(t=he.reactiveElementVersions)&&void 0!==t?t:he.reactiveElementVersions=[]).push("2.1.1");var be=globalThis,xe=be.trustedTypes,we=xe?xe.createPolicy("lit-html",{createHTML:n=>n}):void 0,ke="$lit$",Ce="lit$".concat(Math.random().toFixed(9).slice(2),"$"),Ee="?"+Ce,Se="<".concat(Ee,">"),Ae=document,$e=()=>Ae.createComment(""),Le=n=>null===n||"object"!=typeof n&&"function"!=typeof n,Pe=Array.isArray,Me="[ \t\n\f\r]",ze=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ne=/-->/g,Oe=/>/g,Re=RegExp(">|".concat(Me,"(?:([^\\s\"'>=/]+)(").concat(Me,"*=").concat(Me,"*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)"),"g"),He=/'/g,Te=/"/g,Ie=/^(?:script|style|textarea|title)$/i,De=(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=ze,s=0;s"===d[0]?(a=null!=t?t:ze,h=-1):void 0===d[1]?h=-2:(h=a.lastIndex-d[2].length,c=d[1],a=void 0===d[3]?Re:'"'===d[3]?Te:He):a===Te||a===He?a=Re:a===Ne||a===Oe?a=ze:(a=Re,t=void 0);var u=a===Re&&n[s+1].startsWith("/>")?" ":"";r+=a===ze?l+Se:h>=0?(o.push(c),l.slice(0,h)+ke+l.slice(h)+Ce+u):l+Ce+(-2===h?s:u)}return[Be(n,r+(n[i]||"")+(2===e?"":3===e?"":"")),o]};class qe{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=Yn(We(i,o),2),d=c[0],h=c[1];if(this.el=qe.createElement(d,e),Fe.currentNode=this.el.content,2===o||3===o){var p=this.el.content.firstChild;p.replaceWith(...p.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===Ue)return e;var c=void 0!==l?null===(t=s._$Co)||void 0===t?void 0:t[l]:s._$Cl,d=Le(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=Ze(n,c._$AS(n,e.values),c,l)),e}class Ke{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:Ae).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 Ge(a,a.nextSibling,this,n):1===c.type?h=new c.ctor(a,c.name,c.strings,this,n):6===c.type&&(h=new nt(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=Ae,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 Ge{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=je,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=Ze(this,n,arguments.length>1&&void 0!==arguments[1]?arguments[1]:this),Le(n)?n===je||null==n||""===n?(this._$AH!==je&&this._$AR(),this._$AH=je):n!==this._$AH&&n!==Ue&&this._(n):void 0!==n._$litType$?this.$(n):void 0!==n.nodeType?this.T(n):(n=>Pe(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!==je&&Le(this._$AH)?this._$AA.nextSibling.data=n:this.T(Ae.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=qe.createElement(Be(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 Ke(o,this),a=r.u(this.options);r.p(t),this.T(a),this._$AH=r}}_$AC(n){var e=Ve.get(n.strings);return void 0===e&&Ve.set(n.strings,e=new qe(n)),e}k(n){Pe(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 Ge(this.O($e()),this.O($e()),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 Ye{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(n,e,t,i,o){this.type=1,this._$AH=je,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=je}_$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=Ze(this,n,e,0),(r=!Le(n)||n!==this._$AH&&n!==Ue)&&(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:je)!==Ue){var t=this._$AH,i=n===je&&t!==je||n.capture!==t.capture||n.once!==t.once||n.passive!==t.passive,o=n!==je&&(t===je||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 nt{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){Ze(this,n)}}var et=be.litHtmlPolyfillSupport;null!=et&&et(qe,Ge),(null!==(i=be.litHtmlVersions)&&void 0!==i?i:be.litHtmlVersions=[]).push("3.3.3");var tt=globalThis,it=class extends ye{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 Ge(e.insertBefore($e(),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 Ue}};it._$litElement$=!0,it.finalized=!0,null===(o=tt.litElementHydrateSupport)||void 0===o||o.call(tt,{LitElement:it});var ot=tt.litElementPolyfillSupport;null==ot||ot({LitElement:it}),(null!==(r=tt.litElementVersions)&&void 0!==r?r:tt.litElementVersions=[]).push("4.2.1");var rt="code",at="credential_used",st="active",lt="in_sync",ct=["calendar","condition_entity"],dt={type:"divider"},ht="masked_with_reveal",pt=!0,ut=!0,vt=!0,gt=!0,_t=!0;function ft(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 mt(n){return{cards:[{content:n,type:"markdown"}],title:arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Lock Code Manager"}}function yt(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 bt="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",wt="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",kt="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",Ct="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",Et={attribute:!0,type:String,converter:_e,reflect:!1,hasChanged:fe},St=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Et,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 At(n){return(e,t)=>"object"==typeof t?St(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 $t(n){return At(qn(qn({},n),{},{state:!0,attribute:!1}))}var Lt=n=>null!=n?n:je,Pt=ie(a||(a=Bn(["\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"]))),Mt=ie(s||(s=Bn(["\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"]))),zt=ie(l||(l=Bn(["\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"]))),Nt=ie(c||(c=Bn(["\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"]))),Ot=ie(d||(d=Bn(["\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"]))),Rt=ie(h||(h=Bn(["\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"]))),Ht=ie(p||(p=Bn(['\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']))),Tt=ie(u||(u=Bn(["\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"]))),It=ie(v||(v=Bn(["\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"]))),Dt=ie(g||(g=Bn(["\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"])));ie(_||(_=Bn(["\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n"])),Pt,Mt,zt,Nt,Ot,Rt,Ht,Tt,It,Dt);var Ut=[Pt,Mt,Nt,Rt,It,Dt,ie(f||(f=Bn(['\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 jt(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 Gn((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 yt([$t()],e.prototype,"_revealed",void 0),e}var Vt="empty",Ft="unreadable_code";function Bt(n){return null===n||""===n||n===Vt}function Wt(n,e){return n===Ft||(!Bt(n)||!!e)}var qt="masked_with_reveal",Zt=jt(it);class Kt extends Zt{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 Gn((function*(){var e={lock_entity_id:"lock.stub",type:"custom:lcm-lock-codes"};try{return yield Promise.race([Gn((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 qt}_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 De(m||(m=Bn(['\n
\n \n

Lock Code Manager Lock Codes

\n
\n
'])));var p=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,u=null!==(i=null!==(o=null!==(r=null===(a=this._data)||void 0===a?void 0:a.lock_name)&&void 0!==r?r:p)&&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:u)&&void 0!==l?l:"Lock Codes",g="suspended"===(null===(h=this._data)||void 0===h?void 0:h.sync_status);return De(y||(y=Bn(['\n \n
\n \n

',"

\n
\n ",'\n
\n ',"\n ","\n
\n
\n "])),g?"suspended":"",v,g?De(b||(b=Bn(['
\n \n Sync suspended — lock is unreachable\n
']))):je,this._error?De(x||(x=Bn(['
',"
"])),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=Wt(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 Gn((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 De(w||(w=Bn(['
No codes reported
'])));var i=this._groupSlots(t),o=this._identifyBorrowedSlots(i),r=this._renderGroupsWithBorrowing(i,o);return De(k||(k=Bn(['
',"
"])),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(qn(qn({},o),{},{rangeLabel:this._formatSlotRange(d),slots:d})))}}return t}_renderEmptySlotChip(n){return De(C||(C=Bn(['\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=Yn(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":"",Lt(v?"Click to manage this slot":void 0),Lt(v?"button":void 0),Lt(v?"0":void 0),v?"Manage slot ".concat(n.slot).concat(n.config_entry_title?" · ".concat(n.config_entry_title):""):je,v?()=>this._navigateToSlot(n.config_entry_id):je,v?e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this._navigateToSlot(n.config_entry_id))}:je,n.slot,n.config_entry_title?De(S||(S=Bn([' ·\n ',""])),n.config_entry_title):je,s,"active"===s||"inactive"===s||"disabled"===s?De(A||(A=Bn(['']))):je,a,void 0===d?je:De($||($=Bn(['\n ',"\n "])),d?"managed":"external",d?"Managed":"Unmanaged"),u?De(L||(L=Bn(['\n ','\n \n ',"\n \n "])),_?De(P||(P=Bn(['