This commit is contained in:
2026-01-30 23:31:00 -06:00
commit a39095b3de
2665 changed files with 263970 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import{d_ as e,l as t,ex as i,eK as a,ew as n,eE as s,eA as r,eI as o,dZ as c,eF as l,ef as u,dV as h,eC as _,eG as g,eD as d,e0 as m,eJ as p}from"./card-b98d578d.js";import{B as y,i as f}from"./engine-browse-media-e35cac7a.js";import{C}from"./engine-86b0096c.js";import{E as w}from"./entity-camera-94c8dadf.js";import{g as k}from"./engine-generic-75ad9390.js";import{e as D}from"./endOfDay-8eba624c.js";import"./live-provider-845afb16.js";const M=t=>e(t,(e=>e._metadata?.startDate),"desc");class b extends n{}class E extends w{constructor(){super(...arguments),this._reolinkHostID=null,this._reolinkCameraUID=null,this._reolinkChannel=null,this._ptzEntities=null}async initialize(e){return super.initialize(e)}async _getChannelFromConfigurationURL(e,t){const i=this._entity?.device_id;if(!i)return null;const a=await t.getDevice(e,i);if(!a?.configuration_url)return null;try{const e=new URL(a.configuration_url),t=Number(e.searchParams.get("ch"));return isNaN(t)?null:t}catch{return null}}async _initializeChannel(e,i){const a=this._entity?.unique_id,n=a?String(a).match(/^(?<hostid>[^_]+)_(?<channel_or_uid>[^_]+)_/):null,s=n?.groups?.hostid??null,r=n?.groups?.channel_or_uid??null;if(null===s||null===r)throw new b(t("error.camera_initialization_reolink"),this.getConfig());const o=Number(r),c=!isNaN(o)&&o<=999,l=(c?o:null)??await this._getChannelFromConfigurationURL(e,i)??0,u=c?null:r;this._reolinkChannel=l,this._reolinkHostID=s,this._reolinkCameraUID=u}async _initialize(e){await this._initializeChannel(e.hass,e.deviceRegistryManager),this._ptzEntities=await this._getPTZEntities(e.hass,e.entityRegistryManager)}_getUIEndpoint(e){return this._config.reolink?.url?{endpoint:this._config.reolink.url}:null}async _getRawCapabilities(e){const t=k(this.getConfig()),i=this._ptzEntities?this._entitiesToCapabilities(e.hass,this._ptzEntities):null,a=t||i?{...i,...t}:null;return{...await super._getRawCapabilities(e),clips:!0,...a&&{ptz:a}}}_entitiesToCapabilities(e,t){const a={};for(const e of Object.keys(t))switch(e){case"left":case"right":case"up":case"down":a[e]=[i.Continuous];break;case"zoom_in":a.zoomIn=[i.Continuous];break;case"zoom_out":a.zoomOut=[i.Continuous]}const n=t?.presets?e.states[t.presets]:null;
/* istanbul ignore next: this path cannot be reached as ptzEntities will
always have contents when this function is called -- @preserve */
return Array.isArray(n?.attributes.options)&&(a.presets=n.attributes.options),Object.keys(a).length?a:null}async _getPTZEntities(e,t){
/* istanbul ignore next: this path cannot be reached as an exception is
thrown in initialize() if this value is not found -- @preserve */
if(!this._reolinkHostID)return null;const i=this._getPTZEntityUniqueIDPrefix(),a=await t.getMatchingEntities(e,(e=>e.config_entry_id===this._entity?.config_entry_id&&!!e.unique_id&&String(e.unique_id).startsWith(i)&&!e.disabled_by)),n=a.filter((e=>e.entity_id.startsWith("button."))),s=a.filter((e=>e.unique_id===`${i}ptz_preset`&&e.entity_id.startsWith("select."))),r=["stop","left","right","up","down","zoom_in","zoom_out"],o={};for(const e of n)for(const t of r)e.unique_id&&String(e.unique_id).endsWith(t)&&(o[t]=e.entity_id);return 1===s.length&&(o.presets=s[0].entity_id),Object.keys(o).length?o:null}getChannel(){return this._reolinkChannel}_getPTZEntityUniqueIDPrefix(){return`${this._reolinkHostID}_${this._reolinkCameraUID??this._reolinkChannel}_`}getProxyConfig(){return{...super.getProxyConfig(),media:"auto"===this._config.proxy.media||this._config.proxy.media,ssl_verification:"auto"!==this._config.proxy.ssl_verification&&this._config.proxy.ssl_verification,ssl_ciphers:"auto"===this._config.proxy.ssl_ciphers?"intermediate":this._config.proxy.ssl_ciphers}}async executePTZAction(e,t,i){if(await super.executePTZAction(e,t,i))return!0;if(!this._ptzEntities)return!1;if("preset"===t){const t=this._ptzEntities.presets,n=i?.preset;return!(!n||!t)&&(await e.executeActions({actions:[a("select",t,n)]}),!0)}const n="start"===i?.phase?this._ptzEntities[t]:"stop"===i?.phase?this._ptzEntities.stop:null;return!!n&&(await e.executeActions({actions:[{action:"perform-action",perform_action:"button.press",target:{entity_id:n}}]}),!0)}}class z{static isReolinkEventQueryResults(e){return e.engine===r.Reolink&&e.type===d.Event}}class x extends y{constructor(e,t,i,a,n,r,o){super(e,i,a,n,r,o),this._camerasCache=new s,this._cache=new s,this._deviceRegistryManager=t}getEngineType(){return r.Reolink}_reolinkFileMetadataGenerator(e,t,i){
/* istanbul ignore next: This situation cannot happen as the directory would
be rejected by _reolinkDirectoryMetadataGenerator if there was no start date
-- @preserve */
if(!i?._metadata?.startDate||t.media_class!==o)return null;const a=t.title.split(/ +/),n=c(a[0],"HH:mm:ss",i._metadata.startDate);if(!l(n))return null;const s=a.length>1?a[1].match(/(?<hours>\d+):(?<minutes>\d+):(?<seconds>\d+)/):null,r=s?.groups?{hours:Number(s.groups.hours),minutes:Number(s.groups.minutes),seconds:Number(s.groups.seconds)}:null,h=a.length>2?a.splice(2).map((e=>e.toLowerCase())).sort():null;return{cameraID:e,startDate:n,endDate:r?u(n,r):n,...h&&{what:h}}}_reolinkDirectoryMetadataGenerator(e,t){const i=c(t.title,"yyyy/M/d",new Date);return l(i)?{cameraID:e,startDate:h(i),endDate:D(i)}:null}_reolinkCameraMetadataGenerator(e){const t=e.media_content_id.match(/^media-source:\/\/reolink\/CAM\|(?<configEntryID>.+)\|(?<channel>\d+)$/);return t?.groups?{configEntryID:t.groups.configEntryID,channel:Number(t.groups.channel)}:null}async createCamera(e,t){const i=new E(t,this,{eventCallback:this._eventCallback});return await i.initialize({entityRegistryManager:this._entityRegistryManager,deviceRegistryManager:this._deviceRegistryManager,hass:e,stateWatcher:this._stateWatcher})}async _getMatchingDirectories(e,t,i,a){const n=t.getConfig(),s=t.getEntity(),r=s?.config_entry_id;if(null===t.getChannel()||!r)return null;const o=await this._browseMediaWalker.walk(e,[{targets:["media-source://reolink"],metadataGenerator:(e,t)=>this._reolinkCameraMetadataGenerator(e),matcher:e=>e._metadata?.channel===t.getChannel()&&e._metadata?.configEntryID===r}],{...!1!==a?.useCache&&{cache:this._camerasCache}});return o?.length?await this._browseMediaWalker.walk(e,[{targets:[`media-source://reolink/RES|${r}|${t.getChannel()}|`+("low"===n.reolink?.media_resolution?"sub":"main")],metadataGenerator:(e,i)=>this._reolinkDirectoryMetadataGenerator(t.getID(),e),matcher:e=>e.can_expand&&f(e,i?.start,i?.end),sorter:e=>M(e)}],{...!1!==a?.useCache&&{cache:this._cache}}):null}async getEvents(t,i,a,n){if(a.favorite||a.tags?.size||a.what?.size||a.where?.size||a.hasSnapshot)return null;const s=new Map,o=async o=>{const c={...a,cameraIDs:new Set([o])},l=n?.useCache??1?this._requestCache.get(c):null;if(l)return void s.set(c,l);const u=i.getCamera(o),h=u&&u instanceof E?await this._getMatchingDirectories(t,u,c,n):null,_=c.limit??C;let g=[];h?.length&&(g=await this._browseMediaWalker.walk(t,[{targets:h,concurrency:1,metadataGenerator:(e,t)=>this._reolinkFileMetadataGenerator(o,e,t),earlyExit:e=>e.length>=_,matcher:e=>!e.can_expand&&f(e,c.start,c.end),sorter:e=>M(e)}],{...!1!==n?.useCache&&{cache:this._cache}}));const m=e(g,(e=>e._metadata?.startDate),"desc").slice(0,_),p={type:d.Event,engine:r.Reolink,browseMedia:m};(n?.useCache??1)&&this._requestCache.set(c,{...p,cached:!0},p.expiry),s.set(c,p)};return await _(a.cameraIDs,(e=>o(e))),s}generateMediaFromEvents(e,t,i,a){return z.isReolinkEventQueryResults(a)?g(a.browseMedia):null}async getMediaMetadata(e,t,i,a){const n=new Map,s=a?.useCache??1?this._requestCache.get(i):null;if(s)return n.set(i,s),n;const o=new Set,c=async i=>{const n=t.getCamera(i);if(!(n&&n instanceof E))return;const s=await this._getMatchingDirectories(e,n,null,a);for(const e of s??[])
/* istanbul ignore next: This situation cannot happen as the directory
will not match without metadata -- @preserve */
e._metadata?.startDate&&o.add(m(e._metadata.startDate))};await _(i.cameraIDs,(e=>c(e)));const l={type:d.MediaMetadata,engine:r.Reolink,metadata:{...o.size&&{days:o}},expiry:u(new Date,{seconds:p}),cached:!1};return(a?.useCache??1)&&this._requestCache.set(i,{...l,cached:!0},l.expiry),n.set(i,l),n}getCameraMetadata(e,t){return{...super.getCameraMetadata(e,t),engineIcon:"reolink"}}}export{x as ReolinkCameraManagerEngine,z as ReolinkQueryResultsClassifier};