/*! Thermal Intelligence Cards 0.1.0-beta.4 — beta frontend module. GPL-3.0-only. Editable source and build instructions accompany this project; no third-party runtime dependencies. */ var be=Object.freeze({dry_below:30,good_min:40,good_max:60,wet_above:70}),ee=Object.freeze(Object.keys(be)),Be=["low","high"],mt=t=>!!t&&typeof t=="object"&&!Array.isArray(t),gt=(t,e)=>Object.hasOwn(t,e)&&typeof t[e]=="number"&&Number.isFinite(t[e])&&t[e]>=0&&t[e]<=100;function oe(t){return mt(t)&&!Be.some(e=>Object.hasOwn(t,e))&&ee.every(e=>gt(t,e)&&Number.isInteger(t[e]))&&t.dry_belowObject.hasOwn(t,e))&&Be.every(e=>gt(t,e))&&t.low100?{key:"unknown",label:"Unavailable",colour:"#8795a8"}:X(e)?te.high?{key:"above",label:"Above legacy display band",colour:"#eb7777"}:{key:"within",label:"Within legacy display band",colour:"#7dc39c"}:oe(e)?tl?t:{...t,[e]:Math.max(o,Math.min(l,Math.round(i)))}}var Te="thermal-intelligence-beta-card",ue="thermal-intelligence-beta-card-editor",Se=`custom:${Te}`,_t=1,Ke=["humidity","trend","valve","energy","power","window","motion","presence","power_management","follow","custom"],ui=["blue","plum","black"],Me=new Set(["__proto__","prototype","constructor"]),$e=/^[a-z0-9_]+\.[a-z0-9_]+$/;function te(t){return t!==null&&typeof t=="object"&&(Object.getPrototypeOf(t)===Object.prototype||Object.getPrototypeOf(t)===null)}function D(t){if(typeof t=="number")return Number.isFinite(t)?t:null;if(typeof t!="string"||!t.trim()||!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(t.trim()))return null;let e=Number(t);return Number.isFinite(e)?e:null}function Ge(t,e,i=!1){if(typeof t!="number"||!Number.isFinite(t)||i&&t<=0)throw new Error(`${e} must be a finite${i?" positive":""} number.`);return t}function ne(t,e,{empty:i=!1}={}){if(typeof t!="string"||!i&&!t.trim()||/[\u0000-\u001f\u007f<>]/u.test(t))throw new Error(`${e} must be plain text.`);return t}function ae(t){if(!te(t))throw new Error("Card configuration must be an object.");if(t.type!==void 0&&t.type!==Se)throw new Error(`Card type must be ${Se}.`);if(t.schema_version!==void 0&&t.schema_version!==_t)throw new Error("Unsupported configuration schema_version.");let e=t.entity===void 0?"":t.entity;if(typeof e!="string"||e!==""&&(!$e.test(e)||!e.startsWith("climate.")))throw new Error("Select a climate entity.");let i=t.adapter===void 0?"auto":t.adapter;if(!["auto","standard","vtherm"].includes(i))throw new Error("Adapter must be auto, standard or vtherm.");let r=t.background===void 0?"blue":t.background;if(!ui.includes(r))throw new Error("Background must be blue, plum or black.");if(t.theme!==void 0&&!["auto","light","dark"].includes(t.theme))throw new Error("Legacy theme must be auto, light or dark.");let n={...t,type:Se,schema_version:_t,entity:e,adapter:i,background:r};for(let o of["trend_entity","humidity_entity","valve_position_entity"])if(t[o]!==void 0&&(typeof t[o]!="string"||t[o]!==""&&(!$e.test(t[o])||!t[o].startsWith("sensor."))))throw new Error(`${o} must be a sensor entity ID or empty.`);if(t.response_arc!==void 0){if(!te(t.response_arc))throw new Error("response_arc must be an object.");let o={...t.response_arc};for(let[l,a]of[["room_sensor","sensor"],["native_thermostat","climate"],["confirmed_output","climate"],["heat_source","climate"]]){let s=o[l];if(s!==void 0&&(typeof s!="string"||s!==""&&(!$e.test(s)||!s.startsWith(`${a}.`))))throw new Error(`response_arc.${l} must be a ${a} entity ID or empty.`)}if(o.freshness_minutes!==void 0&&(Ge(o.freshness_minutes,"response_arc.freshness_minutes",!0),o.freshness_minutes>30))throw new Error("response_arc.freshness_minutes must be at most 30 minutes.");n.response_arc=o}t.name!==void 0&&(n.name=ne(t.name,"Name",{empty:!0})),t.step!==void 0&&(n.step=Ge(t.step,"Step",!0));for(let o of["show_activity","show_details"]){if(t[o]!==void 0&&typeof t[o]!="boolean")throw new Error(`${o} must be true or false.`);n[o]=t[o]??!0}if(t.preset_labels!==void 0&&!te(t.preset_labels))throw new Error("preset_labels must be an object.");n.preset_labels={};for(let[o,l]of Object.entries(t.preset_labels??{})){if(Me.has(o))throw new Error("Invalid preset label key.");ne(o,"Preset key"),n.preset_labels[o]=ne(l,"Preset label")}if(t.telemetry!==void 0&&!Array.isArray(t.telemetry))throw new Error("telemetry must be an array.");if(n.telemetry=(t.telemetry??[]).map(o=>{if(!te(o)||!Ke.includes(o.kind))throw new Error("Unsupported telemetry kind.");if(typeof o.entity!="string"||o.entity!==""&&!$e.test(o.entity))throw new Error("Telemetry entity must be an entity ID or empty.");let l={...o};if(o.attribute!==void 0&&(ne(o.attribute,"Telemetry attribute"),o.attribute.split(".").some(a=>!a||Me.has(a))))throw new Error("Invalid telemetry attribute path.");for(let a of["label","unit"])o[a]!==void 0&&ne(o[a],`Telemetry ${a}`);return o.max_age_seconds!==void 0&&Ge(o.max_age_seconds,"Telemetry max_age_seconds",!0),l}),t.features!==void 0&&!Array.isArray(t.features))throw new Error("features must be an array.");if(n.features=(t.features??[]).map(o=>{if(!te(o)||typeof o.entity!="string"||o.entity!==""&&!$e.test(o.entity))throw new Error("A feature requires an entity ID or an empty setup source.");let l={...o};for(let a of["label","unit"])o[a]!==void 0&&ne(o[a],`Feature ${a}`,{empty:!0});if(o.attribute!==void 0&&o.attribute!==""&&(ne(o.attribute,"Feature attribute"),o.attribute.split(".").some(a=>!/^\w+$/.test(a)||Me.has(a))))throw new Error("Invalid feature attribute path.");if(o.icon!==void 0&&(typeof o.icon!="string"||!/^[a-z0-9_-]+:[a-z0-9_-]+$/i.test(o.icon)))throw new Error("Feature icon must be a named icon, such as mdi:thermometer.");if(o.preset!==void 0&&(ne(o.preset,"Feature preset"),Me.has(o.preset)))throw new Error("Invalid feature preset value.");return l}),t.humidity_bands!==void 0){if(!te(t.humidity_bands))throw new Error("humidity_bands must be an object.");if(!oe(t.humidity_bands)&&!X(t.humidity_bands))throw new Error("Humidity guides require ordered integer dry_below < good_min < good_max < wet_above within 0–100, or explicit legacy low < high bounds. Do not mix the formats.");n.humidity_bands={...t.humidity_bands}}return n}var pe=Object.freeze({window:{label:"Window detection",active:"Open",inactive:"Closed",icon:"mdi:window-open-variant",inactiveIcon:"mdi:window-closed-variant",colour:"#efb86b",flag:"is_window_configured",path:"window_manager.window_state",note:"This is the selected window detector’s state. Selecting its icon does not enable window protection or establish that a radiator valve is closed."},motion:{label:"Motion detection",active:"Detected",inactive:"Clear",icon:"mdi:motion-sensor",inactiveIcon:"mdi:motion-sensor-off",colour:"#b9a1e6",flag:"is_motion_configured",path:"motion_manager.motion_state",note:"VT uses room motion or occupancy to choose the configured motion/no-motion temperatures while Activity is selected, after its detection delays. Displaying a sensor here does not enable that feature."},presence:{label:"Presence detection",active:"Home",inactive:"Away",icon:"mdi:home-account",colour:"#7dc39c",flag:"is_presence_configured",path:"presence_manager.presence_state",note:"VT Presence selects home or away preset temperatures. Its input can be a person, occupancy sensor or helper; room Motion is a separate feature. This display does not establish household policy or alarm state."},power_management:{label:"Power management",active:"Limiting",inactive:"Normal",icon:"mdi:transmission-tower",colour:"#efb86b",flag:"is_power_configured",path:"power_manager.overpowering_state",note:"This reports the selected power-management state. Limiting is a control restriction, not a power measurement or proof that heating has stopped."},follow:{label:"Follow underlying target",active:"On",inactive:"Off",icon:"mdi:link-variant",colour:"#73c9bd",path:"vtherm_over_climate.follow_underlying_temp_change",note:"When On, VT can adopt target changes made on its underlying device. Off does not disable temperature regulation. Selecting this icon does not change the setting."}});function vt(t,e){let i=pe[t];if(!i)return null;let r=typeof e=="string"?e.trim().toLowerCase():null,n=e===!0||["on",i.active.toLowerCase()].includes(r),o=e===!1||["off",i.inactive.toLowerCase(),...t==="presence"?["not_home"]:[]].includes(r);return n===o?null:n}var A={unknown:"#94a3b8",off:"#7c8ba2",cooling:"#66cfff",above:"#c4a7ff",at:"#62e3b0",below:"#50c8ff",warming:"#ff9459"},wt=["off","heat","cool","heat_cool","auto","dry","fan_only"],pi={warming:"Recent warming",above_target:"Above target",at_target:"At target",below_target:"Below target",off:"Off",cooling:"Cooling reported",response_expired:"Response evidence expired",not_rising:"Not rising",rapid_change:"Rapid temperature change"},E=(t,e,i,r=null,n=null,o=!1)=>({color:t,reason:e,label:pi[e]||"Response unverified",explanation:i,responding:o,delta:r,expiresAt:n}),ye=t=>!!t&&typeof t.state=="string"&&t.state.trim()!==""&&!["unknown","unavailable"].includes(t.state),Ie=(t,e)=>typeof e=="string"&&Object.hasOwn(t?.states??{},e)?t.states[e]:null;function hi(t,e){if(!e||!t.ready||!t.available||!Number.isFinite(t.current))return E(A.unknown,"unavailable","Target comparison is unavailable because connection, readiness or current temperature is unavailable. Physical heating is unverified.");if(!["°C","°F"].includes(t.unit))return E(A.unknown,"unit_unknown","Target comparison requires a known climate display unit. Physical heating is unverified.");if(t.ranged||t.keys?.length!==1||t.keys[0]!=="temperature")return E(A.unknown,"target_unknown","Target comparison requires one selected climate target; a temperature range is not reduced to an invented single target. Physical heating is unverified.");let i=t.targets?.temperature,{min:r,max:n}=t;if(![i,r,n].every(Number.isFinite)||r>=n||!Number.isFinite(n-r)||in)return E(A.unknown,"target_invalid","Target comparison requires a finite selected climate target within valid reported limits. Physical heating is unverified.");let o=t.current-i;return Number.isFinite(o)?wt.includes(t.state)?t.state==="off"?E(A.off,"off","The selected climate mode is Off. Its target comparison does not establish physical heating.",o):t.action==="cooling"?E(A.cooling,"cooling","The selected climate reports cooling. The target comparison and reported activity do not establish physical heat delivery.",o):o>.2+1e-9?E(A.above,"above_target","Current temperature is above the selected climate target. This target comparison does not establish physical heating.",o):o>=-1e-9?E(A.at,"at_target","Current temperature is at the selected climate target or no more than 0.2 display degrees above it. This target comparison does not establish physical heating.",o):E(A.below,"below_target","Current temperature is below the selected climate target. Accepted-output and fresh warming-response evidence are not established; this is a target comparison only.",o):E(A.unknown,"mode_unknown","Target comparison is unavailable because the climate mode is unrecognized. Physical heating is unverified.",o):E(A.unknown,"target_invalid","Target comparison could not produce a finite temperature difference. Physical heating is unverified.")}function fi(t,e){if(t?.status!=="available"||!Number.isFinite(t.value))return null;let r=(e==="°C"?["°C/h","°C/hour","°C/hr","degC/h"]:["°F/h","°F/hour","°F/hr"]).includes(t.unit)?t.value:t.unit===`${e}/min`?t.value*60:null;return Number.isFinite(r)?r:null}function xt(t,e){let i=e.telemetry?.find(n=>n.kind===t?.kind&&n.entity===t.source?.entity&&(n.attribute??null)===(t.source?.attribute??null)),r=Date.parse(t?.source?.timestamp);return i&&Number.isFinite(i.max_age_seconds)&&i.max_age_seconds>0&&Number.isFinite(r)?r+i.max_age_seconds*1e3:1/0}function kt({hass:t,config:e,climate:i,trendSource:r,valve:n,now:o,connected:l}){let a=hi(i,l);if(e.response_arc===void 0)return a;if(!te(e.response_arc))return E(A.unknown,"response_configuration_invalid","Response sources must be explicitly configured.");if(i.provider?.kind!=="vtherm")return{...a,explanation:`${a.explanation} Corroborated warming response is supported only by the documented VT observation contract; a standard climate heating action is insufficient.`};if(["unavailable","unit_unknown","target_unknown","target_invalid","mode_unknown"].includes(a.reason))return a;let s=e.response_arc,d=i.attrs??{},p=d.configuration?.temperature_unit;if(!["°C","°F"].includes(p))return E(A.unknown,"response_unit_unknown","VT must declare its internal temperature unit before response evidence can be compared.");if(p!==i.unit)return E(A.unknown,"response_unit_mismatch","VT internal and Home Assistant display temperature units differ; no response conversion is assumed.");let c=d.current_state?.hvac_mode??i.state;if(!wt.includes(c))return E(A.unknown,"mode_unknown","The effective VT mode is unknown.");let y=d.current_state&&Object.hasOwn(d.current_state,"target_temperature")?D(d.current_state.target_temperature):i.targets.temperature;if(y===null||!Number.isFinite(y)||yi.max||!Number.isFinite(i.current-y))return E(A.unknown,"target_invalid","A valid effective VT target in the declared unit is unavailable.");let g=i.current-y,h=(O,U,H=null)=>E(A.below,O,U,g,H);if(c==="off")return E(A.off,"off","Room heating is switched off.",g);let f=n!=null&&n.status!=="disabled"||!!s.confirmed_output,x=Ie(t,s.native_thermostat),u=s.native_thermostat?ye(x)&&x.state!=="off"?x.attributes?.hvac_action:null:i.action;if(!f&&u==="cooling")return E(A.cooling,"cooling","The thermostat reports cooling.",g);let _=Ie(t,s.room_sensor),b=ye(_)?D(_.state):null;if(!s.room_sensor||b===null||_.attributes?.unit_of_measurement!==i.unit)return E(A.unknown,"room_source_unavailable","The mapped room-temperature source is unavailable or uses different units.",g);if(g>.2+1e-9)return E(A.above,"above_target","The room is above its effective target.",g);if(g>=-1e-9)return E(A.at,"at_target","The room is at its effective target.",g);if(c!=="heat")return h("no_heat_request","The room is below target; effective Heat is not enabled.");if(i.action!=="heating")return h("no_heat_request","The room is below target; VT is not reporting a current heating request.");let $=d.safety_manager,k=d.window_manager,L=d.power_manager;if([d.is_safety_configured,d.is_window_configured,d.is_power_configured].some(O=>typeof O!="boolean"))return h("restricted","VT restriction configuration is unknown; response evidence is not confirmed.");if(d.is_safety_configured&&$?.safety_state!=="off"||d.is_window_configured&&k?.window_state!=="off"&&k?.is_window_bypass!==!0||d.is_power_configured&&L?.overpowering_state!=="off")return h("restricted","A configured VT restriction is active or its state is unknown.");let q=s.heat_source||s.native_thermostat,z=Ie(t,q);if(q&&(!ye(z)||z.state!=="heat"))return h("heat_source_unavailable","The mapped heat source is not available in Heat mode.");if(f){if(n?.status!=="available"||n.unit!=="%"||!Number.isFinite(n.value)||n.value<=0||n.value>100)return h("trv_not_open","The room is below target; a current open TRV observation is not reported.");let O=d.vtherm_over_climate?.underlying_entities,U=s.confirmed_output,H=Ie(t,U),N=H?.attributes??{},G=N.automatic_recovery;if(!U||!Array.isArray(O)||!O.includes(U)||!ye(H)||H.state!=="heat"||N.command_confirmed!==!0||N.native_write_unconfirmed!==!1||N.control_fault!==null||N.warm_cutoff_inhibited!==!1||N.manual_reconciliation_pending!==!1||N.physical_override_unadopted!==!1||G!==void 0&&!(te(G)&&G.active===!1&&["idle","recovered"].includes(G.phase)&&(G.phase!=="recovered"||G.verified===!0)))return h("output_unconfirmed","The current TRV output is not confirmed, or control is restricted.")}else{let O=d.vtherm_over_climate?.regulation,U=D(O&&Object.hasOwn(O,"regulated_target_temperature")?O.regulated_target_temperature:d.regulated_target_temperature),H=ye(x)?D(x.attributes?.temperature):null,N=D(x?.attributes?.target_temp_step);if(!ye(x)||x.state!=="heat"||U===null||H===null||Math.abs(H-U)>(N!==null&&N>0?N/2:.05)+1e-9)return h("output_unconfirmed","The native thermostat has not confirmed the current regulated target.")}let K=fi(r,i.unit),M=D(d.specific_states?.temperature_slope);if(Math.abs(b-i.current)>.05+1e-6||K===null||M===null||Math.abs(M-K)>.01)return h("response_unverified","Room temperature and shared VT trend do not provide matching response evidence.");let Z=D(s.freshness_minutes),R=D($?.safety_delay_min),Y=Math.min(Z!==null&&Z>0&&Z<=30?Z:10,R!==null&&R>0?R:1/0),se=Date.parse(_.last_changed),j=Date.parse(r?.source?.timestamp),W=O=>Number.isFinite(o)&&Number.isFinite(O)&&O<=o&&o-O<=Y*6e4;if(!W(se)||!W(j))return h("response_expired",`Recent warming cannot be established: the room value change and trend must both be within ${Y} minutes. A VT attribute refresh does not renew this evidence.`);let B=Math.min(se+Y*6e4,j+Y*6e4,xt(r,e),f?xt(n,e):1/0);if(!Number.isFinite(B)||BC?h("rapid_change","The temperature change is unusually fast; it is not used as ordinary warming evidence.",B):E(A.warming,"warming",`Recent room readings show warming while the current heat request is accepted. Orange describes these observations together; it does not prove boiler firing or radiator flow. Evidence expires after ${Y} minutes without a room value change.`,g,B,!0)}var mi={humidity:"Humidity",trend:"Temperature trend",valve:"Valve",energy:"Energy",power:"Power",custom:"Telemetry"},gi={humidity:["%"],valve:["%"],trend:["°C/h","°F/h","°C/hour","°C/hr","degC/h","°F/hour","°F/hr","°C/min","°F/min"],power:["W","kW","MW"],energy:["Wh","kWh","MWh","J","kJ","MJ","GJ"]},bi=new Set(["__proto__","prototype","constructor"]);function F(t,e){let i=t;for(let r of e.split(".")){if(bi.has(r)||i===null||typeof i!="object"||!Object.hasOwn(i,r))return{found:!1,value:void 0};i=i[r]}return{found:!0,value:i}}function Ye(t,e,i,r=!0,n={}){return e.telemetry.map(o=>{let l={entity:o.entity,attribute:o.attribute??null,evidence:"Reported by configured Home Assistant entity"},a=pe[o.kind],s={kind:o.kind,label:o.label||a?.label||mi[o.kind],status:"unknown",value:null,unit:o.unit??null,source:l,detail:"",...a?{icon:a.icon,topic:o.kind}:{}};o.kind==="humidity"&&(s.guides=ce(e),s.guideMode=X(s.guides)?"legacy":"canonical",s.band="unknown",s.bandLabel="Local display band",s.bandStatus=ke(null,s.guides),s.colour=s.bandStatus.colour);let d=(x,u)=>({...s,status:x,detail:u});if(!o.entity)return d("disabled","No source configured.");if(!r)return d("unavailable","Home Assistant is disconnected; this source cannot be refreshed.");let p=Object.hasOwn(t?.states??{},o.entity)?t.states[o.entity]:null;if(!p||p.state==="unavailable")return d("unavailable","Configured source is unavailable.");if(p.state==="unknown")return d("unknown","Configured source has no known state.");let c=p.attributes??{};if(typeof c.friendly_name=="string"&&c.friendly_name.trim()&&(s.source.name=c.friendly_name),a?.flag&&n.provider?.kind==="vtherm"&&o.entity===e.entity&&(o.attribute===a.path||o.kind==="window"&&o.attribute==="window_manager.window_auto_state")){let x=o.attribute==="window_manager.window_auto_state"?"is_window_auto_configured":a.flag,u=F(c,x).value;if(u===!1)return d("disabled","This feature is not configured in VT. Selecting its icon does not enable it.");if(u!==!0)return d("unknown","VT does not report whether this feature is configured.");if(["window","motion","presence"].includes(o.kind)&&o.attribute===a.path){let _=F(c,`${o.kind}_manager.${o.kind}_sensor_entity_id`).value;if(typeof _=="string"&&_.trim()){let b=Object.hasOwn(t.states,_)?t.states[_]:null;if(!b||["unknown","unavailable"].includes(b.state)){let $=b?.state==="unknown"?"unknown":"unavailable";return d($,`VT's configured ${o.kind} input is ${$}. Its retained detector state is not a current observation; this does not change VT's control behaviour.`)}}}}let y=o.attribute?F(c,o.attribute):{found:!0,value:p.state};if(!y.found)return d("unavailable","Configured attribute is missing.");if(y.value==="unavailable")return d("unavailable","Configured value is unavailable.");if(y.value===null||y.value===void 0||y.value==="unknown")return d("unknown","Configured value is unknown.");let g=!o.attribute&&Object.hasOwn(c,"unit_of_measurement")&&typeof c.unit_of_measurement=="string"?c.unit_of_measurement:null;if(g&&o.unit&&g!==o.unit)return d("invalid",`Source unit ${g} does not match configured unit ${o.unit}; no conversion was applied.`);if(s.unit=g||o.unit||null,!a&&o.kind!=="custom"&&(!s.unit||!gi[o.kind].includes(s.unit)))return d("invalid","A compatible unit is required; no unit is inferred.");let h=D(y.value);if(a){if(s.unit)return d("invalid","A categorical state must not have a measurement unit.");let x=vt(o.kind,y.value);if(x===null)return d("invalid","The source does not report a recognised state for this telemetry type.");s.value=x?a.active:a.inactive,s.icon=!x&&a.inactiveIcon?a.inactiveIcon:a.icon,s.activity=x?"active":"neutral",s.colour=x?a.colour:"#a9b8cb"}else if(o.kind==="custom"&&typeof y.value=="boolean")s.value=y.value;else if(o.kind==="custom"&&typeof y.value=="string"&&h===null&&y.value.trim())s.value=y.value;else{if(h===null)return d("invalid","Source value is not a finite number.");if(s.value=h,(o.kind==="humidity"||o.kind==="valve")&&(h<0||h>100))return d("invalid","Percentage must be between 0 and 100.")}let f=typeof p.last_updated=="string"?Date.parse(p.last_updated):NaN;if(s.source.timestamp=Number.isFinite(f)?p.last_updated:null,o.max_age_seconds!==void 0){if(!Number.isFinite(f)||!Number.isFinite(i)||f>i)return d("unknown","Freshness cannot be established from the source timestamp.");if(i-f>o.max_age_seconds*1e3)return d("stale","Older than the configured maximum age.")}return o.kind==="humidity"&&(s.bandStatus=ke(h,s.guides),s.band=s.bandStatus.key,s.colour=s.bandStatus.colour),d("available",a?.note||"Reported observation; no controller decision is inferred.")})}var yi={humidity:"mdi:water-percent",trend:"mdi:trending-up",valve:"mdi:pipe-valve",energy:"mdi:lightning-bolt",power:"mdi:flash",custom:"mdi:information-outline"},$t={frost:"mdi:snowflake",eco:"mdi:leaf",comfort:"mdi:sofa",boost:"mdi:fire"},St=t=>typeof t=="string"&&/^[a-z0-9_-]+:[a-z0-9_-]+$/i.test(t),_i=t=>typeof t=="boolean"?t?"On":"Off":String(typeof t=="number"?Number(t.toPrecision(12)):t??"");function Tt(t,e,i,r,n){let o=Ye(t,e,r,n,i),l=(u,_)=>Object.hasOwn(e,u)?o.find($=>$.kind===_&&$.source.entity===e[u]&&$.source.attribute===null)??Ye(t,{...e,telemetry:[{kind:_,entity:e[u]}]},r,n)[0]:o.find(b=>b.kind===_)??null,a=l("humidity_entity","humidity"),s=l("trend_entity","trend"),d=l("valve_position_entity","valve"),p=e.features.map(u=>{let _=Object.hasOwn(t?.states??{},u.entity)?t.states[u.entity]:null,b=u.label||(u.attribute?u.attribute.split(".").at(-1).replaceAll("_"," "):_?.attributes?.friendly_name)||u.entity||"Unconfigured feature",$={kind:"custom",entity:u.entity,label:b};return u.attribute&&($.attribute=u.attribute),u.unit&&($.unit=u.unit),{...Ye(t,{...e,telemetry:[$]},r,n)[0],icon:u.icon,feature:u}}),c=vi(p,e,i,n),y=[...p,...o];a&&!y.some(u=>u.kind==="humidity"&&u.source.entity===a.source.entity&&u.source.attribute===a.source.attribute)&&y.splice(p.length,0,a);let g=y.map((u,_)=>{let b=u.feature?.preset??null,$=b?c[b]:null;if(b&&(!$?.selected||$.status!=="available"))return null;let L=(Object.hasOwn(t?.states??{},u.source.entity)?t.states[u.source.entity]:null)?.attributes?.icon,q=u.status==="available"?_i(u.value)+(u.unit?` ${u.unit}`:""):u.status==="disabled"?"Not configured":u.status.charAt(0).toUpperCase()+u.status.slice(1);return{...u,key:`extra-${_}-${u.source.entity}-${u.source.attribute||"state"}`,entity:u.source.entity,attribute:u.source.attribute,rawValue:u.value,value:q,shortLabel:$?.label??u.label,icon:St(u.icon)?u.icon:b&&Object.hasOwn($t,b)?$t[b]:St(L)?L:yi[u.kind],colour:u.status==="available"?u.colour:"#8795a8",activity:b?"active":u.status!=="available"?"unknown":u.activity||([!0,"on","On","true"].includes(u.value)?"active":"neutral"),activityLabel:b?"Preset selected":u.kind==="humidity"?u.status==="available"?u.bandStatus.label:u.status.charAt(0).toUpperCase()+u.status.slice(1):"",topic:u.topic||(u.kind==="humidity"?"humidity":"entity"),preset:b}}).filter(Boolean),h=xi(s,i),f=wi(d,i,n),x=kt({hass:t,config:e,climate:i,trendSource:s,valve:d,now:r,connected:n});return{telemetry:o,humidity:a,features:p,headerTelemetry:g,presetReadings:c,trend:h,output:f,arc:x}}function vi(t,e,i,r){let n=i.choices?.find(a=>a.key==="preset_mode"),o=n?.options??[],l=[...new Set([...o,...t.map(a=>a.feature.preset).filter(a=>typeof a=="string")])];return Object.fromEntries(l.map(a=>{let s=t.filter(f=>f.feature.preset===a),d=s.length===1?s[0]:null,p=Object.hasOwn(e.preset_labels,a)?e.preset_labels[a]:d?.label||a.charAt(0).toUpperCase()+a.slice(1),c=r&&i.ready&&i.available&&i.state==="heat"&&a!=="none"&&o.includes(a)&&n?.value===a,y={preset:a,label:p,status:"unknown",value:null,unit:d?.unit??null,source:d?.source??null,selected:!!c,reason:""},g=(f,x,u=null)=>[a,{...y,status:f,value:u,reason:x}];if(!o.includes(a))return g("unsupported","This preset is not advertised by the selected climate entity.");if(!s.length)return g("disabled","No explicit temperature source is associated with this preset.");if(s.length>1)return g("invalid","Multiple temperature sources are associated with this preset; no source is selected.");if(d.status!=="available")return g(d.status,d.detail);let h=D(d.value);return h===null?g("invalid","The associated preset reading must be a finite number."):["°C","°F"].includes(d.unit)?["°C","°F"].includes(i.unit)?d.unit!==i.unit?g("invalid","The preset reading and selected climate use different temperature units; no conversion was applied."):g("available","Explicit preset-temperature observation; only the provider-selected preset controls its active indicator.",h):g("unknown","The selected climate temperature unit is unknown."):g("invalid","The associated preset reading requires a reported or explicit temperature unit.")}))}function xi(t,e){let i=t?.value??null,r=t?.unit??null,n=typeof r=="string"&&r.startsWith("°F"),o=typeof r=="string"&&r.endsWith("/min")?i*60:i,l=Number.isFinite(o),a=t?.status==="available"&&l&&e.ready&&e.current!==null,s=n?.09:.05,d=n?4.5:2.5,p=a?Math.abs(o)<=s?"Stable":o>0?"Rising":"Falling":"Trend unavailable",c=a?o===0?"#c4d4e7":Math.abs(o)<=s?"#62e3b0":o>d?"#ff5252":o>0?"#ff962e":o<-d?"#4169ff":"#32bfff":"#8795a8",y=a?i!==0&&Number(i.toFixed(2))===0?i.toPrecision(3):i.toFixed(2):"";return{available:!!a,value:i,unit:r,label:p,color:c,text:a?`${p} · ${i>0?"+":""}${y} ${r}`:p,status:a?"available":t?.status==="available"?l?"unavailable":"invalid":t?.status??"disabled",source:t?.source??null,detail:t?.detail||"No temperature-trend source is configured."}}function wi(t,e,i){if(t){let d=t.status==="available"?t.value:null,p=d===null?"Unavailable":d>0&&d<1?"<1%":`${Number(d.toFixed(1))}%`;return{kind:"trv",icon:"mdi:pipe-valve",percentage:d,display:p,text:d===null?"TRV unavailable":`TRV ${d>0?"Open":"Closed"} · ${p}`,tone:!i||d===null?"unknown":d>0?"open":"neutral",status:t.status,source:t.source,detail:"Reported valve opening; this does not establish boiler heating or heat delivery."}}let r=e.state,n=e.action,o=r==="off"&&["heating","cooling","fan","drying"].includes(n),l=!i||!e.available||o,a=e.provider.kind==="vtherm",s={heating:a?"Heat requested":"Heating (reported)",cooling:"Cooling (reported)",idle:"Idle",off:"Off",fan:"Fan running",drying:"Drying",preheating:"Preheating (reported)",defrosting:"Defrosting (reported)"}[n];return{kind:"thermostat",icon:null,action:l?null:n,mode:r,text:l?o?"Activity uncertain":"Thermostat unavailable":r==="off"?"Off":s||(r==="heat"?"Heat enabled":"Activity unavailable"),tone:l?"unknown":n==="heating"?a?"request":"heat":n==="cooling"?"cool":"neutral",status:l?"unavailable":s||r==="off"?"available":"unknown",source:{entity:e.entity,attribute:"hvac_action",evidence:"Selected climate entity reports its activity"},detail:"Thermostat-reported activity; physical heat delivery is not verified."}}var Le=(t,e)=>t!=null&&Object.hasOwn(t,e);function Rt(t,e){let i=(r,n)=>({kind:"unknown",status:r,label:"Provider unverified",evidence:"No matching registry evidence",reason:n});if(!t.entity)return i("unconfigured","Select a climate entity.");if(e!=null){if(!Le(e,"entity_id")||e.entity_id!==t.entity||!Le(e,"platform")||typeof e.platform!="string"||!e.platform.trim())return i("invalid","Registry identity does not match the selected entity.");let r=e.platform==="versatile_thermostat";return!r&&t.adapter==="vtherm"?i("invalid","The selected VT adapter conflicts with the entity registry."):{kind:r?"vtherm":"standard",status:"verified",label:r?"Versatile Thermostat":"Home Assistant climate",evidence:`Entity registry: ${e.platform}`,reason:r&&t.adapter==="standard"?"Registry identifies VT; VT readiness restrictions remain active.":""}}return t.adapter==="auto"?i("unknown","Provider lookup is pending or unavailable. Select an adapter explicitly if this entity is not registered."):{kind:t.adapter,status:"configured",label:t.adapter==="vtherm"?"Versatile Thermostat":"Home Assistant climate",evidence:"Explicit card configuration; registry unavailable",reason:""}}function Ct(t,e){if(t.kind==="unknown")return{status:"unknown",ready:!1,reason:t.reason};if(t.kind==="standard")return{status:"not_applicable",ready:!0,reason:"No additional readiness contract is declared for standard climate entities."};if(!Le(e,"is_ready")||typeof e.is_ready!="boolean")return{status:"unknown",ready:!1,reason:"Versatile Thermostat readiness is unknown."};if(e.is_ready){let i=F(e,"specific_states.is_locked"),r=F(e,"lock_manager.lock_users");if(i.value===!0&&r.value!==!1)return{status:"locked",ready:!1,reason:"Versatile Thermostat is locked for user control, or the lock scope is unknown."};if(Le(e,"is_lock_configured")&&e.is_lock_configured===!0&&typeof i.value!="boolean")return{status:"unknown",ready:!1,reason:"Versatile Thermostat lock state is unknown."}}return e.is_ready?{status:"ready",ready:!0,reason:"Versatile Thermostat reports initialization complete."}:{status:"not_ready",ready:!1,reason:"Versatile Thermostat is not ready."}}function At(t,e){let i=F(t,"specific_states.is_locked").value,r=F(t,"is_lock_configured").value;return r!==!0&&i!==!0&&(r===!1||typeof i!="boolean")?null:e?typeof i!="boolean"?{status:"unknown",label:"VT lock state unknown",icon:"mdi:lock-question"}:i?{status:"locked",label:"VT locked",icon:"mdi:lock-outline"}:{status:"unlocked",label:"VT unlocked",icon:"mdi:lock-open-variant-outline"}:{status:"unavailable",label:"VT lock state unavailable",icon:"mdi:lock-question"}}function ki(t,e){let i=F(t,"configuration.type").value,r=["over_switch","over_valve","over_climate_valve"];return[{label:"Regulation mode",path:"vtherm_over_climate.regulation.auto_regulation_mode",types:["over_climate"],flag:"vtherm_over_climate.is_regulated",detail:"Provider-selected climate regulation mode; observation only."},{label:"Regulated target",path:"vtherm_over_climate.regulation.regulated_target_temperature",types:["over_climate"],flag:"vtherm_over_climate.is_regulated",numeric:!0,temperature:!0,detail:"Internal regulated target in the provider-declared unit; this is not a measured temperature."},{label:"Proportional function",path:"configuration.proportional_function",types:r,detail:"Provider-declared regulation function; no algorithm is reconstructed."},{label:"Regulation demand",path:"on_percent",types:r,numeric:!0,max:1,scale:100,unit:"%",detail:"Provider demand after its safety and maximum-output adjustments; not measured power or heat delivery."},{label:"Requested valve opening",path:"valve_open_percent",types:["over_valve","over_climate_valve"],numeric:!0,max:100,unit:"%",detail:"Provider-requested opening; not independently observed valve position or heat delivery."}].map(o=>{let l={label:o.label,value:null,status:"unknown",unit:o.unit??null,source:`Versatile Thermostat: ${o.path}`,detail:o.detail},a=d=>({...l,value:d==="available"?l.value:null,status:d});if(!e)return a("unavailable");if(typeof i!="string"||!i.trim()||["unknown","unavailable"].includes(i))return a("unknown");if(!o.types.includes(i))return a("unsupported");if(o.flag){let d=F(t,o.flag).value;if(typeof d!="boolean")return a("unknown");if(!d)return a("disabled")}let s=F(t,o.path);if(!s.found||s.value==="unavailable")return a("unavailable");if(s.value===null||s.value===void 0||s.value==="unknown")return a("unknown");if(o.numeric){let d=D(s.value);if(d===null||o.max!==void 0&&(d<0||d>o.max))return a("invalid");if(l.value=o.scale?Number((d*o.scale).toPrecision(12)):d,o.temperature){let p=F(t,"configuration.temperature_unit").value;if(!["°C","°F"].includes(p))return a("unknown");l.unit=p}}else{if(typeof s.value!="string"||!s.value.trim())return a("invalid");l.value=s.value}return a("available")})}function Et(t,e){return[...[["Readiness","is_ready"],["Initialized","specific_states.is_initialized"],["Device active","specific_states.is_device_active"],["Activity requested","specific_states.should_device_be_active"],["Off reason","specific_states.hvac_off_reason"],["Mode reason","specific_states.hvac_mode_reason"],["Locked","specific_states.is_locked"],["Sleeping","specific_states.is_sleeping"],["Window","window_manager.window_state","is_window_configured"],["Automatic window detection","window_manager.window_auto_state","is_window_auto_configured"],["Presence","presence_manager.presence_state","is_presence_configured"],["Motion","motion_manager.motion_state","is_motion_configured"],["Safety","safety_manager.safety_state","is_safety_configured"],["Power limiting","power_manager.overpowering_state","is_power_configured"]].map(([n,o,l])=>{let a=F(t,o),s=l?F(t,l):null,d=["specific_states.hvac_off_reason","specific_states.hvac_mode_reason"].includes(o),c=d&&a.found&&a.value===null?"None reported":a.value,y=d?typeof c=="string"&&c.trim().length>0:["string","number","boolean"].includes(typeof c)&&(typeof c!="number"||Number.isFinite(c)),g=!a.found||c==="unavailable"?"unavailable":!y||c==="unknown"?"unknown":"available";return s&&(!s.found||typeof s.value!="boolean")&&(g="unknown"),s?.value===!1&&(g="disabled"),e||(g="unavailable"),{label:n,value:g==="available"?c:null,status:g,source:`Versatile Thermostat: ${o}`}}),...ki(t,e)]}var he=Object.freeze({TARGET_TEMPERATURE:1,TARGET_TEMPERATURE_RANGE:2,FAN_MODE:8,PRESET_MODE:16,SWING_MODE:32,SWING_HORIZONTAL_MODE:512}),Je=(t,e)=>t!=null&&Object.hasOwn(t,e),ie=(t,e)=>Je(t,e)?t[e]:void 0,Re=(t,e)=>D(ie(t,e)),$i=t=>Array.isArray(t)?[...new Set(t.filter(e=>typeof e=="string"&&e.trim()))]:[],We=(t,e)=>Je(t?.services?.climate,e)&&t.services.climate[e]!==null&&t.services.climate[e]!==void 0;function Si(t,e,i,r){if(!r)return{label:"Disconnected",detail:"Activity cannot be refreshed."};if(!i)return{label:"Unavailable",detail:"The climate entity has no available state."};let n=ie(t.attributes,"hvac_action");return typeof n!="string"||!n.trim()||["unknown","unavailable"].includes(n)?{label:"Unknown",detail:"No integration-reported activity is available."}:e.kind==="vtherm"&&n==="heating"?{label:"Heat requested",detail:"Versatile Thermostat reports heating; physical heat delivery is not verified."}:{label:`${n.charAt(0).toUpperCase()}${n.slice(1)} (reported)`,detail:"Thermostat-reported activity; physical heat delivery is not verified."}}function Ot(t,e,i=null,{now:r=Date.now()}={}){let n=ae(e),o=Je(t?.states,n.entity)?t.states[n.entity]:null,l=o?.attributes??{},a=t?.connected===!0||t?.connected!==!1&&t?.connection?.connected===!0,s=!!o&&typeof o.state=="string"&&!["unknown","unavailable",""].includes(o.state),d=Rt(n,i),p=Ct(d,l),c=a&&s&&p.ready,y=t?.config?.unit_system?.temperature,g=["°C","°F"].includes(y)?y:null,h=Re(l,"min_temp"),f=Re(l,"max_temp"),x=ie(l,"target_temp_step"),u=x!==void 0?D(x):n.step??(g==="°C"?.5:g==="°F"?1:null),_=x!==void 0?"entity":n.step!==void 0?"configuration":"display increment",b=Re(l,"supported_features"),$=Number.isSafeInteger(b)&&b>=0?b:0,k=C=>($&C)===C,L=k(he.TARGET_TEMPERATURE_RANGE)&&(o?.state==="heat_cool"||!k(he.TARGET_TEMPERATURE)),q=L?["target_temp_low","target_temp_high"]:k(he.TARGET_TEMPERATURE)?["temperature"]:[],z=Object.fromEntries(q.map(C=>[C,Re(l,C)])),K=h!==null&&f!==null&&h0&&K&&u<=f-h&&Number.isSafeInteger(Math.floor((f-h)/u)),Z=q.length>0&&q.every(C=>z[C]!==null&&z[C]>=h&&z[C]<=f)&&(!L||z.target_temp_low<=z.target_temp_high),R=n.entity?a?s?p.ready?"":p.reason:"The climate entity is unavailable or unknown.":"Home Assistant is disconnected.":"Select a climate entity.",Y=R;R||(R=q.length?g?K?M?Z?We(t,"set_temperature")?"":"The set_temperature service is unavailable.":"The current target is invalid or unavailable.":"Temperature step is invalid.":"Temperature limits are invalid or unavailable.":"Temperature unit is unknown.":"Temperature setting is unsupported.");let se=c&&!R,W=[["hvac_mode","HVAC mode","hvac_modes",null,"set_hvac_mode"],["preset_mode","Preset","preset_modes",he.PRESET_MODE,"set_preset_mode"],["fan_mode","Fan","fan_modes",he.FAN_MODE,"set_fan_mode"],["swing_mode","Swing","swing_modes",he.SWING_MODE,"set_swing_mode"],["swing_horizontal_mode","Horizontal swing","swing_horizontal_modes",he.SWING_HORIZONTAL_MODE,"set_swing_horizontal_mode"]].map(([C,O,U,H,N])=>{let G=$i(ie(l,U)),ge=H===null||k(H),Oe=c&&ge&&G.length>0&&We(t,N),Ve=C==="hvac_mode"?o?.state??null:ie(l,C)??null,qe=Y||(ge?G.length?We(t,N)?"":`The ${N} service is unavailable.`:"No supported options reported.":"Unsupported by this climate entity.");return{key:C,label:O,options:ge?G:[],value:Ve,service:N,enabled:Oe,status:ge?Oe?"available":"unavailable":"unsupported",reason:qe}});if(d.kind==="vtherm"){let C=ie(l,"version");typeof C=="string"&&C.trim()&&(d.version=C)}let B=Re(l,"current_temperature"),le=Tt(t,n,{entity:n.entity,attrs:l,state:o?.state??null,action:s?ie(l,"hvac_action")??null:null,provider:d,ready:c,available:s,current:B,unit:g,min:h,max:f,ranged:L,keys:q,targets:z,choices:W},r,a);return{entity:n.entity,name:n.name||ie(l,"friendly_name")||n.entity||"Thermal Intelligence",provider:d,connected:a,available:s,ready:c,readiness:p,unit:g,min:h,max:f,step:u,stepSource:_,lock:d.kind==="vtherm"?At(l,a&&s):null,keys:q,targets:z,ranged:L,current:B,choices:W,canSet:se,reason:R,attrs:l,state:o?.state??null,action:s?ie(l,"hvac_action")??null:null,activity:Si(o,d,s,a),ownership:{label:"Unknown",detail:"No authoritative ownership contract is configured; presets and temperatures do not establish ownership."},...le,details:d.kind==="vtherm"?Et(l,a&&s):[]}}function Xe(t,e,i,r){if(![t,e,i,r].every(s=>typeof s=="number"&&Number.isFinite(s))||e>=i||!Number.isFinite(i-e)||r<=0||r>i-e||!Number.isSafeInteger(Math.floor((i-e)/r)))throw new Error("Invalid temperature value, limits or step.");let n=Math.floor((i-e)/r+1e-9),o=Math.max(0,Math.min(n,Math.round((Math.max(e,Math.min(i,t))-e)/r))),l=e+o*r,a=Math.max(0,Math.min(100,Math.ceil(-Math.log10(r))+10));return Number(l.toFixed(a))}function ze(t,e,i){if(!t?.canSet||!t.keys?.includes(e))throw new Error(t?.reason||"Temperature control is unavailable.");let r=Xe(i,t.min,t.max,t.step),n={...t.targets,[e]:r};if(t.ranged&&(n.target_temp_low===null||n.target_temp_high===null||n.target_temp_low>n.target_temp_high))throw new Error("The low target must not exceed the high target.");return n}var Ti=/^climate\.[a-z0-9_]+$/,V=(t,e)=>t&&typeof t=="object"?Object.getOwnPropertyDescriptor(t,e)?.value:void 0,Fe=t=>typeof t=="boolean"?t:null;function fe(t){let e=V(t,"attributes"),i=V(e,"lock_manager"),r=Fe(V(V(e,"specific_states"),"is_locked")),n=V(i,"is_locked"),o=Fe(V(i,"lock_code")),l=Fe(V(i,"lock_users")),a=Fe(V(i,"lock_automations")),s=V(i,"auto_relock_sec"),d=Number.isSafeInteger(s)&&s>=0?s:null,p=V(e,"is_lock_configured")===!0,c=V(e,"is_ready")===!0,y=V(t,"state"),g=typeof y=="string"&&y.length>0&&!["unknown","unavailable"].includes(y);return Object.freeze({available:g,configured:p,ready:c,known:r!==null&&o!==null&&l!==null&&a!==null&&d!==null&&(n===void 0||n===r)&&(l||a),locked:r,codeRequired:o,lockUsers:l,lockAutomations:a,autoRelockSec:d})}function qt({hass:t,entity:e,identity:i,registryStatus:r,preview:n,attached:o}={}){let l=V(t?.states,e),a=fe(l),s=t?.services?.versatile_thermostat,d=Object.hasOwn(s??{},"lock"),p=Object.hasOwn(s??{},"unlock"),c={locked:a.locked,codeRequired:a.codeRequired,lockUsers:a.lockUsers,lockAutomations:a.lockAutomations,autoRelockSec:a.autoRelockSec},y=JSON.stringify(["vt-lock-1",typeof e=="string"?e:null,i?.entity_id??null,i?.platform??null,r??null,t?.user?.id??null,t?.user?.is_admin===!0,a.configured,a.ready,a.available,a.known,a.locked,a.codeRequired,a.lockUsers,a.lockAutomations,a.autoRelockSec,d,p]),g=(h,f="")=>Object.freeze({allowed:h,reason:f,...c,signature:y});return o!==!0?g(!1,"Card is detached."):n!==!1?g(!1,"Lock controls are disabled while previewing or editing."):t?.connected!==!0||typeof t?.user?.id!="string"||!t.user.id||!t.connection||typeof t.connection!="object"?g(!1,"An active authenticated Home Assistant connection is required."):t.user.is_admin!==!0?g(!1,"Lock control is available to Home Assistant administrators."):typeof e!="string"||!Ti.test(e)||r!=="verified"||i?.entity_id!==e||i?.platform!=="versatile_thermostat"?g(!1,"Verified Versatile Thermostat identity is required."):V(l,"entity_id")!==e||!a.available?g(!1,"The thermostat is unavailable."):a.ready?a.configured?a.known?typeof t.callService!="function"||!d||!p?g(!1,"Versatile Thermostat lock services are unavailable."):g(!0):g(!1,"Versatile Thermostat lock details are unknown or inconsistent."):g(!1,"Versatile Thermostat lock configuration is unavailable."):g(!1,"Versatile Thermostat readiness is unknown or not ready.")}var Lt=Object.freeze({set_hvac_mode:"hvac_mode",set_preset_mode:"preset_mode",set_fan_mode:"fan_mode",set_swing_mode:"swing_mode",set_swing_horizontal_mode:"swing_horizontal_mode"}),Ri=new Set(["off","heat","cool","heat_cool","auto","dry","fan_only"]),Nt=/^climate\.[a-z0-9_]+$/;function Ci(t,e){if(!e||typeof e!="object"||Array.isArray(e))throw Error("Invalid command values.");let i=Object.getPrototypeOf(e);if(i!==Object.prototype&&i!==null)throw Error("Invalid command values.");let r=Object.getOwnPropertyDescriptors(e),n=Reflect.ownKeys(r);if(n.some(l=>typeof l!="string"||!Object.hasOwn(r[l],"value")))throw Error("Command values must be plain data.");let o=Object.fromEntries(n.map(l=>[l,r[l].value]));if(t==="set_temperature"){let l=n.length===1&&n[0]==="temperature",a=n.length===2&&n.includes("target_temp_low")&&n.includes("target_temp_high");if(!l&&!a)throw Error("Choose a single target or a complete target range.");if(n.some(s=>typeof o[s]!="number"||!Number.isFinite(o[s])))throw Error("Target temperatures must be finite numbers.");if(a&&o.target_temp_low>o.target_temp_high)throw Error("Lower target exceeds upper target.")}else{let l=Lt[t];if(!l||n.length!==1||n[0]!==l)throw Error("Unsupported command fields.");let a=o[l];if(typeof a!="string"||!a.trim()||a.length>128||/[\u0000-\u001f\u007f]/.test(a))throw Error("Choose a supported non-empty value.");if(l==="hvac_mode"&&!Ri.has(a))throw Error("Unsupported HVAC mode.")}return Object.freeze(o)}function Ze({hass:t,entity:e,preview:i,contextKey:r,model:n}){return{hass:t,entity:e,preview:i!==!1,contextKey:r,connected:t?.connected===!0,user:t?.user?.id,admin:t?.user?.is_admin===!0,connection:t?.connection,socket:t?.connection?.socket,ready:n?.ready!==!1,lockReady:n?.lockReady===!0}}function et(t,e="climate"){return t.connected&&!t.preview&&(e==="lock"?t.lockReady&&t.admin:t.ready)&&typeof t.user=="string"&&t.user.length>0&&t.connection!==null&&typeof t.connection=="object"}function Qe(t,e,i="climate"){return et(t,i)&&et(e,i)&&t.entity===e.entity&&t.user===e.user&&t.connection===e.connection&&t.socket===e.socket&&t.contextKey===e.contextKey}function Dt(t,e,i="climate"){return typeof t?.callService=="function"&&Object.hasOwn(t?.services?.[i]??{},e)}function Mt(t){return JSON.stringify([t.configured,t.codeRequired,t.lockUsers,t.lockAutomations,t.autoRelockSec])}function It(t){let e=t?.last_updated??t?.last_changed;if(typeof e!="string")return null;let i=Date.parse(e);return Number.isFinite(i)?i:null}function Ai(t,e,i,r){if(!t||["unknown","unavailable"].includes(t.state))return!1;if(r==="lock"){let n=fe(t);return n.available&&n.ready&&n.configured&&n.known&&n.locked===i.locked}return Object.entries(i).every(([n,o])=>{let l=e==="set_hvac_mode"?t.state:t.attributes?.[n];return typeof o=="number"?typeof l=="number"&&Number.isFinite(l)&&Math.abs(l-o)<1e-6:l===o})}function me(t,e,i){return Object.freeze({phase:t,message:e,...i?{target:i.context.entity,service:i.service,values:i.values,...i.kind==="lock"?{domain:i.domain}:{}}:{}})}var Pe=class{constructor({onChange:e=()=>{},timeoutMs:i=1e4,setTimer:r=(o,l)=>globalThis.setTimeout(o,l),clearTimer:n=o=>globalThis.clearTimeout(o)}={}){if(!Number.isFinite(i)||i<=0||i>6e4)throw RangeError("Invalid request timeout.");if(typeof r!="function"||typeof n!="function")throw TypeError("Invalid timer functions.");this._onChange=typeof e=="function"?e:()=>{},this._timeoutMs=i,this._setTimer=r,this._clearTimer=n,this._context=null,this._job=null,this._flight=null,this._disposed=!1,this._status=me("idle","")}get status(){return this._status}get busy(){return!this._disposed&&(this._job!==null||this._flight!==null)}_notify(e=this._status){if(!this._disposed)try{this._onChange(e)}catch{}}_publish(e,i,r){let n=me(e,i,r);return this._status=n,this._notify(n),n}_clearJobTimer(e){if(e.timer!==null){try{this._clearTimer(e.timer)}catch{}e.timer=null}}_finish(e,i,r){if(this._job!==e||e.finished)return;e.finished=!0,this._clearJobTimer(e),this._job=null;let n=me(i,r,e);this._status=n,e.resolve(n),this._notify(n)}_retire(e,i){this._finish(e,e.dispatched?"uncertain":"failed",e.dispatched?`${i} The request will not be repeated.`:"The request was cancelled before it was sent.")}_contextStillMatches(e){let i=this._context&&Qe(e.context,this._context,e.kind)&&Qe(e.context,Ze({hass:e.context.hass,entity:e.context.entity,preview:!1,contextKey:e.context.contextKey,model:{ready:e.context.ready,lockReady:e.context.lockReady}}),e.kind);if(!i||e.kind!=="lock")return i;let r=this._context.hass?.states?.[e.context.entity],n=fe(r);return r?.entity_id===e.context.entity&&n.available&&n.ready&&n.configured&&n.known&&Mt(n)===e.lockSettings}_validation(e){if(!this._contextStillMatches(e))return{allowed:!1,reason:"Control context changed."};if(!Dt(this._context.hass,e.service,e.domain))return{allowed:!1,reason:"The selected service is unavailable."};if(e.kind==="lock"&&!e.dispatched&&fe(this._context.hass?.states?.[e.context.entity]).locked!==e.baselineLocked)return{allowed:!1,reason:"The lock state changed. Review the current state before trying again."};try{let i=e.validate({entity:e.context.entity,service:e.service,values:e.values});return i&&typeof i.then=="function"?(Promise.resolve(i).catch(()=>{}),{allowed:!1,reason:"A synchronous capability check is required."}):i?.allowed===!0?{allowed:!0}:{allowed:!1,reason:(e.kind!=="lock"&&typeof i?.reason=="string"?i.reason.replace(/[\u0000-\u001f\u007f]/g," ").trim().slice(0,240):"")||"Controls are not available."}}catch{return{allowed:!1,reason:"Control validation could not complete."}}}updateContext({hass:e,entity:i,preview:r,contextKey:n,model:o}={}){return this._disposed?this.status:(this._context=Ze({hass:e,entity:i,preview:r,contextKey:n,model:o}),this._job&&!this._contextStillMatches(this._job)&&this._retire(this._job,"Control context changed; the outcome is uncertain."),this.status)}request({hass:e,entity:i,service:r,values:n,preview:o,validate:l,contextKey:a}={}){if(this._disposed)return Promise.resolve(me("failed","This card is no longer active."));if(this.busy)return Promise.resolve(me("failed","A previous request is still unresolved."));let s=c=>Promise.resolve(this._publish("failed",c));if(o!==!1)return s("Controls are disabled during setup and preview.");if(typeof i!="string"||!Nt.test(i))return s("Select a climate entity before controlling it.");if(r!=="set_temperature"&&!Object.hasOwn(Lt,r))return s("Unsupported climate service.");if(typeof l!="function")return s("A current capability check is required.");let d;try{d=Ci(r,n)}catch(c){return s(c.message)}let p=this._prepareRequest({hass:e,entity:i,service:r,values:d,preview:o,validate:l,contextKey:a,kind:"climate",domain:"climate"});return p.job&&this._dispatch(p.job,{...d,entity_id:p.job.context.entity}),p.result}requestLock({hass:e,entity:i,locked:r,code:n,preview:o,validate:l,contextKey:a}={}){try{if(this._disposed)return Promise.resolve(me("failed","This card is no longer active."));if(this.busy)return Promise.resolve(me("failed","A previous request is still unresolved."));if(o!==!1)return this._rejectRequest("Controls are disabled during setup and preview.");if(typeof i!="string"||!Nt.test(i))return this._rejectRequest("Select a climate entity before controlling it.");if(typeof r!="boolean")return this._rejectRequest("Choose an explicit lock state.");if(typeof l!="function")return this._rejectRequest("A current capability check is required.");let s=fe(e?.states?.[i]);if(!s.available||!s.ready||!s.configured||!s.known)return this._rejectRequest("Versatile Thermostat lock details are unavailable.");if(s.locked!==r&&s.codeRequired&&(typeof n!="string"||n.length===0))return this._rejectRequest("Enter the configured lock code.");let d=this._prepareRequest({hass:e,entity:i,service:r?"lock":"unlock",values:Object.freeze({locked:r}),preview:o,validate:l,contextKey:a,kind:"lock",domain:"versatile_thermostat",facts:s});return d.job&&this._dispatch(d.job,s.codeRequired?{code:n,entity_id:d.job.context.entity}:{entity_id:d.job.context.entity}),d.result}finally{n=void 0}}_rejectRequest(e){return Promise.resolve(this._publish("failed",e))}_prepareRequest({hass:e,entity:i,service:r,values:n,preview:o,validate:l,contextKey:a,kind:s,domain:d,facts:p}){let c=_=>({result:this._rejectRequest(_)}),y=Ze({hass:e,entity:i,preview:o,contextKey:a===void 0?this._context?.contextKey:a,model:s==="lock"?{lockReady:this._context?.lockReady}:void 0});if(!et(y,s))return c(s==="lock"?"An eligible administrator lock-control context is required.":"An active authenticated Home Assistant connection is required.");if(this._context||(this._context=y),!Qe(y,this._context,s))return c("The selected control context has changed.");if(!Dt(e,r,d))return c("The selected service is unavailable.");let g=e.states?.[i],h={context:y,service:r,values:n,validate:l,kind:s,domain:d,...s==="lock"?{lockSettings:Mt(p),baselineLocked:p.locked}:{},call:e.callService.bind(e),baseline:g,lastObservation:g,lastTimestamp:It(g),accepted:!1,observedMatch:!1,dispatched:!1,timer:null,finished:!1,resolve:null},f=new Promise(_=>{h.resolve=_});this._job=h;let x=this._validation(h);if(this._job!==h)return{result:f};if(!x.allowed)return this._finish(h,"failed",x.reason),{result:f};if(s==="lock"&&p.locked===n.locked)return!this._contextStillMatches(h)||fe(this._context.hass?.states?.[i]).locked!==n.locked?this._finish(h,"failed","The lock state changed. Review the current state before trying again."):this._finish(h,"observed","The requested lock state is already reported. No command was sent."),{result:f};if(this._publish("pending","Requesting the selected setting…",h),this._job!==h)return{result:f};let u=this._validation(h);if(!u.allowed||!this._contextStillMatches(h))return this._finish(h,"failed",u.reason||"Control context changed before dispatch."),{result:f};try{h.timer=this._setTimer(()=>{let _=h.accepted?"Request accepted, but the setting was not observed before timeout. It will not be repeated.":h.observedMatch?"A matching setting was observed, but the service response is missing. The request will not be repeated.":"The request outcome is uncertain. It will not be repeated.";this._finish(h,"uncertain",_)},this._timeoutMs)}catch{return this._finish(h,"failed","The request could not be tracked safely."),{result:f}}return this._job!==h?{result:f}:{result:f,job:h}}_dispatch(e,i){this._flight=e,e.dispatched=!0;try{let r=e.call(e.domain,e.service,i);i=void 0,this._trackTransport(e,r)}catch{this._transportSettled(e,!1)}finally{i=void 0}}_trackTransport(e,i){Promise.resolve(i).then(()=>this._transportSettled(e,!0),()=>this._transportSettled(e,!1))}_observedMessage(e){return e.kind==="lock"?"Requested lock state observed. Versatile Thermostat owns any automatic relocking.":"Setting observed. This does not establish physical heating."}_transportSettled(e,i){if(this._flight===e&&(this._flight=null),!this._disposed){if(this._job!==e){this._notify();return}if(!this._contextStillMatches(e)){this._retire(e,"Control context changed; the outcome is uncertain.");return}if(!i){this._finish(e,"failed","The service call failed; its effect is unknown. Check the current setting.");return}e.accepted=!0,e.observedMatch?this._finish(e,"observed",this._observedMessage(e)):this._publish("accepted","Request accepted; waiting for the setting to be observed.",e)}}observe(e){let i=this._job;if(this._disposed||!i?.dispatched||e?.entity_id!==i.context.entity)return this.status;if(!this._contextStillMatches(i))return this._retire(i,"Control context changed; the outcome is uncertain."),this.status;if(e===i.lastObservation)return this.status;let r=It(e);return i.lastTimestamp!==null&&(r===null||r<=i.lastTimestamp)?this.status:(i.lastObservation=e,r!==null&&(i.lastTimestamp=r),i.observedMatch=Ai(e,i.service,i.values,i.kind),i.accepted&&i.observedMatch?this._finish(i,"observed",this._observedMessage(i)):i.accepted||this._publish("pending",i.observedMatch?"A matching setting was observed; waiting for the service response.":"Requesting the selected setting…",i),this.status)}cancel(e="Tracking stopped; the request outcome is uncertain."){return this._job&&this._retire(this._job,e),this.status}dispose(){this._disposed||(this._disposed=!0,this._job&&this._retire(this._job,"The card was detached; the request outcome is uncertain."),this._onChange=()=>{},this._context=null)}};var Ei=".expanded-health>[data-device]{position:relative;left:-14px;display:grid;place-items:center;background:transparent;border:0;width:44px;height:44px;padding:0}.icon-controls{display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr))!important;padding:12px 0!important;gap:6px!important}.rows.icon-controls>button{min-width:0;white-space:normal;overflow-wrap:normal;padding:8px 4px;font-size:10px;line-height:1.35}.icon-controls ha-icon{display:block;margin:0 auto 6px;--mdc-icon-size:16px;width:16px;height:16px}.expanded-health{display:flex;justify-content:flex-start;margin-top:12px}.trend-halo{animation:halo-breathe 4s ease-in-out infinite}@keyframes halo-breathe{0%,100%{filter:var(--halo-low)}50%{filter:var(--halo-high)}}@media(prefers-reduced-motion:reduce){.trend-halo{animation:none}}:host{display:block;color:#eff5ff;font-family:var(--primary-font-family,system-ui)}*{box-sizing:border-box}ha-card{display:block;background:radial-gradient(ellipse at 50% 20%,#425d8821,transparent 70%),#0b111b;border:1px solid #293344;border-radius:24px;padding:18px 17px 14px;color:#eff5ff}ha-card.plum{background:radial-gradient(ellipse at 50% 20%,#79559424,transparent 70%),#140e1c}ha-card.black{background:radial-gradient(ellipse at 50% 20%,#ffffff06,transparent 70%),#050607}header{position:relative;display:flex;align-items:center;justify-content:space-between;font-size:13px}header strong{font-weight:650;min-width:0;overflow-wrap:break-word}.heading{display:flex;align-items:center;gap:6px;min-width:0;width:100%;min-height:44px}.heading>strong{flex:0 1 45%;max-width:45%;min-width:0;line-height:1.4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.heading.no-telemetry>strong{flex-basis:100%;max-width:100%}.header-telemetry{display:flex;flex:1;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:thin;gap:4px;white-space:nowrap}.header-telemetry[hidden]{display:none}.header-telemetry:focus{outline:none}.header-telemetry .telemetry-item{display:inline-flex;align-items:center;gap:4px;flex-shrink:0;min-width:44px;justify-content:center;position:relative;min-height:44px;padding:6px 4px;background:transparent;border:0;border-radius:8px;font-size:11px;color:#c4d0df}.telemetry-item ha-icon{color:var(--telemetry-colour,#a9b8cb)}.telemetry-item[data-activity=unknown] ha-icon{color:#8795a8}.telemetry-item ha-icon{width:14px;height:14px;--mdc-icon-size:14px;flex-shrink:0}button,select{font:inherit;color:inherit;background:#141e2b;border:1px solid #ffffff18;border-radius:12px;min-height:44px;padding:8px;cursor:pointer}button:focus-visible,select:focus-visible{outline:2px solid #8ac9f1;outline-offset:3px}button:disabled,select:disabled{opacity:.4;cursor:default}.condition{font-size:11px;color:#a9b8cb;margin-top:12px}.control-footer>[data-device]{position:absolute;left:-14px;top:0;display:grid;place-items:center;background:transparent;border:0;width:44px;height:44px;padding:0}.dial{position:relative;width:calc(100% + 18px);max-width:260px;left:50%;transform:translateX(-50%);margin:2px 0 4px}svg{width:100%;display:block;overflow:visible}.dial>svg{display:inline}.reading{font-size:35px;font-weight:650;letter-spacing:-1.5px;fill:#f4f8ff}.unit{font-size:11px;letter-spacing:2px;fill:#8e9db1}.limit{font-size:8px;fill:#a9b8cb}.diamond{fill:var(--accent);opacity:.44;animation:glint 19s ease-in-out infinite;transform:translate(var(--tick-x,0px),var(--tick-y,0px));transition:transform 120ms ease-out}.adjusting .diamond{animation:none}.diamond.selected{fill:white;opacity:1;filter:drop-shadow(0 0 1px white)}@keyframes glint{0%,61%,79%,100%{opacity:var(--base,.3)}69%{opacity:.85}73%{opacity:var(--base,.3)}}@media(prefers-reduced-motion:reduce){.diamond{animation:none;transition:none;transform:none}}[data-marker-visual]{pointer-events:none}[data-marker-visual].keyboard-focus .target-dot{filter:drop-shadow(0 0 2px #8ac9f1)}[data-recovering=true] .status-led{animation:recovery-pulse 2s ease-in-out infinite}@keyframes recovery-pulse{50%{opacity:.35}}@media(prefers-reduced-motion:reduce){[data-recovering=true] .status-led{animation:none}}.status-led{display:block;width:6px;height:6px;border-radius:50%;background:var(--health-color);box-shadow:0 0 8px color-mix(in srgb,var(--health-color) 60%,transparent)}.marker-controls{position:absolute;left:0;top:0;width:100%;aspect-ratio:180 / 184;pointer-events:none}.marker-controls>[data-marker]{position:absolute;transform:translate(-50%,-50%);width:44px;height:44px;min-height:44px;padding:0;border:0;border-radius:50%;background:transparent;pointer-events:auto;touch-action:none;cursor:grab}.marker-controls>[data-marker]:focus-visible{outline:none}.marker-controls>[data-marker]:disabled{pointer-events:none}.marker-controls>[data-marker][data-active=true]{cursor:grabbing}@media(pointer:coarse){.marker-controls>[data-marker]{width:clamp(44px,36%,56px);height:auto;aspect-ratio:1}}.centre{position:absolute;left:50%;top:54.348%;transform:translate(-50%,-50%);width:35.556%;height:34.783%;min-width:44px;min-height:44px;border:0;border-radius:50%;background:transparent}.summary{text-align:center}.trend{color:#a9b8cb;font-size:12px;font-weight:600;margin:0 0 15px}.status{display:inline-flex;align-items:center;justify-content:center;gap:5px;max-width:100%;font-size:11px;line-height:1.4;font-weight:600;color:#c4d0df;background:#ffffff06;border:1px solid #ffffff18;padding:5px 8px;border-radius:20px}.status[data-output=heat],.status[data-output=open]{color:#ffad77;border-color:#ffad7740}.status[data-output=request]{color:#efb86b;border-color:#efb86b40}.status[data-output=cool]{color:#76d3ff}.status[data-output=unknown]{color:#a9b8cb}.status ha-icon{width:15px;height:15px;--mdc-icon-size:15px;flex-shrink:0}.summary-space{height:12px}.targets{margin-top:0;min-height:44px}.target>div{display:flex;align-items:center;justify-content:space-between;gap:8px}.target button{flex:1;width:auto;height:44px;min-height:44px;padding:7px;border-radius:999px;background:#ffffff08;font-size:22px;line-height:28px}.target strong{min-width:44px;text-align:center}.target strong{font-size:16px}.target strong small{font-size:9px;line-height:12px;margin:0;color:#b9cddd}.target strong small[hidden]{display:none}.target>small{display:block;text-align:center;margin:4px 0 7px;font-size:11px;color:#a9b8cb}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.control-footer{position:relative;display:grid;place-items:center;height:44px;margin-top:0}.range-targets{display:grid;grid-template-columns:1fr 1fr;gap:10px}.range-targets .target{position:relative;height:44px;border:1px solid #ffffff12;border-radius:16px;padding:0 4px;background:#ffffff03}.range-targets .lower{border-top-color:#ffad6650}.range-targets .upper{border-top-color:#66cfff50}.range-targets .target>small{position:absolute;bottom:calc(100% + 5px);left:0;right:0;font-size:10px;letter-spacing:.6px;margin:0}.range-targets .lower>small{color:#ffbd87}.range-targets .upper>small{color:#88d8ff}.range-targets .target>div{display:grid;grid-template-columns:32px minmax(0,1fr) 32px;gap:0}.range-targets .target button{flex:none;width:32px;padding:0;min-height:44px;border:0;background:transparent;font-size:20px}.range-targets .target strong{font-size:17px;text-align:center;font-variant-numeric:tabular-nums}.range-targets [data-value]{overflow-wrap:anywhere}.disclosure{align-items:center;width:44px;height:44px;min-height:44px;padding:0;border:0;display:grid;place-items:center;background:transparent;border-radius:50%}.rows{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(120px,100%),1fr));gap:8px;padding:12px 8px}.rows label{font-size:11px;color:#a9b8cb}.choice-field{position:relative;display:block;margin-top:5px}.rows select{appearance:none;-webkit-appearance:none;width:100%;margin:0;padding-right:46px}.choice-chevron{position:absolute;right:8px;top:50%;transform:translateY(-50%);display:grid;place-items:center;width:28px;height:28px;border:1px solid #273448;border-radius:50%;pointer-events:none;color:#eff5ff}.choice-chevron svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.choice-field select:disabled+.choice-chevron{opacity:.4}.presets{grid-template-columns:repeat(auto-fit,minmax(min(50px,100%),1fr))}.presets button{font-size:10px;padding:6px 4px}.disclosure span{display:grid;place-items:center;width:32px;height:32px;background:#101824;border:1px solid #273448;border-radius:50%;font-size:18px}.disclosure span svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.disclosure[aria-expanded=true] span svg{transform:rotate(180deg)}.disclosure[aria-expanded=true] span{color:#62e3b0;border-color:#62e3b080}.presets .preset-temperature{font-size:12px;font-weight:650;color:inherit;white-space:nowrap;margin-top:4px}.preset-hint{margin:0 8px 8px;color:#8795a8}.presets button[aria-pressed=true]{border-color:var(--accent);background:#263446}.information{border-top:1px solid #ffffff12;font-size:11px}.information>button{min-width:0;overflow-wrap:anywhere}.information>div{background:#141e2b;border-radius:10px;padding:10px;overflow-wrap:anywhere}small{display:block;color:#a9b8cb;margin-top:5px}.hint{font-size:10px;color:#8795a8;padding:0 8px}section[hidden]{display:none}",Oi=` button,select,input,summary{transition:background-color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,color 150ms ease} button:focus-visible,select:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid #a3d7f5;outline-offset:2px} @media(hover:hover){button:enabled:hover,summary:hover{border-color:#7895b65c;box-shadow:inset 0 0 0 1px #c0dcff0b}} @media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}} `,qi=`${Oi} :host{align-self:start;justify-self:stretch;min-width:0} ha-card{border-color:#607b9b42;box-shadow:inset 0 1px 0 #d5e7ff09,0 8px 24px #00000016} .heading>strong{flex:0 0 auto;max-width:45%} .header-telemetry{flex:0 1 auto;margin-left:auto;gap:2px;scroll-padding-inline:4px} .header-telemetry .telemetry-item{border-radius:12px} .header-telemetry .telemetry-item:focus-visible{outline-offset:-3px;box-shadow:none} .header-telemetry .ownership-indicator{width:44px;flex:0 0 44px;padding:0} .ownership-indicator ha-icon{width:14px;height:14px;--mdc-icon-size:14px} .ownership-indicator[data-source=Schedule],.ownership-indicator[data-source=Manual]{background:radial-gradient(circle at center,color-mix(in srgb,var(--telemetry-colour) 13%,transparent),transparent 72%)} .ownership-indicator[data-source=Schedule] ha-icon,.ownership-indicator[data-source=Manual] ha-icon{filter:drop-shadow(0 0 4px color-mix(in srgb,var(--telemetry-colour) 45%,transparent))} .source-summary{margin:12px 8px 0;font-size:11px;line-height:1.5;color:#a9b8cb} .source-summary strong{color:#dcebf7;font-weight:600} .source-summary:focus-visible{outline:2px solid #a3d7f5;outline-offset:3px;border-radius:4px} .ownership-controls{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:10px 8px} .ownership-controls button{display:flex;align-items:center;justify-content:center;gap:8px;font-size:11px} .ownership-controls ha-icon{width:17px;height:17px;--mdc-icon-size:17px} .ownership-controls button[aria-pressed=true]{background:#233c45;border-color:#88c6bd80;box-shadow:inset 0 1px 0 #d8fff50a;color:#d8f4ec} .ownership-controls button[data-owner=Manual][aria-pressed=true]{background:#2e2b40;border-color:#baafe080;color:#e9e2ff} .presets button[aria-pressed=true]{box-shadow:inset 0 1px 0 #ffffff0b} .rows.information>button,.rows.icon-controls>button{background:#142032;border-color:#5a749638} .hint:empty{display:none} .target strong{font-variant-numeric:tabular-nums} .disclosure span{box-shadow:inset 0 1px 0 #ffffff05} `,Ni=` .lock-indicator{display:inline-flex;align-items:center;justify-content:center;flex:0 0 14px;width:14px;height:14px;line-height:0;color:#a9b8cb} .lock-indicator>ha-icon{--mdc-icon-size:14px;width:14px;height:14px} .lock-indicator.lock-action{flex:0 0 44px;width:44px;height:44px;min-height:44px;padding:0;background:transparent;border:0;border-radius:8px} .heading:has(.lock-action).no-telemetry>strong{flex:0 1 auto;max-width:calc(100% - 50px)} .lock-indicator>ha-icon,.header-telemetry .telemetry-item>ha-icon,.status>ha-icon{display:inline-flex;align-items:center;justify-content:center;line-height:0;flex-shrink:0} .header-telemetry .telemetry-item>span{line-height:14px} .lock-indicator[data-lock-state=locked]{color:#efb86b} .lock-indicator[data-lock-state=unknown],.lock-indicator[data-lock-state=unavailable]{color:#8795a8} `,zt=Ei+qi+Ni;var w=t=>String(t??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]),tt=(t,e)=>[100+t*Math.cos(e*Math.PI/180),100+t*Math.sin(e*Math.PI/180)],_e=t=>Number.isFinite(t)?new Intl.NumberFormat(void 0,{maximumSignificantDigits:12}).format(t):"—",it=(t,e="#94a3b8")=>/^#[0-9a-f]{6}$/i.test(t??"")?t:/^#[0-9a-f]{6}$/i.test(e)?e:"#94a3b8",ve=t=>typeof t=="number"&&Number.isFinite(t)?t:typeof t=="string"&&/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t.trim())&&Number.isFinite(Number(t))?Number(t):null,Di=new Set(["temperature","target_temp_low","target_temp_high"]);function Mi(t){return t.topic==="window"&&t.attribute&&t.attribute!=="window_manager.window_state"&&t.attribute!=="window_manager.window_auto_state"?t.activity==="active"?"#b9a1e6":"#a9b8cb":t.activity==="unknown"?"#8795a8":t.activity!=="active"?"#a9b8cb":{eco:"#7dc39c",comfort:"#e3b778",boost:"#e78e76",frost:"#9bcbe9"}[t.preset]||{window:"#efb86b",power_management:"#efb86b",motion:"#b9a1e6",presence:"#7dc39c",follow:"#73c9bd",energy:"#c6a2eb",humidity:"#73c9bd",temperature:"#8abfda"}[t.topic]||"#cad6e3"}function Ft({config:t={},model:e={},open:i=!1,allowed:r=!1,lockAllowed:n=!1,busy:o=!1,draftValues:l=null,statusMessage:a="",preview:s=!1,expanderId:d="thermal-features",animationOffset:p=0}={}){let c={...e,attrs:e.attrs??{},keys:(e.keys??[]).filter(m=>Di.has(m)),targets:e.targets??{},choices:e.choices??[],current:e.available?ve(e.current):null,min:ve(e.min),max:ve(e.max),unit:e.unit??""};c.bounds=c.min!==null&&c.max!==null&&c.min[m,Number.isFinite(l?.[m])?l[m]:c.targets[m]])),h=r&&!o&&!s&&c.canSet===!0,f={config:t,open:i===!0,expanderId:/^[a-zA-Z][\w-]*$/.test(d)?d:"thermal-features",message:String(a??""),targetEditable:()=>h},x=c.state??c.choices.find(m=>m.key==="hvac_mode")?.value;c.state=x;let u=c.current!==null&&Number.isFinite(c.targets.temperature)?c.current-c.targets.temperature:null,_=t.entity?c.available?!c.ready&&c.readiness?.status!=="locked"?"Thermostat not ready":c.current===null?"Temperature unavailable":x==="off"?"Switched off":c.ranged?"Temperature range":u===null?"Target unavailable":Math.abs(u)<=.2?"Near target":`${Math.abs(u).toFixed(1)}° ${u>0?"above":"below"} target`:"Unavailable":"Select a climate entity in the card editor",b={tone:"unknown",text:c.activity?.label||"Activity unknown",icon:null,kind:"thermostat",...c.output??{}},$={brief:c.output?.brief||"",title:c.reason||_},k=null,L=it(c.arc?.color),q=Array.isArray(c.headerTelemetry)?c.headerTelemetry:[],z=null,K="",M=c.lock,Z=M?n&&!s?``:``:"",R=q.map(m=>{let S=`${m.label??"Telemetry"}: ${m.value??"Unavailable"}${m.activityLabel?` · ${m.activityLabel}`:""}`,I=["frost","eco","comfort","boost"].includes(m.preset)?'data-presets aria-haspopup="dialog"':m.topic?`data-telemetry-topic="${w(m.topic)}" aria-haspopup="dialog"`:`data-info="${w(m.entity)}"`;return``}).join(""),Y="",se=c.bounds&&c.current!==null?Math.max(0,Math.min(1,(c.current-c.min)/(c.max-c.min))):0,j=2*Math.PI*72,W=c.trend??{available:!1,value:null,color:"#8795a8",text:"Trend unavailable"},B=W.available===!0&&Number.isFinite(W.value),le=B?W.value:0,C=it(W.color,"#8795a8"),O=B?Math.min(1,Math.abs(le)/1.375):0,U=Math.round(90+90*O).toString(16).padStart(2,"0"),H=Math.min(28,6+Math.abs(le)*16),N=`drop-shadow(0 0 ${H*(.8-.25*O)}px ${C}${U})`,G=`drop-shadow(0 0 ${H}px ${C}${U})`,ge=B&&c.current!==null?`drop-shadow(0 0 ${Math.min(28,6+Math.abs(le)*16)}px ${C}${U})`:"none",Oe=W.text||"Trend unavailable",Ve=Array.from({length:21},(m,S)=>{let[I,de]=tt(85,135+S*13.5),Q=S%5===0?1.35:.75;return``}).join(""),qe=c.available&&c.bounds?c.keys.filter(m=>Number.isFinite(g[m])&&g[m]>=c.min&&g[m]<=c.max):[],Qt=qe.map(m=>{let[S,I]=tt(72,135+270*(g[m]-c.min)/(c.max-c.min));return``}).join(""),ei=qe.map(m=>{let[S,I]=tt(72,135+270*(g[m]-c.min)/(c.max-c.min));return``}).join(""),Ne=c.choices.find(m=>m.key==="preset_mode"&&m.status!=="unsupported"),we=c.provider?.kind==="vtherm",ti=m=>{if(!we||!["frost","eco","comfort","boost"].includes(m))return"";let S=c.presetReadings?.[m],I=S?.status==="available"?S.unit:["°C","°F"].includes(y.configuration?.temperature_unit)?y.configuration.temperature_unit:"",de=S&&S.status!=="disabled"?S.status==="available"?S.value:null:c.available&&c.connected&&I?ve(y.preset_temperatures?.[`${m}_temp`]):null;return`${w(_e(de)+I)}`},ii=Ne?`
${Ne.options.map(m=>``).join("")}
${we?'

Change preset temperatures in settings.

':""}`:"",oi="",ni=c.choices.filter(m=>m.key!=="preset_mode"&&m.status!=="unsupported").map(m=>``).join(""),dt=l?"Draft":o?"Pending":"",ai={text:String(a||(c.canSet?"Target reported by thermostat.":c.reason||"Thermostat target is unavailable."))},ct=c.details?.some(m=>["unknown","unavailable","invalid","stale"].includes(m.status)),ri=o||!c.ready||!c.canSet||ct?"amber":"green",De={level:c.connected===!1||!c.available?"red":ri};De.color={green:"#62e3b0",amber:"#efb86b",red:"#ef7777"}[De.level];let ut=t.show_details===!1||!we?[]:[["Readiness",c.attrs.is_ready===!0?"Ready":c.attrs.is_ready===!1?"Not initialised":"Unknown"],...[["Window","is_window_configured","Window"],["Presence","is_presence_configured","Presence"],["Motion","is_motion_configured","Motion"],["Power","is_power_configured","Power limiting"],["Safety","is_safety_configured","Safety"]].filter(([,m])=>c.attrs[m]===!0).map(([m,,S])=>[m,c.details?.find(I=>I.label===S)?.value??"Unknown"])],si='',pt=c.connected&&c.available?c.attrs.vtherm_over_climate?.follow_underlying_temp_change:null,li=`
${we?``:""}${si}
`,di=we&&(Object.values(c.presetReadings??{}).some(m=>m.status!=="disabled")||Object.keys(y.preset_temperatures??{}).length)?'
':"",ci=q.filter(m=>!m.preset&&(m.feature||(t.telemetry??[]).some(S=>S.entity===m.entity&&(S.attribute||null)===(m.attribute||null)))),ht=t.show_details===!1?"":ci.map(m=>``).join(""),ft=``;return`
${w(f.config.name||y.friendly_name||f.config.entity)}${Z}
${K}${R}
${s?'

Preview — thermostat controls are disabled.

':""}
${Ve}${_e(c.current)}${w(c.unit)}${_e(c.min)}${_e(c.max)}${Qt}
${ei}
${w(Oe)}
${w(b.kind==="trv"&&b.percentage===null?"":$.brief||(!c.ready&&c.readiness?.status!=="locked"?$.title:_))}
${c.keys.map(m=>`
${c.ranged?`${m==="target_temp_low"?"Lower":"Upper"}`:""}
${w(_e(g[m])+c.unit)}${w(dt)}
`).join("")}
${a?`

${w(a)}

`:""}
${oi}${ii}${di}${Y}
${ni}
${ut.length?`
${ut.map(([m,S])=>`
${w(m)}${w(c.available?S:"Unavailable")}
`).join("")}
`:""}${li}${ht?`
${ht}
`:""}${Number.isInteger(y.supported_features)&&y.supported_features&4?'':""}

Device configuration and advanced settings remain in thermostat details.

${f.open?`
${ft}
`:""}
`}var ot={dry_below:"Too dry below",good_min:"Good range starts",good_max:"Good range ends",wet_above:"Too wet above",low:"Legacy lower boundary",high:"Legacy upper boundary"},P=t=>String(t??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]),Ce=t=>Number.isFinite(t)?new Intl.NumberFormat(void 0,{maximumSignificantDigits:12}).format(t):"Unavailable",nt=(t,e)=>t!==null&&typeof t=="object"&&Object.hasOwn(t,e),at=t=>typeof t=="number"&&Number.isFinite(t)?String(t):"",Ii=t=>typeof t=="string"&&/^[a-z0-9_]+\.[a-z0-9_]+$/.test(t)?t:"";function Pt({model:t={},config:e={},row:i,statusMessage:r=""}={}){let n=i??t.humidity??t.headerTelemetry?.find(R=>R.kind==="humidity"||R.topic==="humidity"),o=n?.source??{},l=n?.rawValue??n?.value,a=n?.status==="available"&&n?.unit==="%"&&t.connected!==!1&&typeof l=="number"&&Number.isFinite(l)&&l>=0&&l<=100?l:null,s=a===null?"Unavailable":`${Ce(a)} %`,d=e.humidity_bands,p=d!==void 0&&(nt(d,"low")||nt(d,"high"))&&!ee.some(R=>nt(d,R)),c=ce(e),y=c!==null&&(p?X(c):oe(c)),g=c??(d&&typeof d=="object"&&!Array.isArray(d)?d:{}),h=p?["low","high"]:ee,f=ke(a,c),x=bt(c),u=Ii(o.entity||n?.entity),b=`
${[["Selected reading (raw)",s],["Source",o.name||o.entity||n?.entity||"Not configured"],...o.attribute?[["Source attribute",o.attribute]]:[],["Source last reported",o.timestamp||"Unavailable"],...n?.status==="available"&&a!==null?[]:[["Reading status",a===null&&n?.status==="available"?"Unavailable":n?.status||"Not configured"]]].map(([R,Y])=>`
${P(R)}
${P(Y)}
`).join("")}
${u?``:""}`,$=p?"":h.map(R=>``).join(""),k=h.map(R=>``).join(""),L=y?p?`
Below ${P(Ce(g.low))}%Within ${P(Ce(g.low))}–${P(Ce(g.high))}%Above ${P(Ce(g.high))}%
`:`
Dry <${g.dry_below}%Good ${g.good_min}–${g.good_max}%Wet >${g.wet_above}%
`:"",q=`${P(s)} · ${P(f.label)}${L}`,z=p?"This card has a legacy two-boundary colour preference. Its original lower and upper values are retained; they have not been converted into four boundaries.":"Slide the four markers, or enter exact percentages. Amber fills the gaps around the good range.",K=y?"Draft only — nothing has been saved.":p?"Keep two increasing percentages between 0 and 100.":"Keep four increasing whole percentages between 0 and 100.",M=y?"background:#24483f;border-color:#79bfa15e;color:#e1fff2":"background:#274b44";return{title:"Humidity",html:`
${P(e.name||t.name||"Room")} · display

Humidity

${b}

${p?"Legacy colour preference":"Colour boundaries"}

${z}

${$}
0%100%
${k}
${q}

These colours are visual guides only. They do not control heating, humidifiers or dehumidifiers. Draft only; copy into this card’s Home Assistant editor to save.

${P(r||K)}

`}}var Ae=`.safe-retry{border-top:1px solid #293344;padding:0 14px 14px}.safe-retry .note{padding:10px 0 0}button:disabled{opacity:.5;cursor:default}dialog[data-view=telemetry]{width:min(410px,calc(100vw - 24px));border-radius:20px}dialog[data-view=telemetry] h2{font-size:18px}dialog[data-view=telemetry] dl{padding:0 0 12px}dialog[data-view=telemetry] .note{padding:0}.topic-links{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.topic-links button{font-size:12px}dialog[data-view=presets]{width:min(380px,calc(100vw - 24px));border-radius:20px}dialog[data-view=presets] header{padding:14px 16px 10px;align-items:center}dialog[data-view=presets] h2{font-size:17px}dialog[data-view=presets] .eyebrow{font-size:10px;letter-spacing:.5px;margin-bottom:4px}dialog[data-view=presets] .content{padding:12px 16px 14px}dialog[data-view=presets] .chart-rows{margin:4px 0 8px;gap:10px}dialog[data-view=presets] .chart-hint{font-size:11px;color:#91a4ba;margin:14px 0 0}dialog[data-view=presets] .chart-timetable{margin:4px 0 0;padding:8px 0;border:0;background:none;font-size:11px;color:#b9d9f1;display:flex;align-items:center;justify-content:center;gap:5px}.chart-timetable ha-icon{--mdc-icon-size:15px;width:15px;height:15px}footer[hidden]{display:none}.chart-intro{margin:0}.chart-rows{display:grid;gap:18px;margin:22px 0 8px}.chart-row{display:grid;grid-template-columns:64px minmax(40px,1fr) 90px;gap:10px;align-items:center}.chart-label{font-size:12px}.chart-label span{display:block;font-size:10px;color:#76d3ff;min-height:13px;margin-top:4px}.bar-track{height:24px;border-radius:6px;background:#ffffff08;position:relative;overflow:hidden}.bar-track:after{content:"";position:absolute;left:var(--zero);height:100%;border-left:1px solid #ffffff55}.bar{position:absolute;height:100%;border-radius:5px;background:#7dc39c}.bar-frost{background:#9bcbe9}.bar-comfort{background:#e3b778}.bar-boost{background:#e78e76}.chart-row[data-selected=true] .bar-track{outline:1px solid #a6dfff;outline-offset:3px}.chart-row button{font-size:12px;padding:5px;min-width:0}.chart-scale{display:flex;justify-content:space-between;margin-left:74px;margin-right:100px;font-size:10px;color:#91a4ba}.chart-timetable{display:block;width:100%;margin-top:18px}.chart-notice{color:#efb86b}:host{font-family:var(--primary-font-family,system-ui);color:#eff5ff}*{box-sizing:border-box}dialog{--accent:#62e3b0;color:#eff5ff;border:1px solid #344155;border-radius:24px;padding:0;background:radial-gradient(ellipse at 50% 0,#334c712d,transparent 65%),#0b111b;width:min(560px,calc(100vw - 24px));max-width:none;max-height:calc(100dvh - 32px);box-shadow:0 24px 90px #0009;overflow:hidden}dialog[data-tone=heat]{--accent:#ffad77}dialog[data-tone=cool]{--accent:#76d3ff}dialog[data-tone=warning]{--accent:#efb86b}dialog::backdrop{background:#02060bc2;backdrop-filter:blur(5px)}.shell{max-height:calc(100dvh - 34px);display:flex;flex-direction:column}header{padding:20px 20px 14px;display:flex;gap:12px;justify-content:space-between;align-items:start;border-bottom:1px solid #ffffff10;flex-shrink:0}.eyebrow{font-size:11px;color:#91a4ba;letter-spacing:1.2px;margin:0 0 7px}h2{font-size:20px;line-height:1.3;margin:0;font-weight:600;overflow-wrap:anywhere}button{font:inherit;color:inherit;cursor:pointer;min-height:44px;background:#141e2b;border:1px solid #ffffff18;border-radius:12px;padding:9px 12px}button:focus-visible,summary:focus-visible{outline:2px solid #8ac9f1;outline-offset:3px}.close{font-size:25px;width:44px;flex-shrink:0;padding:0;border-radius:50%}.content{overflow:auto;overscroll-behavior:contain;padding:16px 20px;min-height:0}.health{padding:14px 16px;background:#101824;border:1px solid color-mix(in srgb,var(--health-color) 35%,#293344);border-radius:16px;margin-bottom:14px}.health h3{color:var(--health-color)}.health[data-level=green]{padding:3px 1px 12px;margin-bottom:0;background:none;border:0}.health[data-level=green] h3{font-size:12px}.health .health-note{color:#899bb1;font-size:11px}.summary{padding:15px 16px;background:color-mix(in srgb,var(--accent) 5%,#101824);border:1px solid color-mix(in srgb,var(--accent) 25%,#263446);border-radius:16px;margin-bottom:14px}.state{display:flex;align-items:center;gap:9px}i{height:6px;width:6px;background:var(--accent);border-radius:50%;flex-shrink:0}h3{font-size:15px;line-height:1.35;margin:0;color:var(--accent)}p{font-size:12px;line-height:1.55;margin:9px 0 0;color:#c4d0df}.notices{border-top:1px solid #ffffff18;margin-top:12px;padding-top:2px}.notices p{color:#f1c390}.groups{display:grid;gap:8px}details{border:1px solid #293344;border-radius:13px;background:#101824;overflow:hidden}summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:48px;padding:12px 14px;cursor:pointer;font-size:12px;font-weight:600;list-style:none}summary::-webkit-details-marker{display:none}summary span{font-size:18px;color:#91a4ba}details[open] summary span{transform:rotate(180deg)}details[open] summary{border-bottom:1px solid #ffffff0b}dl{margin:0;padding:4px 14px 8px}dl>div{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px;align-items:center;min-height:38px;padding:6px 0;border-bottom:1px solid #ffffff08}dl>div:last-child{border:0}dt{color:#9dacbf;font-size:11px;overflow-wrap:anywhere}dd{margin:0;text-align:right;font-size:12px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}dt button{display:block;background:none;border:0;text-align:left;padding:4px 0;min-height:36px;font-size:11px;color:#b9d9f1;text-decoration:underline;text-decoration-color:#b9d9f144;text-underline-offset:3px}.note{font-size:11px;color:#899bb1;margin:0;padding:0 14px 13px}footer{padding:12px 20px 16px;border-top:1px solid #ffffff10;display:flex;flex-wrap:wrap;gap:8px;flex-shrink:0}footer button{font-size:11px;flex:1}footer p{flex-basis:100%;font-size:11px;color:#899bb1;margin:0}@media(max-width:420px){header{padding:16px 14px 12px}.content{padding:12px 14px}footer{padding:10px 14px 13px}h2{font-size:18px}.eyebrow{font-size:9px;letter-spacing:.8px}} button,select,input,summary{transition:background-color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,color 150ms ease} button:focus-visible,select:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid #a3d7f5;outline-offset:2px} @media(hover:hover){button:enabled:hover,summary:hover{border-color:#7895b65c;box-shadow:inset 0 0 0 1px #c0dcff0b}} @media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}} dialog{color-scheme:dark;border-color:#6380a45c;border-radius:22px;background:radial-gradient(ellipse at 35% 0,#526d9420,transparent 60%),#0d1622;box-shadow:inset 0 1px 0 #e3efff0a,0 24px 80px #0009;max-height:calc(100dvh - 24px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));overflow:hidden} dialog[open]{display:flex;flex-direction:column} dialog::backdrop{background:#030811bd;backdrop-filter:blur(4px)} dialog>.shell{display:flex;flex-direction:column;min-height:0;max-height:inherit} header,footer{flex:0 0 auto;background:#ffffff02} header{align-items:center;border-bottom:1px solid #b9d5f314} header>div{min-width:0} header h2{overflow-wrap:anywhere;line-height:1.3} header>button{flex:0 0 44px;width:44px;height:44px;min-height:44px;padding:0;border-radius:50%;display:grid;place-items:center;line-height:1} .body,.content{flex:1 1 auto;min-height:0;max-height:none;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#546a8266 transparent} footer{border-top:1px solid #b9d5f314;gap:8px} footer button{white-space:normal;overflow-wrap:anywhere} button:disabled{cursor:default} dl>div{gap:12px}dt,dd{min-width:0;overflow-wrap:anywhere} input:not([type=range]):not([type=checkbox]):not([type=radio]),select{border-color:#607d9e66;background-color:#152234} [data-save]:enabled{background:#24483f;border-color:#79bfa15e;color:#e1fff2} @media(max-height:500px){header{padding-top:10px;padding-bottom:10px}footer{padding-top:8px;padding-bottom:8px}} `,jt=`:host{font:14px var(--primary-font-family,system-ui);color:#e3edf7}*{box-sizing:border-box}dialog{color:inherit;background:linear-gradient(145deg,#152235,#0b1420);border:1px solid #34465c;border-radius:22px;padding:0;width:min(580px,calc(100vw - 24px));max-height:calc(100dvh - 24px);overflow:hidden;box-shadow:0 24px 80px #0009}dialog::backdrop{background:#02060baa}header,footer{display:flex;align-items:center;gap:12px;padding:14px 18px;background:#ffffff03}header>div{flex:1}h2{font-size:20px;margin:4px 0}h3{font-size:14px;margin:22px 0 6px}small,p,dt{color:#a6b9cc}p{font-size:12px;line-height:1.55}header button{font-size:22px}button,input{font:inherit;color:inherit;background:#19293c;border:1px solid #3c5067;border-radius:10px;min-height:44px;padding:8px}button{cursor:pointer}button:disabled,input:disabled{opacity:.45;cursor:default}button:focus-visible,input:focus-visible{outline:2px solid #8ac9f1;outline-offset:2px}.body{padding:0 18px 10px;max-height:calc(100dvh - 190px);overflow:auto;overscroll-behavior:contain}dl>div{display:flex;justify-content:space-between;gap:14px;border-bottom:1px solid #ffffff0c;padding:9px 0;font-size:12px}dd{margin:0;text-align:right;overflow-wrap:anywhere;max-width:65%}.bar{position:relative;height:52px;margin:12px 10px 0}.track{position:absolute;top:20px;height:12px;left:0;right:0;border-radius:8px;box-shadow:inset 0 1px 3px #0007}.bar input{position:absolute;inset:0;margin:0;width:100%;height:52px;padding:0;appearance:none;-webkit-appearance:none;background:transparent;border:0;pointer-events:none;touch-action:none;min-height:0}.bar input::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:22px;height:40px;border:2px solid #e8f2ff;border-radius:7px;background:#20334a;pointer-events:auto;cursor:ew-resize;box-shadow:0 2px 7px #0008}.bar input::-moz-range-thumb{width:18px;height:36px;border:2px solid #e8f2ff;border-radius:7px;background:#20334a;pointer-events:auto;cursor:ew-resize}.bar input:focus-visible{outline:none}.bar input:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 3px #8ac9f1}.bar input:focus-visible::-moz-range-thumb{box-shadow:0 0 0 3px #8ac9f1}.scale{display:flex;justify-content:space-between;font-size:10px;color:#93a8bd;margin:0 3px 15px}.fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.fields label{font-size:11px;color:#b7c7d9}.fields span{display:flex;align-items:center;gap:6px;margin-top:5px}.fields input{width:100%;min-width:0;font-size:15px;font-variant-numeric:tabular-nums}.preview{border:1px solid #ffffff17;border-radius:12px;margin-top:18px;padding:12px}.preview strong{font-size:15px}.legend{display:flex;flex-wrap:wrap;gap:12px;font-size:10px;margin-top:9px}.scope{font-size:11px}.message{min-height:20px;color:#d0dfef}footer{justify-content:flex-end;border-top:1px solid #ffffff13}[data-save]{background:#274b44}@media(max-width:400px){header,footer{padding:12px 14px}.body{padding:0 14px 8px}} button,select,input,summary{transition:background-color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,color 150ms ease} button:focus-visible,select:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid #a3d7f5;outline-offset:2px} @media(hover:hover){button:enabled:hover,summary:hover{border-color:#7895b65c;box-shadow:inset 0 0 0 1px #c0dcff0b}} @media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}} dialog{color-scheme:dark;border-color:#6380a45c;border-radius:22px;background:radial-gradient(ellipse at 35% 0,#526d9420,transparent 60%),#0d1622;box-shadow:inset 0 1px 0 #e3efff0a,0 24px 80px #0009;max-height:calc(100dvh - 24px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));overflow:hidden} dialog[open]{display:flex;flex-direction:column} dialog::backdrop{background:#030811bd;backdrop-filter:blur(4px)} dialog>.shell{display:flex;flex-direction:column;min-height:0;max-height:inherit} header,footer{flex:0 0 auto;background:#ffffff02} header{align-items:center;border-bottom:1px solid #b9d5f314} header>div{min-width:0} header h2{overflow-wrap:anywhere;line-height:1.3} header>button{flex:0 0 44px;width:44px;height:44px;min-height:44px;padding:0;border-radius:50%;display:grid;place-items:center;line-height:1} .body,.content{flex:1 1 auto;min-height:0;max-height:none;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#546a8266 transparent} footer{border-top:1px solid #b9d5f314;gap:8px} footer button{white-space:normal;overflow-wrap:anywhere} button:disabled{cursor:default} dl>div{gap:12px}dt,dd{min-width:0;overflow-wrap:anywhere} input:not([type=range]):not([type=checkbox]):not([type=radio]),select{border-color:#607d9e66;background-color:#152234} [data-save]:enabled{background:#24483f;border-color:#79bfa15e;color:#e1fff2} @media(max-height:500px){header{padding-top:10px;padding-bottom:10px}footer{padding-top:8px;padding-bottom:8px}} `,Ut=`.choice>label{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.choice>label>span{flex:1;min-width:0;overflow-wrap:anywhere}.reorder{display:flex;align-items:center;flex:none;gap:2px}.reorder>div{display:flex;flex-direction:column}.reorder button{padding:3px;min-height:28px;width:30px}.reorder [data-grip]{touch-action:none;-webkit-touch-callout:none;user-select:none;cursor:grab;width:38px;min-height:44px;border:0;background:transparent}.dragging{background:#ffffff12;box-shadow:0 0 0 1px #8dc4af66}.dragging [data-grip]{cursor:grabbing}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:host{font:14px system-ui;color:#dce7f4}*{box-sizing:border-box}dialog{color:inherit;background:#101925;border:1px solid #ffffff24;border-radius:22px;padding:0;width:min(480px,calc(100vw - 24px));max-height:calc(100dvh - 32px);overflow:hidden;box-shadow:0 24px 80px #0009}dialog::backdrop{background:#0008}header,footer{display:flex;align-items:center;gap:12px;padding:16px 20px;background:#ffffff03}header>div{flex:1;min-width:0}header small{color:#a2b4c8;font-size:11px}h2{font-size:18px;margin:5px 0;overflow-wrap:anywhere}header>button{font-size:23px}footer{justify-content:flex-end;border-top:1px solid #ffffff12}.body{padding:4px 20px 16px;overflow-y:auto;max-height:calc(100dvh - 214px);overscroll-behavior:contain}p{line-height:1.5;color:#aebed0;font-size:12px}.preview{display:flex;gap:12px;overflow-x:auto;min-height:44px;align-items:center;padding:8px;border:1px solid #ffffff16;border-radius:12px}.preview>span{display:inline-flex;gap:5px;align-items:center;white-space:nowrap;font-size:12px}ha-icon{--mdc-icon-size:18px;width:18px;height:18px;color:#a9bdd4;flex:none}fieldset{border:1px solid #ffffff18;border-radius:14px;margin:16px 0;padding:10px}legend{padding:0 6px;color:#c9d8e8}.choice{display:flex;align-items:center;gap:10px;min-height:48px;border-radius:8px;padding:4px}.choice>span{flex:1;overflow-wrap:anywhere}.choice small{display:block;color:#9fb1c6;font-size:11px;margin-top:3px}input[type=checkbox]{accent-color:#8dc4af;min-width:20px;height:20px}button,input{font:inherit;color:inherit;background:#192534;border:1px solid #ffffff24;border-radius:10px;padding:10px;min-height:44px}button{cursor:pointer}button:disabled,input:disabled{opacity:.45;cursor:default}button:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid #8ac9f1;outline-offset:2px}[data-save]{background:#284c48}details{border:1px solid #ffffff18;border-radius:12px;padding:12px}summary{cursor:pointer;min-height:30px}.add label{display:block;font-size:12px;margin:10px 0}.add label input{display:block;width:100%;margin-top:5px}code{overflow-wrap:anywhere}.scope,.status{font-size:12px}.status{color:#d1e1ed}.add-error{color:#f1a98f}@media(max-width:400px){header,footer{padding:12px 14px}.body{padding:4px 14px 12px}h2{font-size:16px}} button,select,input,summary{transition:background-color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,color 150ms ease} button:focus-visible,select:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid #a3d7f5;outline-offset:2px} @media(hover:hover){button:enabled:hover,summary:hover{border-color:#7895b65c;box-shadow:inset 0 0 0 1px #c0dcff0b}} @media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}} dialog{color-scheme:dark;border-color:#6380a45c;border-radius:22px;background:radial-gradient(ellipse at 35% 0,#526d9420,transparent 60%),#0d1622;box-shadow:inset 0 1px 0 #e3efff0a,0 24px 80px #0009;max-height:calc(100dvh - 24px - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));overflow:hidden} dialog[open]{display:flex;flex-direction:column} dialog::backdrop{background:#030811bd;backdrop-filter:blur(4px)} dialog>.shell{display:flex;flex-direction:column;min-height:0;max-height:inherit} header,footer{flex:0 0 auto;background:#ffffff02} header{align-items:center;border-bottom:1px solid #b9d5f314} header>div{min-width:0} header h2{overflow-wrap:anywhere;line-height:1.3} header>button{flex:0 0 44px;width:44px;height:44px;min-height:44px;padding:0;border-radius:50%;display:grid;place-items:center;line-height:1} .body,.content{flex:1 1 auto;min-height:0;max-height:none;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#546a8266 transparent} footer{border-top:1px solid #b9d5f314;gap:8px} footer button{white-space:normal;overflow-wrap:anywhere} button:disabled{cursor:default} dl>div{gap:12px}dt,dd{min-width:0;overflow-wrap:anywhere} input:not([type=range]):not([type=checkbox]):not([type=radio]),select{border-color:#607d9e66;background-color:#152234} [data-save]:enabled{background:#24483f;border-color:#79bfa15e;color:#e1fff2} @media(max-height:500px){header{padding-top:10px;padding-bottom:10px}footer{padding-top:8px;padding-bottom:8px}} `;var Ht=Ae,T=t=>String(t??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]),re=t=>Number.isFinite(t)?new Intl.NumberFormat(void 0,{maximumSignificantDigits:12}).format(t):"Unavailable",Li=t=>typeof t=="number"&&Number.isFinite(t)?t:typeof t=="string"&&/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t.trim())&&Number.isFinite(Number(t))?Number(t):null,je=t=>typeof t=="string"&&/^[a-z0-9_]+\.[a-z0-9_]+$/.test(t)?t:"",Vt=(t,e="Open source details")=>je(t)?``:"",Bt=t=>`
${t.map(e=>`
${e.entity&&je(e.entity)?``:T(e.label)}
${T(e.value??"Unavailable")}
`).join("")}
`,Gt=t=>t?.entity?`${t.entity}${t.attribute?` · ${t.attribute}`:""}`:"No source configured",Kt=t=>t?`

${T(t)}

`:"";function rt({view:t,title:e,eyebrow:i,content:r,footer:n="",tone:o="neutral"}){return`

${T(i)}

${T(e)}

${r}
${n}
`}function zi({model:t,config:e,statusMessage:i}){let r=e.name||t.name||"Thermostat",n=t.choices?.find(k=>k.key==="hvac_mode"),o=t.choices?.find(k=>k.key==="preset_mode"),l=t.unit||"",a=t.available&&t.connected?t.current:null,s=t.ready?"Ready":t.readiness?.status==="unknown"?"Unknown":"Not ready",d=t.reason||t.readiness?.reason||"Readiness is not reported.",p=t.output??{text:t.activity?.label||"Activity unknown",detail:t.activity?.detail||"No activity evidence is available.",tone:"unknown"},c=t.ready?["heat","cool"].includes(p.tone)?p.tone:"neutral":"warning",y=t.details?.some(k=>["unknown","unavailable","invalid","stale"].includes(k.status)),g=!t.connected||!t.available?"red":!t.ready||!t.canSet||y?"amber":"green",h={green:"#62e3b0",amber:"#efb86b",red:"#ef7777"}[g],f=`

${T(g==="green"?"Thermostat connection and readiness reported":"Thermostat needs attention")}

${g==="green"?"":`

${T(t.ready&&y?"Some provider observations are unknown or unavailable. Inspect provider details below.":d)}

`}
`,x=`

${T(p.text)}

${T(p.detail||t.activity?.detail)}

${i?`

${T(i)}

`:""}
`,u=(t.keys??[]).map(k=>({label:k==="target_temp_low"?"Lower target":k==="target_temp_high"?"Upper target":"Target temperature",value:t.available&&Number.isFinite(t.targets?.[k])?`${re(t.targets[k])} ${l}`:"Unavailable"})),_=[{id:"temperatures",title:"Temperatures",rows:[{label:"Current temperature",value:Number.isFinite(a)?`${re(a)} ${l}`:"Unavailable"},...u,{label:"Reported limits",value:Number.isFinite(t.min)&&Number.isFinite(t.max)?`${re(t.min)}–${re(t.max)} ${l}`:"Unavailable"},{label:"Temperature step",value:Number.isFinite(t.step)?`${re(t.step)} ${l} · ${t.stepSource||"Unknown source"}`:"Unavailable"}]},{id:"control",title:"Control source and modes",rows:[{label:"HVAC mode",value:n?.value??t.state??"Unknown"},{label:"Preset",value:o?.value??"Not reported"},{label:"Control ownership",value:t.ownership?.label||"Unknown"}],note:t.ownership?.detail||"No authoritative ownership contract is configured. Presets and matching target temperatures do not identify ownership."},{id:"provider",title:"Provider and readiness",rows:[{label:"Provider",value:t.provider?.label||"Unknown"},{label:"Provider identity",value:t.provider?.evidence||"Not verified"},{label:"Provider restrictions",value:t.provider?.reason||"No additional provider restrictions reported."},{label:"Connection",value:t.connected?"Connected":"Disconnected or unknown"},{label:"Readiness",value:`${s} · ${d}`},{label:"Climate entity",value:e.entity||"Not configured",entity:e.entity}]},{id:"activity",title:"Activity and confirmation",rows:[{label:"Reported output",value:p.text},{label:"Output source",value:Gt(p.source),entity:p.source?.entity},{label:"Request status",value:i||"No active card request is reported."}],note:"This view shows the signals available to the card. It does not verify physical heat delivery, sensor accuracy or device response."},{id:"response",title:"Temperature response",rows:[{label:"Response arc",value:t.arc?.label||t.arc?.reason?.replaceAll("_"," ")||"Unavailable"},{label:"Warming response",value:t.arc?.responding?"Recent warming with an accepted request":"Not established"}],note:t.arc?.explanation||"Temperature-response evidence is unavailable."}];t.details?.length&&_.push({id:"provider-details",title:"Provider details",rows:t.details.map(k=>({label:k.label,value:`${k.value??k.status??"Unavailable"}${k.unit?` ${k.unit}`:""}${k.detail?` · ${k.detail}`:""}`}))});let b=`
${_.map(k=>`
${T(k.title)}${Bt(k.rows)}${k.note?`

${T(k.note)}

`:""}
`).join("")}
`,$=`${Vt(e.entity,"Thermostat details")}

Thermostat details open in Home Assistant.

`;return{title:r,html:rt({view:"overview",title:r,eyebrow:"Thermal intelligence · overview",content:(g==="green"?x+f:f+x)+b,footer:$,tone:c})}}function Fi({model:t,config:e,rowKey:i,statusMessage:r}){let n=t.headerTelemetry??[],o=n.find(c=>c.key===i)??n.find(c=>c.topic===i)??(i==="humidity"?t.humidity:null);if(i==="follow"&&!o&&t.provider?.kind==="vtherm"){let c=t.connected&&t.available?t.attrs?.vtherm_over_climate?.follow_underlying_temp_change:null;o={label:"Follow underlying temperature",status:typeof c=="boolean"?"available":"unknown",value:c===!0?"On":c===!1?"Off":"Unavailable",source:{entity:e.entity,attribute:"vtherm_over_climate.follow_underlying_temp_change"},detail:"This is the provider-reported follow setting. Selecting its icon does not change thermostat configuration."}}if(i==="humidity"||o?.kind==="humidity"||o?.topic==="humidity")return Pi({model:t,config:e,row:o,statusMessage:r});let l=o?.label||(i==="follow"?"Follow underlying target":"Telemetry"),a=pe[o?.kind],s=o?.status==="available"?o.value??re(o.rawValue):o?.status==="disabled"?"Not configured":o?.status||"Unavailable",d=[{label:a?"State":"Selected reading",value:s},{label:"Status",value:o?.status||"Not configured"},{label:"Source",value:Gt(o?.source),entity:o?.source?.entity},...a?[]:[{label:"Unit",value:o?.unit||"Not reported"}]],p=`
${Bt(d)}

${T(o?.detail||"No matching configured source is available. No source is inferred from an entity name.")}

${Kt(r)}
`;return{title:l,html:rt({view:"telemetry",title:l,eyebrow:e.name||t.name||"Thermostat",content:p})}}var Pi=t=>Pt(t);function ji({model:t,config:e,allowed:i,busy:r,preview:n,statusMessage:o}){let l=e.name||t.name||"Thermostat",a=t.choices?.find(b=>b.key==="preset_mode"),s=a?.options??[],d=["°C","°F"].includes(t.attrs?.configuration?.temperature_unit)?t.attrs.configuration.temperature_unit:null,p=Object.values(t.presetReadings??{}).some(b=>b.status==="available")?t.unit:d||t.unit||"",y=(t.provider?.kind==="vtherm"?[...new Set(["frost","eco","comfort","boost",...s.filter(b=>b!=="none")])]:s.filter(b=>b!=="none")).map(b=>{let $=t.presetReadings?.[b],k=$&&$.status!=="disabled"?$.status==="available"?$.value:null:t.connected&&t.available&&t.provider?.kind==="vtherm"&&d===p&&["frost","eco","comfort","boost"].includes(b)?Li(t.attrs?.preset_temperatures?.[`${b}_temp`]):null;return{key:b,label:Object.hasOwn(e.preset_labels??{},b)?e.preset_labels[b]:b.charAt(0).toUpperCase()+b.slice(1),value:k,selected:t.connected&&t.ready&&t.state==="heat"&&a?.value===b,entity:$?.source?.entity||(k!==null?e.entity:"")}}),g=y.map(b=>b.value).filter(b=>b!==null),h=Math.floor(Math.min(0,...g)/5)*5,f=Math.ceil(Math.max(p==="°F"?90:30,...g)/5)*5;(!Number.isFinite(h)||!Number.isFinite(f)||!Number.isFinite(f-h)||f<=h)&&(h=0,f=p==="°F"?90:30);let x=f-h,u=-h/x*100,_=`
${t.connected?"":'

Connection lost. Current preset temperatures are unavailable.

'}
${y.map(b=>`
${T(b.label)}${b.selected?"Preset selected":""}
`).join("")}
${y.length?`
Scale ${T(`${h} ${p}`)}${T(`${f} ${p}`)}
`:"

No presets are advertised by this thermostat.

"}

Tap a temperature to edit its preset.

${Kt(o)}
`;return{title:l,html:rt({view:"presets",title:l,eyebrow:"Preset temperatures",content:_})}}function Ui({model:t,config:e,statusMessage:i}){let r=`${e.name||t.name||"Thermostat"} · Card settings`,n=(t.headerTelemetry??[]).map(o=>`${T(o.value??"Unavailable")}`).join("")||"No configured header readings.";return{title:r,html:`
Thermal intelligence · display

${T(r)}

Choose the readings beside the card name and the display settings for this card.

${n}
<${ue} data-dialog-editor>
Configuration to copy

Draft only. This dialog does not save dashboard configuration. Copy the configuration below into this card’s Home Assistant editor, or make the same changes directly in its visual editor, then save.

${T(i||"")}

`}}function st({kind:t="overview",model:e={},config:i={},rowKey:r,settingsDraft:n,statusMessage:o="",allowed:l=!1,busy:a=!1,preview:s=!1}={}){let d={model:e,config:t==="settings"&&n?n:i,rowKey:r,statusMessage:o,allowed:l,busy:a,preview:s},p=t==="telemetry"||t==="humidity"?Fi({...d,rowKey:t==="humidity"?"humidity":r}):t==="presets"?ji(d):t==="settings"?Ui(d):zi(d),c=p.html.startsWith('String(t??"").replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e]);function Yt({name:t,locked:e,codeRequired:i}){return`

THERMOSTAT LOCK

${Hi(t)}

`}function Wt(t){return t.lockUsers===!0&&t.lockAutomations===!0?"Locks changes from users and automations.":t.lockUsers===!0&&t.lockAutomations===!1?"Locks changes from users. Automations remain available.":t.lockUsers===!1&&t.lockAutomations===!0?"Locks changes from automations. User controls remain available.":"Lock scope is unavailable."}function Jt(t){return t.autoRelockSec===0?"Automatic relocking is off.":Number.isFinite(t.autoRelockSec)&&t.autoRelockSec>0?`VT relocks ${t.autoRelockSec} seconds after unlocking.`:"Automatic relock timing is unavailable."}var Xt=t=>Number.isFinite(t)?new Intl.NumberFormat(void 0,{maximumSignificantDigits:12}).format(t):"—",Vi=new Set(["hui-card-preview","hui-dialog-edit-card","hui-dialog-create-card","hui-card-element-editor"]),Ue=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._view=document.createElement("div"),this.shadowRoot.append(this._view),this._animationOffset=Date.now()%4e3,this.config=ae({}),this._preview=!1,this._editMode=!1,this._identity=null,this._identityToken=0,this._knownVT=new Set,this._vtByConnection=new WeakMap,this._configRevision=0,this._open=!1,this._activeTarget="temperature",this._registryStatus="idle",this._coordinator=new Pe({onChange:()=>this._scheduleRender()})}static getStubConfig(e,i=[],r=[]){let n=[...i,...r,...Object.keys(e?.states??{})];return{type:Se,entity:n.find(o=>typeof o=="string"&&o.startsWith("climate."))??"",adapter:"auto"}}static async getConfigElement(){return document.createElement(ue)}getCardSize(){return this._open?10:6}getGridOptions(){return{columns:6,min_columns:4,rows:"auto"}}setConfig(e){this._closeDialog(),this._cancelDraft(),this.config=ae(e),this._configRevision++,this._registryKey=null,this._identityToken++,this._identity=null,this._refreshIdentity(),this._updateContext(),this._scheduleRender()}set hass(e){this._hass=e,this._syncIdentityContext(),this._refreshIdentity();let i=this.model();this._draftContext&&!this._draftStillValid(i)&&this._cancelDraft("Thermostat context changed. The draft was cancelled."),this._updateContext(i),this._coordinator.observe(e?.states?.[this.config.entity]),this._scheduleRender()}get hass(){return this._hass}set preview(e){this._preview=e===!0,this._cancelDraft(),this._updateContext(),this._scheduleRender()}get preview(){return this._preview}set editMode(e){this._editMode=e===!0,this._cancelDraft(),this._updateContext(),this._scheduleRender()}get editMode(){return this._editMode}connectedCallback(){this._refreshIdentity(),this._updateContext(),this._scheduleRender()}disconnectedCallback(){this._closeDialog(),clearTimeout(this._collapseTimer),clearTimeout(this._freshnessTimer),this._identityToken++,this._registryKey=null,this._cancelDraft(),this._coordinator.cancel("Card detached. Any remote outcome remains unverified.")}_isPreview(){if(this._preview||this._editMode||this.config.preview===!0)return!0;let e=this;for(;e;){if(Vi.has(e.localName))return!0;e=e.parentElement??e.getRootNode?.().host??null}return!1}_updateContext(e=this.model()){let i=this._lockCapability();this._coordinator.updateContext({hass:this._hass,entity:this.config.entity,preview:this._isPreview(),contextKey:this._configRevision,model:{ready:e.ready&&this._registryStatus!=="loading",lockReady:i.allowed}}),this._checkLockIntent(i)}_lockCapability(){return qt({hass:this._hass,entity:this.config.entity,identity:this._identity,registryStatus:this._registryStatus,preview:this._isPreview(),attached:this.isConnected})}_session(){return{connection:this._hass?.connection,socket:this._hass?.connection?.socket,user:this._hass?.user?.id,connected:this._hass?.connected===!0}}_sameSession(e,i){return!!e&&Object.keys(i).every(r=>e[r]===i[r])}_syncIdentityContext(){let e=this._session();this._sameSession(this._identityContext,e)||(this._identityContext=e,this._identityToken++,this._registryKey=null,this._identity=null,this._registryStatus="idle",e.connection&&typeof e.connection=="object"?(this._vtByConnection.has(e.connection)||this._vtByConnection.set(e.connection,new Set),this._knownVT=this._vtByConnection.get(e.connection)):this._knownVT=new Set)}async _refreshIdentity(){let e=this._hass,i=this.config.entity;if(!e||!i){this._registryStatus="idle";return}this._syncIdentityContext();let r=`${i}|${this._configRevision}`;if(this._registryKey===r)return;if(this._knownVT.has(i)&&(this._identity={entity_id:i,platform:"versatile_thermostat"}),e.connected!==!0){this._registryStatus="unavailable";return}this._registryKey=r;let n=e.entities?.[i];if(n?.entity_id===i&&typeof n.platform=="string"){this._acceptIdentity(n);return}if(typeof e.callWS!="function"){this._registryStatus="unavailable";return}let o=this._session(),l=++this._identityToken;this._registryStatus="loading";try{let a=await e.callWS({type:"config/entity_registry/get",entity_id:i});if(l!==this._identityToken||!this._sameSession(o,this._session())||this.config.entity!==i)return;if(a?.entity_id!==i||typeof a.platform!="string")throw new Error("Invalid identity");this._acceptIdentity(a)}catch{if(l!==this._identityToken||!this._sameSession(o,this._session()))return;this._registryStatus="unavailable"}this._updateContext(),this._scheduleRender()}_acceptIdentity(e){this._identity={entity_id:e.entity_id,platform:e.platform,device_id:e.device_id},e.platform==="versatile_thermostat"?this._knownVT.add(e.entity_id):this._knownVT.delete(e.entity_id),this._registryStatus="verified"}model(){return Ot(this._hass??{},this.config,this._identity)}_guard(e=this.model()){return this.isConnected?this._isPreview()?{allowed:!1,reason:"Controls are disabled while previewing or editing."}:this._hass?.connected!==!0||!this._hass?.user?.id||!this._hass?.connection?{allowed:!1,reason:"An active authenticated Home Assistant connection is required."}:this._registryStatus==="loading"?{allowed:!1,reason:"Checking thermostat provider identity."}:{allowed:e.ready===!0,reason:e.reason||"Thermostat control is unavailable."}:{allowed:!1,reason:"Card is detached."}}async _request(e,i){return this._draftMessage="",this._updateContext(),this._coordinator.request({hass:this._hass,entity:this.config.entity,service:e,values:i,preview:this._isPreview(),validate:({values:r})=>{let n=this.model(),o=this._guard(n);if(!o.allowed)return o;if(e==="set_temperature"){let a=Object.keys(r);return{allowed:(n.canSet&&a.length===n.keys.length&&a.every(d=>n.keys.includes(d)&&r[d]>=n.min&&r[d]<=n.max&&Math.abs(Xe(r[d],n.min,n.max,n.step)-r[d])<1e-8)&&(!n.ranged||r.target_temp_low<=r.target_temp_high))===!0,reason:n.reason||"The requested targets no longer match the current limits or step."}}let l=n.choices.find(a=>a.service===e);return{allowed:l?.enabled===!0&&l.options.includes(r[l.key]),reason:"This control is not supported or available."}}})}_temperature(e,i=this._targetKey(this.model())){let r=this.model();try{let n=ze(r,i,e);return Object.entries(n).every(([o,l])=>r.targets[o]===l)?Promise.resolve():this._request("set_temperature",n)}catch(n){return this._draftMessage=n.message,this._scheduleRender(),Promise.resolve()}}_targetKey(e){return e.keys.includes(this._activeTarget)?this._activeTarget:e.keys[0]}_signature(e){return JSON.stringify([this.config.entity,this._configRevision,this._isPreview(),this._targetKey(e),e.ready,e.canSet,e.unit,e.min,e.max,e.step,e.keys,e.targets,e.choices.find(i=>i.key==="hvac_mode")?.value,e.provider])}_startDraft(e=this.model()){this._draftContext={session:this._session(),signature:this._signature(e)}}_draftStillValid(e=this.model()){return!!this._draftContext&&this._sameSession(this._draftContext.session,this._session())&&this._draftContext.signature===this._signature(e)&&this._guard(e).allowed&&!this._coordinator.busy}_cancelDraft(e=""){this._drag=null,this._draft=null,this._draftContext=null,e&&(this._draftMessage=e)}_scheduleRender(){this._renderQueued||(this._renderQueued=!0,queueMicrotask(()=>{this._renderQueued=!1,this.isConnected&&(this._draftContext?this._refreshArcDuringDraft():this.render())}))}_refreshArcDuringDraft(){let e=this.model().arc,i=/^#[0-9a-f]{6}$/i.test(e?.color??"")?e.color:"#94a3b8",r=this.shadowRoot.querySelector("ha-card");r&&(r.style.setProperty("--accent",i),r.dataset.arcState=e?.responding?"responding":"not-responding",r.dataset.arcReason=e?.reason||"unavailable");for(let n of this.shadowRoot.querySelectorAll("[data-response-arc]"))n.setAttribute("stroke",i);this._viewHTML=null,this._renderDialog(),this._scheduleFreshness()}_pointerAngle(e){try{let i=this.shadowRoot.querySelector(".dial > svg"),r=i?.getScreenCTM();if(!r||!Number.isFinite(e.clientX)||!Number.isFinite(e.clientY))return null;let n=new DOMPoint(e.clientX,e.clientY).matrixTransform(r.inverse());return!Number.isFinite(n.x)||!Number.isFinite(n.y)||Math.hypot(n.x-100,n.y-100)<1?null:Math.atan2(n.y-100,n.x-100)*180/Math.PI}catch{return null}}_paintDraft(e){this._viewHTML=null;let i=this.model(),r=this._targetKey(i),n=(h,f)=>[100+h*Math.cos(f*Math.PI/180),100+h*Math.sin(f*Math.PI/180)],o=135+270*(e-i.min)/(i.max-i.min),[l,a]=n(72,o),s=this.shadowRoot.querySelector(`[data-value="${r}"]`);s&&(s.textContent=Xt(e)+i.unit);let d=this.shadowRoot.querySelector(`[data-marker="${r}"]`);d&&(d.style.left=`${(l-10)/180*100}%`,d.style.top=`${a/184*100}%`,d.setAttribute("aria-valuenow",e),d.setAttribute("aria-valuetext",`${Xt(e)} ${i.unit}, draft`),d.dataset.active="true");let p=this.shadowRoot.querySelector(`[data-marker-visual="${r}"]`);p&&(p.setAttribute("transform",`translate(${l} ${a})`),p.querySelector(".target-dot")?.setAttribute("r","4"),p.classList.toggle("keyboard-focus",!!d?.matches(":focus-visible")));let c=this.shadowRoot.querySelector(`[data-target-edit="${r}"]`);c&&(c.textContent="Draft",c.hidden=!1,c.title="Adjustment has not been sent.");let y=this.shadowRoot.querySelector("#arrow");y&&(y.style.display="",y.setAttribute("d",`M ${n(76,o)} L ${n(84,o)} M ${n(80,o-2.4)} L ${n(84,o)} L ${n(80,o+2.4)}`));let g=20*(e-i.min)/(i.max-i.min);this.shadowRoot.querySelectorAll(".diamond").forEach((h,f)=>{h.classList.toggle("selected",f<=Math.round(g));let x=Math.abs(f-g),u=x<1.5?2.5*(1+Math.cos(Math.PI*x/1.5)):0,_=(135+f*13.5)*Math.PI/180;h.style.setProperty("--tick-x",`${(Math.cos(_)*u).toFixed(3)}px`),h.style.setProperty("--tick-y",`${(Math.sin(_)*u).toFixed(3)}px`)}),this.shadowRoot.querySelector(".dial > svg")?.classList.add("adjusting")}_bindMarker(e,i){if(!i.canSet||!this._guard(i).allowed||this._coordinator.busy)return;let r=e.dataset.marker;e.onpointerdown=o=>{if(o.button!==0||o.isPrimary===!1||this._drag)return;o.preventDefault(),e.focus({preventScroll:!0}),this._activity(),this._activeTarget=r,this._startDraft();let l=this.model(),a=l.targets[r];this._draft=a,this._drag={pointer:o.pointerId,x:o.clientX,y:o.clientY,moved:!1,initial:a,lastAngle:this._pointerAngle(o),offset:270*(a-l.min)/(l.max-l.min)};try{e.setPointerCapture(o.pointerId),this._paintDraft(a)}catch{this._cancelDraft("Adjustment could not start. Try again."),this._scheduleRender()}};let n=o=>{let l=this._drag;if(!l||l.pointer!==o.pointerId)return!1;if(!this._draftStillValid())return this._cancelDraft("Thermostat context changed. The draft was cancelled."),this._scheduleRender(),!1;let a=this._pointerAngle(o);if(a===null)return!1;if(!l.moved&&Math.hypot(o.clientX-l.x,o.clientY-l.y)<2)return!0;if(l.lastAngle===null)return l.lastAngle=a,!0;l.moved=!0,l.offset=Math.max(0,Math.min(270,l.offset+(a-l.lastAngle+540)%360-180)),l.lastAngle=a;let s=this.model();try{return this._draft=ze(s,r,s.min+l.offset/270*(s.max-s.min))[r],this._paintDraft(this._draft),!0}catch{return this._cancelDraft("The temperature draft is no longer valid."),this._scheduleRender(),!1}};e.onpointermove=n,e.onpointerup=o=>{if(!this._drag||this._drag.pointer!==o.pointerId)return;let l=this._drag.initial,a=n(o)&&this._draftStillValid(),s=this._draft;this._cancelDraft(),e.hasPointerCapture(o.pointerId)&&e.releasePointerCapture(o.pointerId),a&&s!==null&&s!==l&&this._temperature(s,r),this._activity(),this._scheduleRender()},e.onpointercancel=()=>{this._cancelDraft("Adjustment cancelled."),this._scheduleRender()},e.onlostpointercapture=()=>{this._drag&&(this._cancelDraft("Adjustment cancelled."),this._scheduleRender())},e.onkeydown=o=>{let l=this.model(),a=this._draft??l.targets[r];if(o.key==="Escape"){this._cancelDraft(),this._scheduleRender();return}let s={ArrowUp:l.step,ArrowRight:l.step,ArrowDown:-l.step,ArrowLeft:-l.step,PageUp:l.step*5,PageDown:-l.step*5};if(!(o.key in s)&&!["Home","End"].includes(o.key)||(o.preventDefault(),this._drag||o.repeat&&!this._draftContext))return;if(this._draftContext||(this._activeTarget=r,this._startDraft(l)),!this._draftStillValid(l)){this._cancelDraft(),this._scheduleRender();return}let d=o.key==="Home"?l.min:o.key==="End"?l.max:a+s[o.key];try{this._draft=ze(l,r,d)[r],this._paintDraft(this._draft),this._activity()}catch{this._cancelDraft(),this._scheduleRender()}},e.onkeyup=o=>{if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","PageUp","PageDown","Home","End","Enter"].includes(o.key)&&this._draft!==null&&this._draft!==void 0&&!this._drag){let l=this._draft,a=this._draftStillValid();this._cancelDraft(),a&&this._temperature(l,r),this._scheduleRender()}},e.onfocus=()=>this.shadowRoot.querySelector(`[data-marker-visual="${r}"]`)?.classList.add("keyboard-focus"),e.onblur=()=>{this.shadowRoot.querySelector(`[data-marker-visual="${r}"]`)?.classList.remove("keyboard-focus"),this._draftContext&&(this._cancelDraft(),this._scheduleRender())}}_activity(){clearTimeout(this._collapseTimer),!(!this._open||!this.isConnected)&&(this._collapseTimer=setTimeout(()=>{if(this._draftContext||this._coordinator.busy||this._dialogHost||this.shadowRoot.activeElement){this._activity();return}this._open=!1,this._scheduleRender()},3e4))}_moreInfo(e=this.config.entity){this._closeDialog(),e&&this.dispatchEvent(new CustomEvent("hass-more-info",{detail:{entityId:e},bubbles:!0,composed:!0}))}_deviceSettings(){let e=this._identity?.device_id;if(this._registryStatus!=="verified"||typeof e!="string"||!/^[-a-z0-9_]{1,128}$/i.test(e)){this._moreInfo();return}this._closeDialog(),history.pushState(null,"",`/config/devices/device/${encodeURIComponent(e)}`),window.dispatchEvent(new CustomEvent("location-changed"))}_bindDevice(e){let i=null,r=null,n=!1,o=()=>{clearTimeout(i),i=null,r=null};e.onpointerdown=l=>{l.button!==0||l.isPrimary===!1||(o(),n=!1,r={x:l.clientX,y:l.clientY},i=setTimeout(()=>{o(),e.isConnected&&(n=!0,this._deviceSettings())},600))},e.onpointermove=l=>{r&&Math.hypot(l.clientX-r.x,l.clientY-r.y)>10&&o()},e.onpointerup=o,e.onpointercancel=o,e.onlostpointercapture=o,e.onclick=()=>{if(o(),n){n=!1;return}this._openDialog("overview")},e.onkeydown=l=>{l.key==="Enter"&&l.shiftKey&&(l.preventDefault(),o(),this._deviceSettings())}}_closeDialog(){this._clearLockCode(),this._lockIntent=null;let e=this._dialogHost,i=this._dialogReturnFocus;this._dialogHost=null,this._dialogSpec=null,this._dialogMarkup=null,this._settingsDraft=null,this._displayDraft=null,this._dialogReturnFocus=null,e?.shadowRoot.querySelector("dialog")?.close(),e?.remove(),this._activity(),e&&i&&this.shadowRoot.querySelector(`[data-focus="${CSS.escape(i)}"]`)?.focus({preventScroll:!0})}_openDialog(e="overview",i){let r=e==="lock"?this._lockCapability():null;if(e==="lock"&&(!r.allowed||this._coordinator.busy))return;let n=this._dialogReturnFocus||this.shadowRoot.activeElement?.getAttribute("data-focus");this._closeDialog(),this._cancelDraft(),this._dialogSpec={kind:e,rowKey:i},this._displayDraft=structuredClone(this.config),this._dialogReturnFocus=n,e==="lock"&&(this._lockIntent={entity:this.config.entity,revision:this._configRevision,session:this._session(),signature:r.signature,locked:!r.locked,codeRequired:r.codeRequired,submitted:!1,invalid:!1,message:""}),this._dialogHost=document.createElement("div"),this._dialogHost.attachShadow({mode:"open"}),this._dialogHost.style.cssText="--primary-text-color:#eff5ff;--secondary-text-color:#a9b8cb;--card-background-color:#0d1622;--input-fill-color:#152234;--secondary-background-color:#152234;--divider-color:#34465c;--primary-color:#8ac9f1;color-scheme:dark",this.shadowRoot.append(this._dialogHost),this._renderDialog(!0)}_renderDialog(e=!1){if(!this._dialogHost||!this._dialogSpec)return;if(this._dialogSpec.kind==="lock"){this._renderLockDialog(e);return}if(!e&&this._dialogSpec.kind==="settings"){let u=this._dialogHost.shadowRoot.querySelector("[data-dialog-editor]");u&&(u.hass=this._hass);return}let i=this._dialogHost.shadowRoot,r=this.model(),n=i.activeElement,o=n?.getAttribute("data-focus"),l=n?.tagName==="SUMMARY"?n.parentElement.id:null,a=["data-dialog-close","data-dialog-info","data-dialog-preset","data-dialog-settings"].find(u=>n?.hasAttribute(u)),s=a?n.getAttribute(a):null,d=[...i.querySelectorAll("details[open][id]")].map(u=>u.id),p=i.querySelector(".content,.body")?.scrollTop||0,c=st({...this._dialogSpec,model:r,config:this._displayDraft||this.config,allowed:this._guard(r).allowed,busy:this._coordinator.busy,preview:this._isPreview(),statusMessage:this._draftMessage||(this._coordinator.status.phase==="idle"?"":this._coordinator.status.message)}),y=i.querySelector("dialog"),g=y?.open;if(!e&&y?.dataset.view==="humidity"&&n?.hasAttribute("data-humidity-boundary")){this._paintHumidityDraft(c.html);return}if(!e&&c.html===this._dialogMarkup)return;this._dialogMarkup=c.html,y?.close(),i.innerHTML=`${c.html}`;let h=i.querySelector("dialog");h.addEventListener("cancel",u=>{u.preventDefault(),this._closeDialog()});for(let u of i.querySelectorAll("[data-dialog-close]"))u.onclick=()=>this._closeDialog();for(let u of i.querySelectorAll("[data-dialog-info]"))u.onclick=()=>this._moreInfo(u.dataset.dialogInfo);for(let u of i.querySelectorAll("[data-dialog-preset]"))u.onclick=()=>this._request("set_preset_mode",{preset_mode:u.dataset.dialogPreset});for(let u of i.querySelectorAll("[data-dialog-settings]"))u.onclick=()=>this._openDialog("settings");let f=i.querySelector("[data-dialog-editor]");if(f){f.setConfig(this.config),f.hass=this._hass,f.addEventListener("config-changed",_=>{_.stopPropagation(),this._settingsDraft=_.detail.config;let b=i.querySelector("[data-settings-export]");b&&(b.value=JSON.stringify(this._settingsDraft,null,2))});let u=i.querySelector("[data-settings-export]");u&&(u.value=JSON.stringify(this.config,null,2)),i.querySelector("[data-copy-settings]")?.addEventListener("click",async()=>{let _=i.querySelector("[data-dialog-status]");try{await navigator.clipboard.writeText(JSON.stringify(this._settingsDraft||this.config,null,2)),_&&(_.textContent="Configuration copied. Paste it into this card’s Home Assistant configuration editor, then save.")}catch{u?.focus(),u?.select(),_&&(_.textContent="Select and copy the configuration below. Save it in this card’s Home Assistant configuration editor.")}})}for(let u of i.querySelectorAll("[data-humidity-boundary]"))u.oninput=()=>{let _=u.dataset.humidityBoundary,b=ce(this._displayDraft)||this._displayDraft.humidity_bands||{},$=u.value.trim()===""?NaN:Number(u.value);this._displayDraft={...this._displayDraft,humidity_bands:u.type==="range"?yt(b,_,$):{...b,[_]:$}};let k=st({...this._dialogSpec,model:this.model(),config:this._displayDraft});this._paintHumidityDraft(k.html)};i.querySelector("[data-copy-humidity]")?.addEventListener("click",async()=>{if(!ce(this._displayDraft))return;let u=i.querySelector("[data-dialog-status]"),_=JSON.stringify(this._displayDraft,null,2);try{await navigator.clipboard.writeText(_),u&&(u.textContent="Configuration copied. Paste it into this card’s Home Assistant configuration editor, then save.")}catch{let b=i.querySelector("[data-humidity-export]");b||(b=document.createElement("textarea"),b.dataset.humidityExport="",b.readOnly=!0,b.setAttribute("aria-label","Draft humidity configuration"),b.style.cssText="width:100%;min-height:150px;color:inherit;background:#0b1420",i.querySelector(".body").append(b)),b.value=_,b.focus(),b.select(),u&&(u.textContent="Select and copy the configuration shown below, then save it in this card’s Home Assistant editor.")}});for(let u of d)i.querySelector(`#${CSS.escape(u)}`)?.setAttribute("open","");(e||g)&&h.showModal();let x=i.querySelector(".content,.body");x&&(x.scrollTop=p),o?i.querySelector(`[data-focus="${CSS.escape(o)}"]`)?.focus({preventScroll:!0}):l?i.querySelector(`#${CSS.escape(l)} > summary`)?.focus({preventScroll:!0}):a&&i.querySelector(`[${a}="${CSS.escape(s)}"]`)?.focus({preventScroll:!0})}_clearLockCode(){let e=this._dialogHost?.shadowRoot.querySelector("[data-lock-code]");e&&(e.value="")}_lockIntentReason(e,i){return!e||this._lockIntent!==e||e.entity!==this.config.entity||e.revision!==this._configRevision||!this._sameSession(e.session,this._session())?"Thermostat context changed. Close and reopen lock controls.":e.invalid?e.message||"The lock action is no longer available. Close and reopen lock controls.":i.allowed?e.signature!==i.signature?"Lock state or settings changed. Close and reopen to choose an action.":"":i.reason||"Lock control is unavailable. Close and reopen when available."}_checkLockIntent(e=this._lockCapability()){let i=this._lockIntent;if(!i||i.invalid)return;let r=i.submitted?!e.allowed||!this._sameSession(i.session,this._session())?"Lock control is no longer available. Check the current state before trying again.":"":this._lockIntentReason(i,e);r&&(i.invalid=!0,i.message=r,this._clearLockCode())}_renderLockDialog(e=!1){let i=this._lockIntent,r=this._dialogHost?.shadowRoot;if(!i||!r)return;let n=this._lockCapability();if(this._checkLockIntent(n),!r.querySelector("[data-lock-dialog]")){let c=this.config.name||this._hass?.states?.[i.entity]?.attributes?.friendly_name||i.entity;r.innerHTML=Yt({name:c,locked:i.locked,codeRequired:i.codeRequired}),r.querySelector("dialog").addEventListener("cancel",y=>{y.preventDefault(),this._closeDialog()});for(let y of r.querySelectorAll("[data-lock-close]"))y.onclick=()=>this._closeDialog();r.querySelector("form").onsubmit=y=>{y.preventDefault(),this._submitLock()},r.querySelector("[data-lock-code]").oninput=()=>this._renderLockDialog()}let o=this.model(),l=o.connected===!0&&o.available===!0&&this._registryStatus==="verified"&&this._identity?.entity_id===this.config.entity&&this._identity?.platform==="versatile_thermostat"&&this._hass?.states?.[this.config.entity]?.entity_id===this.config.entity,a=l?o.lock?.status==="locked"?"Locked":o.lock?.status==="unlocked"?"Unlocked":"Unknown":"Unavailable";r.querySelector("[data-lock-current]").textContent=`VT reports: ${a}`,r.querySelector("[data-lock-scope]").textContent=l?Wt(n):"Lock details are unavailable.",r.querySelector("[data-lock-relock]").textContent=l?Jt(n):"";let s=r.querySelector("[data-lock-code]");s.disabled=!i.codeRequired||i.submitted||i.invalid;let d=this._coordinator.status,p=i.submitted&&d.target===i.entity&&d.domain==="versatile_thermostat";r.querySelector("[data-lock-message]").textContent=i.message||(p?d.message:""),r.querySelector("[data-lock-dismiss]").textContent=i.submitted?"Close":"Cancel",r.querySelector("[data-lock-submit]").disabled=i.submitted||i.invalid||!n.allowed||this._coordinator.busy||i.codeRequired&&s.value.length===0,e&&r.querySelector("dialog").showModal()}_submitLock(){let e=this._lockIntent,i=this._dialogHost?.shadowRoot;if(!e||!i||e.submitted||e.invalid||this._coordinator.busy)return;let r=this._lockCapability(),n=this._lockIntentReason(e,r);if(n){e.invalid=!0,e.message=n,this._clearLockCode(),this._renderLockDialog();return}let o=e.codeRequired?i.querySelector("[data-lock-code]").value:void 0;if(e.codeRequired&&o.length===0){i.querySelector("[data-lock-code]").focus();return}e.submitted=!0,this._clearLockCode(),this._draftMessage="",this._updateContext();try{let l=this._coordinator.requestLock({hass:this._hass,entity:e.entity,locked:e.locked,code:o,preview:this._isPreview(),contextKey:e.revision,validate:({entity:a,values:s})=>{let d=this._lockCapability(),p=this._lockIntentReason(e,d);return{allowed:!p&&a===e.entity&&s.locked===e.locked,reason:p||"The selected lock action is no longer available."}}});o=void 0,Promise.resolve(l).catch(()=>{this._lockIntent===e&&(e.message="The lock request could not be completed. Check the current state before trying again.",this._renderLockDialog())})}catch{e.message="The lock request could not be completed. Check the current state before trying again."}finally{o=void 0,this._renderLockDialog(),this._scheduleRender()}}_paintHumidityDraft(e){let i=this._dialogHost?.shadowRoot;if(!i)return;let r=document.createElement("template");r.innerHTML=e;let n=r.content,o=n.querySelector("dialog")?.getAttribute("data-reading-status");o&&i.querySelector("dialog")?.setAttribute("data-reading-status",o);for(let s of[".track",".preview",".reading","[data-dialog-status]"]){let d=i.querySelector(s),p=n.querySelector(s);if(d&&p){d.innerHTML=p.innerHTML;let c=p.getAttribute("style");c!==null&&d.setAttribute("style",c)}}for(let s of i.querySelectorAll("[data-humidity-boundary]")){let d=`input[type="${s.type}"][data-humidity-boundary="${CSS.escape(s.dataset.humidityBoundary)}"]`,p=n.querySelector(d);if(p){(s!==i.activeElement||s.type==="range")&&(s.value=p.value),s.disabled=p.disabled;for(let c of["aria-invalid","aria-valuetext"])p.hasAttribute(c)&&s.setAttribute(c,p.getAttribute(c))}}let l=i.querySelector("[data-copy-humidity]"),a=n.querySelector("[data-copy-humidity]");l&&a&&(l.disabled=a.disabled);for(let s of i.querySelectorAll("[data-dialog-info]"))s.onclick=()=>this._moreInfo(s.dataset.dialogInfo)}_scheduleFreshness(){clearTimeout(this._freshnessTimer);let e=Date.now(),i=this.config.telemetry.filter(n=>n.max_age_seconds&&n.entity).map(n=>{let o=Date.parse(this._hass?.states?.[n.entity]?.last_updated);return o>e?o+1:o+n.max_age_seconds*1e3+1}).filter(n=>Number.isFinite(n)&&n>e),r=this.model().arc?.expiresAt;Number.isFinite(r)&&r+1>e&&i.push(r+1),this.isConnected&&i.length&&(this._freshnessTimer=setTimeout(()=>this._scheduleRender(),Math.min(3e4,Math.max(1,Math.min(...i)-e))))}render(){let e=this.model(),i=this._coordinator.status,r=this.shadowRoot.activeElement?.getAttribute("data-focus"),n=this.shadowRoot.querySelector("[data-header-telemetry]")?.scrollLeft||0,o=this._draftMessage||(i.phase!=="idle"?i.message:this._registryStatus==="loading"?"Checking thermostat provider identity…":e.readiness.status==="locked"?"":e.reason||""),l=``+Ft({config:this.config,model:e,open:this._open,allowed:this._guard(e).allowed,lockAllowed:this._lockCapability().allowed,busy:this._coordinator.busy,draftValues:this._draftContext?{...e.targets,[this._targetKey(e)]:this._draft}:null,statusMessage:o,preview:this._isPreview(),animationOffset:this._animationOffset});if(l===this._viewHTML){this._renderDialog(),this._scheduleFreshness();return}this._view.innerHTML=l,this._viewHTML=l,this.shadowRoot.querySelector("[data-expand]")?.addEventListener("click",()=>{this._open=!this._open,this._activity(),this.render()});for(let s of this.shadowRoot.querySelectorAll("[data-details]"))s.onclick=()=>this._moreInfo();for(let s of this.shadowRoot.querySelectorAll("[data-info]"))s.onclick=()=>this._moreInfo(s.dataset.info);for(let s of this.shadowRoot.querySelectorAll("[data-device]"))this._bindDevice(s);for(let s of this.shadowRoot.querySelectorAll("[data-lock-controls]"))s.onclick=()=>this._openDialog("lock");for(let s of this.shadowRoot.querySelectorAll("[data-status-overview]"))s.onclick=()=>this._openDialog("overview");for(let s of this.shadowRoot.querySelectorAll("[data-presets]"))s.onclick=()=>this._openDialog("presets");for(let s of this.shadowRoot.querySelectorAll("[data-icon-settings]"))s.onclick=()=>this._openDialog("settings");for(let s of this.shadowRoot.querySelectorAll("[data-telemetry-topic]"))s.onclick=()=>this._openDialog("telemetry",s.dataset.telemetryKey||s.dataset.telemetryTopic);for(let s of this.shadowRoot.querySelectorAll("[data-adjust]"))s.onclick=()=>{let d=this.model(),p=s.dataset.adjust;this._temperature(d.targets[p]+Number(s.dataset.delta)*d.step,p),this._activity()};for(let s of this.shadowRoot.querySelectorAll("[data-preset]"))s.onclick=()=>{this._request("set_preset_mode",{preset_mode:s.dataset.preset}),this._activity()};for(let s of this.shadowRoot.querySelectorAll("[data-choice]"))s.onchange=()=>{let d=this.model().choices.find(p=>p.key===s.dataset.choice);d&&this._request(d.service,{[d.key]:s.value}),this._activity()};for(let s of this.shadowRoot.querySelectorAll("[data-marker]"))this._bindMarker(s,e);let a=this.shadowRoot.querySelector("[data-header-telemetry]");a&&(a.scrollLeft=n,a.onkeydown=s=>{if(s.target!==a)return;let d={ArrowLeft:-120,ArrowRight:120};s.key in d?(s.preventDefault(),a.scrollLeft+=d[s.key]):["Home","End"].includes(s.key)&&(s.preventDefault(),a.scrollLeft=s.key==="Home"?0:a.scrollWidth)},a.onfocusin=s=>{if(s.target===a||!s.target.matches(":focus-visible"))return;let d=a.getBoundingClientRect(),p=s.target.getBoundingClientRect();p.leftd.right&&(a.scrollLeft+=p.right-d.right)}),r&&this.shadowRoot.querySelector(`[data-focus="${CSS.escape(r)}"]`)?.focus({preventScroll:!0}),this._renderDialog(),this._scheduleFreshness()}};var Bi=new Set(["__proto__","prototype","constructor"]),lt={humidity:"Humidity",trend:"Temperature trend",valve:"Valve position",energy:"Energy",power:"Power",custom:"Custom telemetry",...Object.fromEntries(Object.entries(pe).map(([t,e])=>[t,e.label]))},Ee=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),xe=t=>t&&typeof t=="object"&&!Array.isArray(t);function J(t){return structuredClone(t)}function Gi(t,e,i){if(!Array.isArray(e)||!e.length||e.some(l=>Bi.has(String(l))))throw new Error("Invalid editor field.");let r=J(t),n=r;for(let l=0;l .grid, .section > .stack { margin-top:14px; } .grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; } .stack { display:grid; gap:14px; } .field { display:grid; align-content:start; gap:6px; min-width:0; font-size:14px; font-weight:550; } .field.wide { grid-column:1/-1; } .field small,.hint,.empty { color:var(--secondary-text-color,#566575); font-size:12px; font-weight:400; line-height:1.5; overflow-wrap:anywhere; } input,select,button { min-height:44px; font:inherit; color:inherit; border-radius:8px; } input,select { width:100%; min-width:0; padding:10px; border:1px solid var(--divider-color,#abb7c4); background:var(--input-fill-color,var(--secondary-background-color,#f4f6f8)); } input[aria-invalid=true] { border-color:var(--error-color,#b3261e); } input:focus-visible,select:focus-visible,button:focus-visible,summary:focus-visible { outline:3px solid var(--primary-color,#168cbb); outline-offset:2px; } input[type=checkbox] { width:22px; height:22px; min-height:22px; margin:0; accent-color:var(--primary-color,#168cbb); flex:none; } .check { display:flex; gap:10px; align-items:center; min-height:44px; font-size:14px; } .check span { overflow-wrap:anywhere; } button { cursor:pointer; padding:9px 14px; background:transparent; border:1px solid var(--divider-color,#abb7c4); font-size:13px; font-weight:600; } button:hover { background:var(--secondary-background-color,rgba(128,145,163,.12)); } button:disabled { cursor:default; opacity:.45; } .add { justify-self:start; color:var(--primary-color,#087aa9); } .actions { display:flex; gap:8px; flex-wrap:wrap; align-items:center; } .actions button { min-width:44px; } .remove { margin-left:auto; } details > summary { cursor:pointer; min-height:44px; display:list-item; padding:10px 2px; font-size:15px; font-weight:650; } details.section { padding-top:4px; padding-bottom:4px; } details[open].section { padding-bottom:16px; } .telemetry-row,.feature-row { display:grid; gap:12px; border:1px solid var(--divider-color,rgba(128,145,163,.28)); border-radius:10px; padding:14px; } .row-heading { display:flex; justify-content:space-between; gap:12px; align-items:center; font-size:14px; font-weight:650; } .preset-row { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:8px; align-items:end; } .status { padding:12px 14px; border-radius:9px; font-size:13px; line-height:1.5; overflow-wrap:anywhere; } .status[hidden] { display:none; } .status.error { color:var(--error-color,#b3261e); background:var(--error-background-color,rgba(179,38,30,.08)); border:1px solid currentColor; } .status.info { color:var(--secondary-text-color,#566575); background:var(--secondary-background-color,#f4f6f8); } @media(max-width:480px) { .grid { grid-template-columns:minmax(0,1fr); } .section { padding-left:12px; padding-right:12px; } } @media(prefers-reduced-motion:reduce) { * { scroll-behavior:auto; } } `,He=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._draft={},this._hass=null,this._sourceSignature=null,this._lastEmittedSignature=null,this._inventorySignature=null,this._error="",this._invalidField="",this._buffers=new Map,this._sections=new Map,this._newPreset={key:"",label:""},this._pendingInventory=!1}set hass(e){this._hass=e;let i=this._inventory();if(!(i===this._inventorySignature&&this.shadowRoot.childElementCount)){if(this._inventorySignature=i,this.shadowRoot.activeElement?.matches("input,select")){this._pendingInventory=!0;return}this._render()}}get hass(){return this._hass}setConfig(e){try{let i=J(e??{}),r=JSON.stringify(i);if(r===this._sourceSignature||r===this._lastEmittedSignature){this._sourceSignature=r;return}this._sourceSignature=r,this._lastEmittedSignature=null,this._buffers.clear(),this._draft=i;try{ae(i),this._error="",this._invalidField=""}catch(n){this._error=n.message}}catch{this._error="Card configuration must contain serializable values."}this._render()}connectedCallback(){this.shadowRoot.childElementCount||this._render()}_inventory(){let e=this._hass?.states??{},i=Object.keys(e).sort().map(n=>[n,e[n]?.attributes?.friendly_name]),r=e[this._draft?.entity]?.attributes?.preset_modes;return JSON.stringify([i,Array.isArray(r)?r:[]])}_capture(){for(let i of this.shadowRoot.querySelectorAll("details[data-section]"))this._sections.set(i.dataset.section,i.open);let e=this.shadowRoot.activeElement;return!e?.dataset.field&&!e?.dataset.action?null:{field:e.dataset.field,action:e.dataset.action,row:e.dataset.row,start:e.selectionStart,end:e.selectionEnd}}_restore(e,i){let r=i??e?.field,n=r?[...this.shadowRoot.querySelectorAll("[data-field]")].find(o=>o.dataset.field===r):e?.action?[...this.shadowRoot.querySelectorAll("[data-action]")].find(o=>o.dataset.action===e.action&&o.dataset.row===e.row):null;if(n&&(n.focus({preventScroll:!0}),!i&&e?.start!=null&&typeof n.setSelectionRange=="function"))try{n.setSelectionRange(e.start,e.end)}catch{}}_commit(e,{field:i,focus:r,open:n,rerender:o=!0}={}){i&&this._buffers.delete(i),this._draft=e;let l;try{l=ae(e),this._draft=J(l),this._error="",this._invalidField=""}catch(a){this._error=`${a.message} This draft has not been applied.`,this._invalidField=i??""}o?this._render({focus:r,open:n}):this._refreshFieldState(),l&&(this._lastEmittedSignature=JSON.stringify(l),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:J(l)},bubbles:!0,composed:!0})))}_change(e,i,r={}){try{this._commit(Gi(this._draft,e,i),{field:e.join("."),rerender:!1,...r})}catch(n){this._error=n.message,this._render()}}_refreshFieldState(){let e=this.shadowRoot.querySelector(".status.error");e&&(e.textContent=this._error,e.hidden=!this._error);for(let n of this.shadowRoot.querySelectorAll("[data-field]"))n.dataset.field===this._invalidField?n.setAttribute("aria-invalid","true"):n.removeAttribute("aria-invalid");let i=xe(this._draft.preset_labels)?this._draft.preset_labels:{};for(let n of this.shadowRoot.querySelectorAll('[data-action="remove-preset"]'))n.disabled=!Ee(i,n.dataset.row);let r=this.shadowRoot.querySelector('[data-action="clear-humidity-bands"]');r&&(r.disabled=this._draft.humidity_bands===void 0);for(let n of this.shadowRoot.querySelectorAll(".telemetry-row")){let o=Number(n.dataset.row),l=this._draft.telemetry?.[o]?.kind;n.querySelector(".row-heading").textContent=`Source ${o+1} · ${lt[l]||"Choose a kind"}`}}_field(e,i,r,{type:n="text",help:o,placeholder:l,options:a,min:s,max:d,step:p="any",list:c,wide:y=!1,onChange:g}={}){let h=v("label",`field${y?" wide":""}`);h.append(v("span","",e));let f=v(a?"select":"input");if(f.dataset.field=i,f.setAttribute("aria-label",e),a)for(let[x,u]of a){let _=v("option","",u);_.value=x,f.append(_)}else f.type=n,n==="number"&&(f.step=String(p)),l&&(f.placeholder=l),s!==void 0&&(f.min=String(s)),d!==void 0&&(f.max=String(d)),c&&f.setAttribute("list",c),f.addEventListener("input",()=>this._buffers.set(i,f.value));return f.value=this._buffers.has(i)?this._buffers.get(i):r==null?"":String(r),this._invalidField===i&&f.setAttribute("aria-invalid","true"),f.addEventListener("change",()=>{if(n==="number"&&f.validity.badInput){this._error="Enter a valid number. This draft has not been applied.",this._invalidField=i,this._refreshFieldState();return}let x=n==="number"?f.value===""?void 0:Number(f.value):f.value;g?.(x)}),f.addEventListener("blur",()=>{this._pendingInventory&&(this._pendingInventory=!1,queueMicrotask(()=>{this.shadowRoot.activeElement?.matches("input,select")||this._render()}))}),h.append(f),o&&h.append(v("small","",o)),h}_checkbox(e,i,r,n){let o=v("label","check"),l=v("input");return l.type="checkbox",l.checked=r,l.dataset.field=i,l.addEventListener("change",()=>n(l.checked)),o.append(l,v("span","",e)),o}_button(e,i,r,{row:n,disabled:o=!1,className:l="",title:a}={}){let s=v("button",l,e);return s.type="button",s.dataset.action=i,n!==void 0&&(s.dataset.row=String(n)),s.disabled=o,a&&s.setAttribute("aria-label",a),s.addEventListener("click",r),s}_details(e,i){let r=v("details","section");return r.dataset.section=e,r.open=this._sections.get(e)??!1,r.append(v("summary","",i)),r.addEventListener("toggle",()=>this._sections.set(e,r.open)),r}_render({focus:e,open:i}={}){let r=this._capture();i&&this._sections.set(i,!0);let n=v("style");n.textContent=Ki;let o=v("form");o.addEventListener("submit",h=>h.preventDefault()),o.append(v("p","intro","These settings belong to this card. Configuring or previewing it does not send heating commands."));let l=v("div","status error",this._error);l.setAttribute("role","alert"),l.hidden=!this._error,o.append(l);let a=v("section","section");a.append(v("h3","","Thermostat"));let s=v("div","grid"),d=this._hass?.states??{},p=Object.keys(d).filter(h=>h.startsWith("climate.")).sort(),c=[["","Select a thermostat"],...p.map(h=>[h,`${d[h]?.attributes?.friendly_name||h} · ${h}`])],y=typeof this._draft.entity=="string"?this._draft.entity:"";y&&!p.includes(y)&&c.push([y,`${y} · currently unavailable`]),s.append(this._field("Thermostat entity","entity",y,{options:c,wide:!0,onChange:h=>this._change(["entity"],h,{rerender:!0})})),s.append(this._field("Provider","adapter",this._draft.adapter??"auto",{options:[["auto","Automatic"],["standard","Standard climate"],["vtherm","Versatile Thermostat"]],help:"Detect the provider automatically, or choose it explicitly. Versatile Thermostat must report that it is ready before controls are enabled.",onChange:h=>this._change(["adapter"],h)})),s.append(this._field("Card name","name",this._draft.name,{placeholder:"Use thermostat name",onChange:h=>this._change(["name"],h||void 0)})),s.append(this._field("Background","background",this._draft.background??"blue",{options:[["blue","Midnight Blue"],["plum","Plum"],["black","Black"]],onChange:h=>this._change(["background"],h)}));let g=v("div","stack");g.append(this._checkbox("Show activity","show_activity",this._draft.show_activity!==!1,h=>this._change(["show_activity"],h)),this._checkbox("Show details","show_details",this._draft.show_details!==!1,h=>this._change(["show_details"],h))),s.append(g),a.append(s),p.length||a.append(v("p","status info",this._hass?"No climate entities are available. Add or enable a thermostat in Home Assistant, then select it here. This setup state sends no commands.":"Waiting for Home Assistant to provide the thermostat list.")),o.append(a),o.append(this._sourceMappings(d),this._responseArc(d),this._features(d),this._telemetry(d),this._presets(d),this._advanced()),o.append(v("p","hint","Save the card in Home Assistant when you are finished. Unknown advanced options are preserved.")),this.shadowRoot.replaceChildren(n,o),this._restore(r,e)}_sourceMappings(e){let i=this._details("sources","Temperature trend, humidity and valve sources"),r=v("div","stack");r.append(v("p","hint","Map the original card surfaces to explicit sensors. No entity names are guessed. If unset, a matching explicitly configured typed telemetry source can be used."));let n=v("datalist");n.id="ti-direct-source-entities";for(let l of Object.keys(e).filter(a=>a.startsWith("sensor.")).sort()){let a=v("option");a.value=l,a.label=e[l]?.attributes?.friendly_name||l,n.append(a)}r.append(n);let o=[["Optional trend entity","trend_entity","Measured temperature change with a reported temperature-per-time unit."],["Optional humidity entity","humidity_entity","A reported percentage, displayed independently of thermostat control."],["Optional TRV opening sensor (%)","valve_position_entity","Reported valve opening from 0 to 100%; this does not confirm heating."]];for(let[l,a,s]of o)r.append(this._field(l,a,this._draft[a],{list:n.id,placeholder:"No direct mapping",help:s,onChange:d=>this._change([a],d||void 0)}));return i.append(r),i}_responseArc(e){let i=this._details("response-arc","Temperature response"),r=v("div","stack");r.append(v("p","hint","Optional, read-only evidence for the Versatile Thermostat response arc. Orange combines an accepted heat request with recent measured warming; it does not confirm physical heat delivery. These mappings use the temperature trend source above and never send heating commands."));let n=xe(this._draft.response_arc)?this._draft.response_arc:{},o={};for(let a of["sensor","climate"]){let s=v("datalist");s.id=`ti-response-${a}-entities`,o[a]=s.id;for(let d of Object.keys(e).filter(p=>p.startsWith(`${a}.`)).sort()){let p=v("option");p.value=d,p.label=e[d]?.attributes?.friendly_name||d,s.append(p)}r.append(s)}r.append(this._field("Room temperature sensor","response_arc.room_sensor",n.room_sensor,{list:o.sensor,placeholder:"No response mapping",help:"The actual room-temperature source must agree with VT’s reading and temperature unit. Leave blank to disable this mapping.",onChange:a=>this._change(["response_arc","room_sensor"],a)})),r.append(v("p","hint","Choose the evidence route for this thermostat: native target confirmation, or confirmed TRV output with the valve opening sensor configured above. Missing evidence cannot produce an orange response."));let l=v("div","grid");for(let[a,s,d]of[["Native thermostat (target confirmation)","native_thermostat","Use the native thermostat whose reported target confirms VT’s regulated target. Leave blank for the TRV output route."],["Confirmed TRV output controller","confirmed_output","Use a controller exposing the supported TRV output-confirmation contract. This is not inferred from its name or an open valve."],["Optional heat-source thermostat","heat_source","Reads the mapped thermostat’s mode and reported activity. This does not control it or prove heat delivery."]])l.append(this._field(a,`response_arc.${s}`,n[s],{list:o.climate,placeholder:"No mapping",help:d,onChange:p=>this._change(["response_arc",s],p)}));return l.append(this._field("Response evidence freshness (minutes)","response_arc.freshness_minutes",n.freshness_minutes,{type:"number",min:0,max:30,placeholder:"10 minutes (default)",help:"Greater than 0, up to 30. Limits the age of the room value change and trend update; a shorter VT safety delay still applies. This does not change sensor reporting or heating.",onChange:a=>this._change(["response_arc","freshness_minutes"],a)})),r.append(l),i.append(r),i}_features(e){let i=Array.isArray(this._draft.features)?this._draft.features:[],r=this._details("features",`Custom telemetry & features${i.length?` · ${i.length}`:""}`),n=v("div","stack");n.append(v("p","hint","Choose named extras for the header and Controls & features. Associate a temperature reading with an exact provider preset to show its header indicator only when that preset is selected. Names and target matches never select presets."));let o=v("datalist");o.id="ti-feature-entities";for(let l of Object.keys(e).sort()){let a=v("option");a.value=l,a.label=e[l]?.attributes?.friendly_name||l,o.append(a)}return n.append(o),i.length||n.append(v("p","empty","No custom features configured.")),i.forEach((l,a)=>{let s=xe(l)?l:{},d=v("div","feature-row");d.dataset.row=String(a),d.append(v("div","row-heading",`Feature ${a+1}`));let p=v("div","grid"),c=[["Entity","entity",{list:o.id,placeholder:"Select or enter an entity"}],["Display label","label",{placeholder:"Use source name"}],["Attribute path","attribute",{placeholder:"Entity state",help:"Own-property paths only, such as safety_manager.safety_state."}],["Unit","unit",{placeholder:"Use reported state unit",help:"Supply attribute units explicitly. This setting does not convert values."}],["Icon","icon",{placeholder:"mdi:information-outline",help:"A named icon, such as mdi:thermometer."}]];for(let[u,_,b]of c)p.append(this._field(`Feature ${a+1} ${u.toLowerCase()}`,`features.${a}.${_}`,s[_],{...b,onChange:$=>this._change(["features",a,_],$===""&&_!=="entity"?void 0:$)}));let y=e[this._draft.entity]?.attributes?.preset_modes,g=Array.isArray(y)?y.filter(u=>typeof u=="string"&&u.trim()):[],h=[["","Unassociated"],...[...new Set(g)].map(u=>[u,u])];typeof s.preset=="string"&&!g.includes(s.preset)&&h.push([s.preset,`${s.preset} · not currently advertised`]),p.append(this._field(`Feature ${a+1} preset association`,`features.${a}.preset`,s.preset??"",{options:h,help:"The exact provider preset value. Requires a compatible numeric temperature source; this setting never selects a preset.",onChange:u=>this._change(["features",a,"preset"],u||void 0)})),d.append(p);let f=v("div","actions"),x=u=>{let _=J(this._draft);[_.features[a],_.features[a+u]]=[_.features[a+u],_.features[a]],this._buffers.clear(),this._commit(_,{focus:`features.${a+u}.entity`,open:"features"})};f.append(this._button("↑","move-feature-up",()=>x(-1),{row:a,disabled:a===0,title:`Move feature ${a+1} up`}),this._button("↓","move-feature-down",()=>x(1),{row:a,disabled:a===i.length-1,title:`Move feature ${a+1} down`}),this._button("Remove","remove-feature",()=>{let u=J(this._draft);u.features.splice(a,1),this._buffers.clear(),this._commit(u,{focus:u.features.length?`features.${Math.min(a,u.features.length-1)}.entity`:void 0,open:"features"}),u.features.length||this.shadowRoot.querySelector('[data-action="add-feature"]').focus({preventScroll:!0})},{row:a,className:"remove",title:`Remove feature ${a+1}`})),d.append(f),n.append(d)}),n.append(this._button("Add feature","add-feature",()=>{let l=J(this._draft),a=Array.isArray(l.features)?l.features:[];l.features=[...a,{entity:""}],this._commit(l,{focus:`features.${a.length}.entity`,open:"features"})},{className:"add"})),r.append(n),r}_telemetry(e){let i=Array.isArray(this._draft.telemetry)?this._draft.telemetry:[],r=this._details("telemetry",`Optional telemetry${i.length?` · ${i.length}`:""}`),n=v("div","stack");n.append(v("p","hint","Each source degrades independently. Leave a source blank to disable that row; labels and colours do not define thermostat policy."));let o=v("datalist");o.id="ti-telemetry-entities";for(let l of Object.keys(e).sort()){let a=v("option");a.value=l,a.label=e[l]?.attributes?.friendly_name||l,o.append(a)}return n.append(o),i.length||n.append(v("p","empty","No optional sources configured.")),i.forEach((l,a)=>{let s=xe(l)?l:{},d=v("div","telemetry-row");d.dataset.row=String(a),d.append(v("div","row-heading",`Source ${a+1} · ${lt[s.kind]||"Choose a kind"}`));let p=v("div","grid"),c=(h,f,x={})=>this._field(h,`telemetry.${a}.${f}`,s[f],{...x,onChange:u=>this._change(["telemetry",a,f],u===""&&f!=="entity"?void 0:u)});p.append(c(`Source ${a+1} kind`,"kind",{options:Ke.map(h=>[h,lt[h]])})),p.append(c(`Source ${a+1} entity`,"entity",{list:o.id,placeholder:"Select or enter an entity",help:"Suggestions come from Home Assistant; the configured ID is never guessed."})),p.append(c(`Source ${a+1} attribute`,"attribute",{placeholder:"Entity state",help:"Optional nested attribute path, such as power_manager.current_power."})),p.append(c(`Source ${a+1} label`,"label",{placeholder:"Use source name"})),p.append(c(`Source ${a+1} unit`,"unit",{placeholder:"Use reported unit",help:"For an attribute with no unit metadata. Known telemetry kinds still validate units."})),p.append(c(`Source ${a+1} maximum age (seconds)`,"max_age_seconds",{type:"number",min:0,placeholder:"No age limit",help:"Set only when the source has a known reporting interval. Old readings become stale."})),d.append(p);let y=v("div","actions"),g=h=>{let f=J(this._draft);[f.telemetry[a],f.telemetry[a+h]]=[f.telemetry[a+h],f.telemetry[a]],this._buffers.clear(),this._commit(f,{focus:`telemetry.${a+h}.kind`,open:"telemetry"})};y.append(this._button("↑","move-telemetry-up",()=>g(-1),{row:a,disabled:a===0,title:`Move source ${a+1} up`}),this._button("↓","move-telemetry-down",()=>g(1),{row:a,disabled:a===i.length-1,title:`Move source ${a+1} down`}),this._button("Remove","remove-telemetry",()=>{let h=J(this._draft);h.telemetry.splice(a,1),this._buffers.clear(),this._commit(h,{focus:h.telemetry.length?`telemetry.${Math.min(a,h.telemetry.length-1)}.kind`:void 0,open:"telemetry"}),h.telemetry.length||this.shadowRoot.querySelector('[data-action="add-telemetry"]').focus({preventScroll:!0})},{row:a,className:"remove",title:`Remove source ${a+1}`})),d.append(y),n.append(d)}),n.append(this._button("Add telemetry source","add-telemetry",()=>{let l=J(this._draft),a=Array.isArray(l.telemetry)?l.telemetry:[];l.telemetry=[...a,{kind:"custom",entity:""}],this._commit(l,{focus:`telemetry.${a.length}.entity`,open:"telemetry"})},{className:"add"})),r.append(n),r}_presets(e){let i=this._details("presets","Preset display labels"),r=v("div","stack");r.append(v("p","hint","Change display labels only. Service values remain provider-owned, including Frost where exposed. A matching target temperature never selects a preset."));let n=e[this._draft.entity]?.attributes?.preset_modes,o=Array.isArray(n)?n.filter(d=>typeof d=="string"):[],l=xe(this._draft.preset_labels)?this._draft.preset_labels:{},a=[...new Set([...o,...Object.keys(l)])];for(let d of a){let p=v("div","preset-row");p.append(this._field(`Label for ${d}`,`preset_labels.${d}`,l[d],{placeholder:d,help:o.includes(d)?"Provider preset value":"Saved mapping; not currently advertised by this provider.",onChange:c=>this._change(["preset_labels",d],c||void 0)})),p.append(this._button("Reset","remove-preset",()=>this._change(["preset_labels",d],void 0,{focus:o.includes(d)?`preset_labels.${d}`:"new-preset-key",rerender:!0}),{row:d,disabled:!Ee(l,d),title:`Reset label for ${d}`})),r.append(p)}a.length||r.append(v("p","empty","The selected thermostat has not advertised any presets. You can add a display mapping for a known provider value."));let s=v("div","grid");return s.append(this._field("Provider preset value","new-preset-key",this._newPreset.key,{placeholder:"Exact provider value",onChange:d=>{this._newPreset.key=d}})),s.append(this._field("Display label","new-preset-label",this._newPreset.label,{placeholder:"Display label",onChange:d=>{this._newPreset.label=d}})),r.append(s),r.append(this._button("Add preset label","add-preset",()=>{let d=(this._buffers.get("new-preset-key")??this._newPreset.key).trim(),p=(this._buffers.get("new-preset-label")??this._newPreset.label).trim();if(!d||!p){this._error="Enter both the exact provider preset value and its display label.",this._render({focus:d?"new-preset-label":"new-preset-key",open:"presets"});return}this._newPreset={key:"",label:""},this._buffers.delete("new-preset-key"),this._buffers.delete("new-preset-label"),this._change(["preset_labels",d],p,{focus:`preset_labels.${d}`,open:"presets",rerender:!0})},{className:"add"})),i.append(r),i}_advanced(){let e=this._details("advanced","Advanced display settings"),i=v("div","stack");i.append(this._field("Temperature step override","step",this._draft.step,{type:"number",min:0,placeholder:"Use provider step",help:"Used only when the provider omits its step. The provider limits and reported step remain authoritative.",onChange:a=>this._change(["step"],a)}));let r=xe(this._draft.humidity_bands)?this._draft.humidity_bands:{...be},n=Ee(r,"low")||Ee(r,"high"),o=v("div","grid"),l={dry_below:"Too dry below",good_min:"Good range minimum",good_max:"Good range maximum",wet_above:"Too wet above",low:"Legacy low",high:"Legacy high"};for(let a of n?["low","high"]:ee)o.append(this._field(`Humidity ${l[a].toLowerCase()} (%)`,`humidity_bands.${a}`,r[a],{type:"number",min:0,max:100,step:n?"any":1,onChange:s=>{let d=this._draft.humidity_bands===void 0?{...be}:this._draft.humidity_bands;this._change(["humidity_bands"],{...d,[a]:s},{open:"advanced"})}}));return i.append(v("p","hint",n?"Legacy two-bound format is preserved explicitly. Reset to source defaults to use the original five display bands. These are local display guides, not HI targets or control thresholds.":"Original five-band humidity display guides default to 30, 40, 60 and 70%. All four ordered integer boundaries must be valid. These are local display guides, not HI targets or control thresholds.")),i.append(o),i.append(this._button("Reset humidity guides to source defaults","clear-humidity-bands",()=>{let a={...this._draft.humidity_bands??{}};for(let s of[...ee,"low","high"])delete a[s];this._change(["humidity_bands"],Object.keys(a).length?{...a,...be}:void 0,{open:"advanced",rerender:!0,focus:"humidity_bands.dry_below"})},{disabled:this._draft.humidity_bands===void 0})),e.append(i),e}};var Zt="0.1.0-beta.4";for(let[t,e]of[[Te,Ue],[ue,He]]){let i=customElements.get(t);i?i.tiVersion!==Zt&&console.warn(`Thermal Intelligence: ${t} is already registered by another version. Check duplicate resources and refresh the browser.`):(e.tiVersion=Zt,customElements.define(t,e))}window.customCards??=[];window.customCards.some(t=>t.type===Te)||window.customCards.push({type:Te,name:"Thermal Intelligence Thermostat · Beta",description:"A capability-aware thermostat with a visual editor. Beta; installs alongside existing Thermal Intelligence cards.",preview:!0,documentationURL:"https://github.com/senyo888/thermal-intelligence#readme"});