mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Пересборка кандидата на голове dev после слияния #669–#673 и #675: бандл из текущих исходников, снимок STATUS и строка текущего цикла. Issue: #663 Issue: #664 Issue: #665 Issue: #666 Issue: #667 Issue: #669 Issue: #670 Issue: #671 Issue: #672 Issue: #673 Issue: #675 User-Visible: no Release: v1.78.0-beta.5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2 lines
11 KiB
JavaScript
2 lines
11 KiB
JavaScript
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d06c84d556e989bcf1e9fa8c0eb28b72de2cbf0fe7a8c35051d3497e78d9cf96";import{s as e,aM as n}from"./houseplan-card-Ddo22-SB.js";import{n as t}from"./namespace-language-CwX4Ljv1.js";var o={title:"Zigbee links",toggle:"Show Zigbee links when hovering over a device",hint:"Shows observed direct neighbours only. Nothing is fetched while you hover.",help:"Shows which Zigbee devices each device talks to directly. Line colour is link quality (LQI) on the same scale as the device's LQI reading: red at 40 and below, green at 180 and above. A dashed line means quality was not reported. An arrow points to the next device on the way to the coordinator: one arrow leads out, incoming arrows are the devices that route through this one. A line without an arrow is a spare neighbour. This is the route tree House Plan builds, not the path a packet takes right now: a label at the end of an arrow means its target is not on this plan, and no arrow at all means the route is unknown. Data comes from the last load and goes stale; links are shown with a mouse only.",help_aria:"Help: Zigbee links",admin_only:"Topology data is available to Home Assistant administrators only.",save_first:"Save this setting before loading topology data.",zha:"ZHA",zha_read:"Read ZHA data",zha_hint:"Reads ZHA's cached topology; it does not start a radio scan.",z2m:"Zigbee2MQTT",z2m_topics:"Base topics (one per line)",z2m_update:"Update map",z2m_warning:"A network-map scan can take 10 seconds to 2 minutes and may temporarily reduce Zigbee responsiveness.",status_idle:"Data not loaded",status_loading:"Loading…",status_ready:"Received {time}",status_stale:"Received {time} · stale",status_partial:"Received {time} · some nodes were omitted",status_no_links:"Received {time} · no observed links found",error_permission:"Administrator permission is required.",error_unsupported:"This provider or Home Assistant API is unavailable.",error_timeout:"The provider did not respond in time.",error_invalid_topic:"Check the Zigbee2MQTT base topic.",error_invalid_payload:"The provider returned unsupported topology data.",error_provider:"Topology data could not be loaded.",remote_count:"+{n} in other spaces",route_device_not_on_plan:"device is not on the plan",route_coordinator_not_on_plan:"coordinator is not on the plan",route_other_space:"another space"};const i=e=>new URL(`${e}?retry`,import.meta.url).href,r=t(o,{ru:e=>0===e?import("./topology-ru-Di7X71yN.js"):import(i("./topology-ru-Di7X71yN.js")),de:e=>0===e?import("./topology-de-BjBB8qiK.js"):import(i("./topology-de-BjBB8qiK.js")),fr:e=>0===e?import("./topology-fr-DXFxoYd-.js"):import(i("./topology-fr-DXFxoYd-.js"))});function s(e,n){const t=r.dictionary(e)?.[n]??o[n];return"string"==typeof t&&t.trim().length>0}function a(n,t,i){return e(r.dictionary(n)?.[t]??o[t]??t,i)}const d=3e5,c=2097152,l=6e3;function u(e){return null!==e&&"object"==typeof e?e:null}function p(e){if("string"!=typeof e&&"number"!=typeof e)return null;let n=String(e).trim().toLowerCase();return n=/^0x[0-9a-f]{16}$/.test(n)?n.slice(2):n.replace(/[:-]/g,""),/^[0-9a-f]{16}$/.test(n)?n:null}function f(e,n,t){return`${e}:${n}:${t}`}function h(e){const n=String(e||"").toLowerCase().replace(/[ _-]/g,"");return n.includes("coordinator")?"coordinator":n.includes("router")?"router":n.includes("enddevice")||"end"===n?"end":"unknown"}const g=["parent","child","sibling","none","previous_child"];function _(e){const n=u(e),t=function(e){const n=Number(e);return Number.isFinite(n)&&n>=0&&n<=255?Math.round(n):void 0}(n?.lqi??n?.linkquality??n?.link_quality),o=function(e){if("number"==typeof e)return g[e];if("string"!=typeof e)return;const n=e.trim().toLowerCase().replace(/[\s_-]+/g,"");return"previouschild"===n?"previous_child":n.slice(0,40)||void 0}(n?.relationship);return{...void 0===t?{}:{lqi:t},...o?{relationship:o}:{}}}function v(e,n){return Array.isArray(e)&&e.length<=n?e:null}function y(e){if("string"==typeof e)return e.length<=c;try{return JSON.stringify(e).length<=c}catch{return!1}}function b(e,n,t,o,i){if(t===o)return void n.push({code:"self_link",nodeKey:t});const r=t<o,s=r?`${t}|${o}`:`${o}|${t}`,a=e.get(s)||{a:r?t:o,b:r?o:t},d=r?"aToB":"bToA";a[d]?n.push({code:"duplicate_link",nodeKey:t}):a[d]=_(i),e.set(s,a)}function m(e,n=Date.now()){const t=[],o=y(e)?v(e,1e3):null;if(!o)return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const i=new Map,r=new Map;for(const e of o){const n=u(e),o=p(n?.ieee??n?.ieee_address);if(!o){t.push({code:"invalid_payload"});continue}const r=f("zha","zha",o);i.set(r,{key:r,ieee:o,..."string"==typeof n?.device_reg_id?{deviceId:n.device_reg_id}:{},role:h(n?.device_type??n?.type),..."boolean"==typeof n?.available?{available:n.available}:{}})}for(const e of o){const n=u(e),o=p(n?.ieee??n?.ieee_address);if(!o)continue;const s=f("zha","zha",o),a=v(n?.neighbors,l);if(a)for(const e of a){if(r.size>=l){t.push({code:"invalid_payload"});break}const n=u(e),o=p(n?.ieee??n?.ieee_address);if(!o){t.push({code:"invalid_payload",nodeKey:s});continue}const a=f("zha","zha",o);i.has(a)||i.set(a,{key:a,ieee:o,role:h(n?.device_type)}),b(r,t,s,a,e)}else null!=n?.neighbors&&t.push({code:"invalid_payload",nodeKey:s})}return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[...i.values()],links:[...r.values()],warnings:t}}function k(e,n){const t=u(e),o=p(t?.ieeeAddr??t?.ieee_address??t?.ieee??e);if(o)return o;for(const o of[t?.id,t?.networkAddress,t?.network_address,e])if(null!=o){const e=n.get(String(o));if(e)return e}return null}function w(e,t,o=Date.now()){const i=[],r=n(t)||"invalid",s=y(e)?function(e){let n=e;if("string"==typeof n){if(n.length>c)return null;try{n=JSON.parse(n)}catch{return null}}const t=u(n),o=u(t?.data);if(n=o?.value??t?.value??t?.data??n,"string"==typeof n){if(n.length>c)return null;try{n=JSON.parse(n)}catch{return null}}return n}(e):null,a=u(s),d=v(a?.nodes,1e3),g=v(a?.links,l);if(!d||!g)return{provider:"z2m",instanceId:r,obtainedAt:o,freshness:"fresh-scan",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const _=new Map,m=new Map;for(const e of d){const n=u(e),t=p(n?.ieeeAddr??n?.ieee_address??n?.ieee);if(!t){i.push({code:"invalid_payload"});continue}const o=f("z2m",r,t);_.set(o,{key:o,ieee:t,role:h(n?.type??n?.device_type),..."boolean"==typeof n?.failed?{available:!n.failed}:{}});for(const e of[n?.id,n?.networkAddress,n?.network_address])null!=e&&m.set(String(e),t)}const w=new Map;for(const e of g){const n=u(e),t=p(n?.sourceIeeeAddr)??k(n?.source,m),o=p(n?.targetIeeeAddr)??k(n?.target,m);if(!t||!o){i.push({code:"invalid_payload"});continue}const s=f("z2m",r,t),a=f("z2m",r,o);_.has(s)||_.set(s,{key:s,ieee:t,role:"unknown"}),_.has(a)||_.set(a,{key:a,ieee:o,role:"unknown"}),b(w,i,s,a,e)}return{provider:"z2m",instanceId:r,obtainedAt:o,freshness:"fresh-scan",nodes:[..._.values()],links:[...w.values()],warnings:i}}function A(e){if("string"!=typeof e)return null;const n=p(e);if(n)return n;const t=e.toLowerCase().match(/^zigbee2mqtt(?:_bridge)?_(.+)$/);return t?p(t[1]):null}function S(e,n){if(e.deviceId&&n.devices[e.deviceId])return[e.deviceId];const t=new Set;for(const[o,i]of Object.entries(n.devices)){const n=u(i);(Array.isArray(n?.identifiers)?n.identifiers:[]).some(n=>Array.isArray(n)&&A(n[1])===e.ieee)&&t.add(o)}if(t.size)return[...t];for(const o of Object.values(n.entities)){const n=u(o);"string"==typeof n?.device_id&&"string"==typeof n.unique_id&&(A(n.unique_id)===e.ieee&&t.add(n.device_id))}return[...t]}function T(e){return!e.hidden&&!e.virtual&&"ha_disabled"!==e.bindingStatus?.kind&&"orphaned"!==e.bindingStatus?.kind&&"unverified"!==e.bindingStatus?.kind}function z(e){const n=new Map,t=new Map,o=e.nodes.filter(e=>"coordinator"===e.role).map(e=>e.key).sort();if(1!==o.length)return{distances:n,parents:t};const i=o[0],r=function(e){const n=new Set(e.nodes.map(e=>e.key)),t=new Map;for(const e of[...n].sort())t.set(e,[]);for(const o of[...e.links].sort((e,n)=>`${e.a}\0${e.b}`.localeCompare(`${n.a}\0${n.b}`)))n.has(o.a)&&n.has(o.b)&&(t.get(o.a).push({neighborKey:o.b,observation:o.aToB}),t.get(o.b).push({neighborKey:o.a,observation:o.bToA}));for(const e of t.values())e.sort((e,n)=>e.neighborKey.localeCompare(n.neighborKey));return t}(e);if(!r.has(i))return{distances:n,parents:t};n.set(i,0);const s=[i];for(let e=0;e<s.length;e++){const t=s[e],o=n.get(t)+1;for(const{neighborKey:e}of r.get(t)||[])n.has(e)||(n.set(e,o),s.push(e))}for(const e of[...n.keys()].sort()){const o=n.get(e);if(0===o)continue;const i=(r.get(e)||[]).filter(({neighborKey:e})=>n.get(e)===o-1).sort((e,n)=>{const t="parent"===e.observation?.relationship?1:0,o="parent"===n.observation?.relationship?1:0;if(t!==o)return o-t;const i=e.observation?.lqi??-1;return(n.observation?.lqi??-1)-i||e.neighborKey.localeCompare(n.neighborKey)});i[0]&&t.set(e,i[0].neighborKey)}return{coordinatorKey:i,distances:n,parents:t}}function I(e,n,t){const o=new Map,i=[];for(const r of e.nodes){if(!1===r.available){i.push({code:"provider_scan_failure",nodeKey:r.key});continue}const e=S(r,t);if(1!==e.length){i.push({code:"unmatched_device",nodeKey:r.key});continue}const s=e[0];let a=n.filter(e=>T(e)&&"device"===e.bindingKind&&e.bindingRef===s);a.length||(a=n.filter(e=>T(e)&&"entity"===e.bindingKind&&!!e.bindingRef&&t.entities[e.bindingRef]?.device_id===s)),1===a.length?o.set(r.key,{markerId:a[0].id,space:a[0].space}):i.push({code:a.length>1?"ambiguous_placement":"unmatched_device",nodeKey:r.key})}return{placements:o,warnings:i}}function R(e,n,t){return e.map(e=>({topology:e,placements:I(e,n,t).placements,routes:z(e)}))}function q(e,n,t){const o=new Map,i=new Set,r=new Map;let s=0;for(const{topology:a,placements:d,routes:c}of e){const e=new Set([...d].filter(([,e])=>e.markerId===t&&e.space===n).map(([e])=>e));if(e.size){for(const t of e){const e=c.parents.get(t);if(!e)continue;const o=d.get(e);if(o?.space===n)continue;let i;i=o?{kind:"remote-space",spaceId:o.space}:"coordinator"===a.nodes.find(n=>n.key===e)?.role?{kind:"unplaced-coordinator"}:{kind:"unplaced-device"};const s="remote-space"===i.kind?`${i.kind}:${i.spaceId}`:i.kind;r.set(s,i)}for(const r of a.links){const a=e.has(r.a),l=e.has(r.b);if(!a&&!l)continue;const u=a?r.a:r.b,p=a?r.b:r.a,f=d.get(p);if(!f||f.markerId===t){s++;continue}const h=c.parents.get(u)===p;if(f.space!==n){h||i.add(f.markerId);continue}const g=a?r.aToB:r.bToA,_=h?"toward-neighbor":c.parents.get(p)===u?"toward-origin":void 0,v=o.get(f.markerId);v?(void 0===v.line.lqi&&void 0!==g?.lqi&&(v.line.lqi=g.lqi),_&&v.directions.add(_)):o.set(f.markerId,{line:{neighborMarkerId:f.markerId,lqi:g?.lqi},directions:new Set(_?[_]:[])})}}}return{lines:[...o.values()].map(({line:e,directions:n})=>1===n.size?{...e,routeDirection:[...n][0]}:e),remoteCount:i.size,omittedCount:s,parentTargets:[...r.values()]}}export{d as T,r as a,R as b,w as c,s as h,I as m,m as n,q as r,a as t};
|