UNPKG

philtv-js

Version:

A simple library to pair and interact with Philips TV

2 lines 7.2 kB
import {b as b$1,a as a$1}from'./chunk-P3LNB3MT.js';import {c as c$1,b,a,d}from'./chunk-JDMABWTO.js';import {randomBytes,createHmac}from'crypto';import {get}from'radash';import*as c from'superstruct';import {z}from'zod';function A(o,t){let e=createHmac("sha1",o);return e.write(t),e.end(),e.read("binary").toString("base64")}async function _(o){let t=b();try{let{data:e}=await t.request(`${o}/system`,{dataType:"json",timeout:2e3,signal:AbortSignal.timeout(2e3)}),{api_version:{Major:i},featuring:{systemfeatures:r}}=e,s=r.secured_transport==="true",n=r.pairing_type==="digest_auth_pairing";return [void 0,{apiVersion:i,systemFeatures:r,isSecureTransport:s,isGoodPairingType:n,isReady:s&&n}]}catch(e){return [e,void 0]}}var h=class{tvBase;deviceId;apiUrls;deviceInformation;httpClients;startPairingResponse;credentials;apiVersion;pairingRequestUrl;constructor(t){this.tvBase=t,this.apiUrls={secure:`https://${this.tvBase.tvIp}:${this.tvBase.apiPort||1926}`},this.deviceId=randomBytes(16).toString("hex"),this.deviceInformation=c$1(this.deviceId);}async init(){let[t,e]=await _(this.apiUrls.secure);return t||(this.apiVersion=e?.apiVersion,this.pairingRequestUrl=`${this.apiUrls.secure}/${this.apiVersion}/pair/request`),[t,e]}async startPairing(){let t=b(),{data:e}=await t.request(this.pairingRequestUrl,{method:"POST",data:{device:this.deviceInformation,scope:["read","write","control"]},dataType:"json"}),{timestamp:i,auth_key:r,timeout:s,error_id:n,error_text:a$1}=e;return n!=="SUCCESS"?[new Error(`Failed to start pairing: ${a$1}`),void 0]:(this.startPairingResponse={authTimestamp:i,authKey:r,timeout:s},this.credentials={user:this.deviceId,password:r},this.httpClients={digest:a({user:this.credentials.user,password:this.credentials.password,baseUrl:`${this.apiUrls.secure}/${this.apiVersion}`})},[void 0,this.startPairingResponse])}async completePairing(t){if(!t)return [new Error("Failed to complete pairing"),void 0];if(this.startPairingResponse){let e=Buffer.from(b$1,"base64"),i=this.startPairingResponse?.authTimestamp+t,r=A(e,i),s={device:this.deviceInformation,auth:{auth_AppId:"1",pin:t,auth_timestamp:this.startPairingResponse.authTimestamp,auth_signature:r}};if(!this.httpClients.digest)return [new Error("No digest client"),void 0];let[n,a]=await this.httpClients.digest.request("pair/grant",{method:"POST",data:s,retry:0});return n?[n,void 0]:a?.error_id!=="SUCCESS"?[new Error(`Failed to complete pairing: ${a?.error_text}`),void 0]:[void 0,{user:this.credentials.user,password:this.credentials.password,apiUrl:this.apiUrls.secure,apiVersion:this.apiVersion,fullApiUrl:`${this.apiUrls.secure}/${this.apiVersion}`}]}return [new Error("Failed to complete pairing"),void 0]}};var g=class{digestClient;constructor(t){this.digestClient=a({user:t.user,password:t.password,baseUrl:t.apiUrl});}getDigestClient(){return this.digestClient}renderResponse(t,e){return [t,e]}async getMenuStructure(t={flat:false}){let[e,i,r]=await this.digestClient.request("menuitems/settings/structure");return e?[e,void 0]:t.flat?[void 0,d(i.node),r]:[void 0,i,r]}async getMenuStructureItem(t,e){let[i,r]=await this.getMenuStructure({flat:true}),s=t?"context":"node_id",n=t||e;return i?[i,void 0]:[void 0,r.find(a=>a?.[s]===n)]}async getMenuStructureItemByContext(t){return this.getMenuStructureItem(t,void 0)}async getMenuStructureItemByNodeId(t){return this.getMenuStructureItem(void 0,t)}async setMenuItemSetting(t,e){if(!t)return [new Error("Item not found"),void 0];let[i,r,s]=await this.digestClient.request("menuitems/settings/update",{method:"POST",data:{values:[{value:{Nodeid:t.node_id,data:e}}]}}),n={status:s?.status,statusText:s?.statusText,body:r,item:t,originalResponse:s};return [i,n]}async handleSetMenuItemSetting(t,e){let[i,r]=await this.getMenuStructureItemByContext(t),[s,n]=await this.setMenuItemSetting(r,e);return [i||s,n]}};var m=class extends g{async getSystem(){let[t,e]=await this.digestClient.request("system");return t?this.renderResponse(t,void 0):[void 0,{...e,fullApiVersion:`${e.api_version.Major}.${e.api_version.Minor}.${e.api_version.Patch}`}]}async setAmbilightBrightness(t){let e=c.enums(a$1.ambilight.ambilightBrightnessAvailableValues),[i]=e.validate(t);return i?this.renderResponse(i,void 0):this.handleSetMenuItemSetting("ambilight_brightness",{value:Number(t)})}async changeAmbilightBrightness(t){let e=c.enums(a$1.ambilight.ambilightChangeBrightnessAvailableValues),[i]=e.validate(t);if(i)return this.renderResponse(i,void 0);let[,r]=await this.getAmbilightBrightnessValue();if(t==="increase"||t==="decrease"){let s=t==="increase"?Number(r)+1:Number(r)-1,n=Math.min(10,Math.max(0,s));return this.setAmbilightBrightness(n)}return this.setAmbilightBrightness(t)}increaseAmbilightBrightness(){return this.changeAmbilightBrightness("increase")}decreaseAmbilightBrightness(){return this.changeAmbilightBrightness("decrease")}getAmbilightConfiguration(){return this.digestClient.request("ambilight/currentconfiguration")}async getAmbilightBrightnessInformation(){let[t,e]=await this.getMenuStructureItemByContext("ambilight_brightness");if(t)return this.renderResponse(t,void 0);let i=e?.node_id,[r,s]=await this.getCurrentSetting(i);return r?this.renderResponse(r,void 0):[void 0,get(s,"0.value",void 0)]}async getAmbilightBrightnessValue(){let[t,e]=await this.getAmbilightBrightnessInformation();return [t,Number(get(e,"data.value",void 0))]}getAmbilightMode(){return this.digestClient.request("ambilight/mode")}async getAmbilightFullInformation(){let[t,e,i,r,s]=await Promise.all([this.getAmbilightConfiguration(),this.getAmbilightBrightnessInformation(),this.getAmbilightMode(),this.getAmbilightCached(),this.getAmbiHue()]),[n,a]=t,[p,v]=e,[w,I]=i,[P,B]=r,[V,M]=s;return [n||p||w||P||V,{configuration:a,mode:I,brightness:v,cached:B,ambiHue:M}]}async setAmbilightCurrentConfiguration(t){return this.digestClient.request("ambilight/currentconfiguration",{method:"POST",data:{styleName:t?.styleName,isExpert:t?.isExpert??false,menuSetting:t?.menuSetting}})}async setAmbilightFollowVideoMode(t){let e=z.enum(a$1.ambilight.followVideoMode).safeParse(t);return e.error?this.renderResponse(e.error,void 0):this.setAmbilightCurrentConfiguration({styleName:"FOLLOW_VIDEO",menuSetting:t})}async setAmbilightFollowAudioMode(t){let e=z.enum(a$1.ambilight.followAudioMode).safeParse(t);return e.error?this.renderResponse(e.error,void 0):this.setAmbilightCurrentConfiguration({styleName:"FOLLOW_AUDIO",menuSetting:t})}setAmbilightMode(t){return this.digestClient.request("ambilight/mode",{method:"POST",data:{current:t}})}getAmbilightCached(){return this.digestClient.request("ambilight/cached")}getCurrentActivity(){return this.digestClient.request("activities/current")}getApplications(){return this.digestClient.request("applications")}sendKey(t){return this.digestClient.request("input/key",{method:"POST",data:{key:t}})}getAmbiHue(){return this.digestClient.request("HueLamp/power")}async getCurrentSetting(t){let[,e]=await this.digestClient.request("menuitems/settings/current",{method:"POST",data:{nodes:[{nodeid:t}]}}),i=e?.values;return i?.length>0?[void 0,i]:[new Error("menuitems/settings/current: No values"),void 0]}}; export{m as PhilTVApi,h as PhilTVPairing};