UNPKG

iobroker.emby

Version:
1,031 lines (949 loc) 34.3 kB
'use strict'; const request = require('request'); const W3CWebSocket = require('websocket').w3cwebsocket; const utils = require('@iobroker/adapter-core'); // Get common adapter utils let adapter; let connection; let laststates = {}; let timeoutretry; let timeoutplays = {}; let timeoutstates = {}; let timeoutstarted = {}; function startAdapter(options) { options = options || {}; Object.assign(options, { name: 'emby', stateChange: fStateChange, unload: fUnload, ready: function () { main(); }, }); adapter = new utils.Adapter(options); return adapter; } // is called when adapter shuts down - callback has to be called under any circumstances! function fUnload(callback) { try { connection.send('{"MessageType":"SessionsStop", "Data": ""}'); if (connection != undefined) { connection.close(); } //checkOnline = null; laststates = {}; Object.keys(timeoutplays).forEach(timeout => { clearTimeout(timeoutplays[timeout]); }); Object.keys(timeoutstates).forEach(timeout => { clearTimeout(timeoutstates[timeout]); }); Object.keys(timeoutstarted).forEach(timeout => { clearTimeout(timeoutstarted[timeout]); }); timeoutplays = {}; timeoutstates = {}; timeoutstarted = {}; adapter.log.info('cleaned everything up...'); clearTimeout(timeoutretry); callback(); } catch { callback(); } } /* // is called if a subscribed object changes adapter.on('objectChange', function (id, obj) { // Warning, obj can be null if it was deleted adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj)); }); */ function fStateChange(id, state) { if (id && state && !state.ack) { id = id.substring(adapter.namespace.length + 1); if (id.indexOf('info.') !== -1 || id.indexOf('playing.') !== -1) { adapter.setState(id, state.val, true); return; } const dId = id.substring(0, id.indexOf('.')); const cmd = id.substring(dId.length + 1); const headers = { 'Content-Type': 'application/json', }; let jsonbody = ''; switch (cmd) { case 'command.toggleplay': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Playing/PlayPause?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.play': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Playing/Unpause?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.pause': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Playing/Pause?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.mute': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Command/Mute?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.unmute': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Command/Unmute?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.togglemute': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Command/ToggleMute?api_key=${ adapter.config.apikey }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.message': request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Message?api_key=${adapter.config.apikey}`, body: `{"Header":"Test", "Text":"${state.val}", "TimeoutMs":"5000" }`, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.dialog': if (state.val.indexOf('|') !== -1) { const paras = state.val.split('|'); jsonbody = `{"Header":"${paras[0]}", "Text":"${paras[1]}" }`; } else { jsonbody = `{"Header":"ioBroker", "Text":"${state.val}" }`; } request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Message?api_key=${adapter.config.apikey}`, body: jsonbody, headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, '', true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.goHome': request.post( `http://${adapter.config.ip}/Sessions/${dId}/Command/GoHome?api_key=${adapter.config.apikey}`, {}, function (error, resp, _body) { if (error) { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.goToSearch': request.post( `http://${adapter.config.ip}/Sessions/${dId}/Command/GoToSearch?api_key=${adapter.config.apikey}`, {}, function (error, resp, _body) { if (error) { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.back': request.post( `http://${adapter.config.ip}/Sessions/${dId}/Command/Back?api_key=${adapter.config.apikey}`, {}, function (error, resp, _body) { if (error) { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); break; case 'command.volume': adapter.log.info( `http://${adapter.config.ip}/Sessions/${dId}/Command/SetVolume?api_key=${adapter.config.apikey}`, ); request.post( { uri: `http://${adapter.config.ip}/Sessions/${dId}/Command/SetVolume?api_key=${ adapter.config.apikey }`, form: JSON.parse(`{ Arguments:{ Volume: ${state.val} } }`), headers: headers, }, function (error, resp, _body) { if (!error) { adapter.setState(id, state.val, true); } else { adapter.log.info(`Fehler: ${JSON.stringify(resp)}`); } }, ); adapter.log.info(JSON.stringify(JSON.parse(`{ Arguments:{ Volume: ${state.val} } }`))); break; default: adapter.log.info(`Not supported command: ${cmd} | ${id}`); break; } } } //adapter.on('message', function (obj) { // if (typeof obj === 'object' && obj.message) { // if (obj.command === 'send') { // if (obj.callback) adapter.sendTo(obj.from, obj.command, 'Message received', obj.callback); // } // } //}); function main() { adapter.subscribeStates('*'); adapter.setObjectNotExists(`${adapter.namespace}.info`, { type: 'channel', common: { name: 'Info', }, native: {}, }); adapter.setObjectNotExists(`${adapter.namespace}.info.connection`, { type: 'state', common: { name: 'If connected to Emby Server', role: 'indicator.connected', type: 'boolean', read: true, write: false, def: false, }, native: {}, }); tryConnect(); } function tryConnect() { try { if (adapter.config.apikey == '') { adapter.log.warn('There is no ApiKey. You can recieve information but cant controle clients.'); } adapter.log.debug(`try to connect to: ${adapter.config.ip}`); const prefix = adapter.config.isSSL ? 'wss://' : 'ws://'; connection = new W3CWebSocket(`${prefix + adapter.config.ip}?api_key=${adapter.config.apikey}&deviceId=00001`); //8306e66875c54b4c816fed315c3cd2e6 connection.onopen = webOpen; connection.onerror = webError; connection.onmessage = webMessage; } catch (e) { adapter.setState('info.connection', false, true); adapter.log.warn(`Verbindung konnte nicht hergestellt werden. Nächster Versuch in 1 Minute: "${e.message}"`); timeoutretry = setTimeout(tryConnect, 60000); } } function webOpen() { if (connection == undefined || connection.readyState !== 1) { adapter.log.error('Verbindung konnte nicht hergestellt werden. (readyState) Nächster Versuch in 1 Minute.'); if (connection) { connection.close(); } connection = null; timeoutretry = setTimeout(tryConnect, 60000); return; } connection.send('{"MessageType":"SessionsStart", "Data": "10000,10000"}'); adapter.log.info(`connected with emby (${adapter.config.ip})`); adapter.setState('info.connection', true, true); } function webError(error) { adapter.setState('info.connection', false, true); adapter.log.debug(`Websocket Error : ${JSON.stringify(error)}`); adapter.log.error('WebSocket Error. Try Reconnect in 60s'); timeoutretry = setTimeout(tryConnect, 60000); } function webMessage(e) { const data = JSON.parse(e.data); adapter.log.debug(JSON.stringify(data)); for (let i = 0; i < data.Data.length; i++) { const d = data.Data[i]; if ( adapter.config.deviceIds == '' || (adapter.config.deviceIds != '' && adapter.config.deviceIds.indexOf(d.Id) == -1) ) { createDevice(d); if (typeof d.DeviceName !== 'undefined') { adapter.setState(`${d.Id}.info.deviceName`, d.DeviceName, true); } else { adapter.setState(`${d.Id}.info.deviceName`, '', true); } if (typeof d.UserName !== 'undefined') { adapter.setState(`${d.Id}.info.userName`, d.UserName, true); } else { adapter.setState(`${d.Id}.info.userName`, '', true); } if (typeof d.NowPlayingItem !== 'undefined') { let endString = ''; if (d.NowPlayingItem.RunTimeTicks != null && d.PlayState.PositionTicks != null) { const endDate = new Date( Date.now() + (d.NowPlayingItem.RunTimeTicks - d.PlayState.PositionTicks) / 10000, ); endString = `${endDate.getHours()}:${ endDate.getMinutes() < 10 ? `0${endDate.getMinutes()}` : endDate.getMinutes() }`; } const npi = d.NowPlayingItem; adapter.setState(`${d.Id}.media.endtime`, endString, true); if (typeof npi.Name !== 'undefined') { adapter.setState(`${d.Id}.media.title`, npi.Name, true); } else { adapter.setState(`${d.Id}.media.title`, '', true); } if (typeof npi.Overview !== 'undefined') { adapter.setState(`${d.Id}.media.description`, npi.Overview, true); } else { adapter.setState(`${d.Id}.media.description`, '', true); } if (typeof npi.Type !== 'undefined') { adapter.setState(`${d.Id}.media.type`, npi.Type, true); } else { adapter.setState(`${d.Id}.media.type`, '', true); } if (typeof npi.CommunityRating !== 'undefined') { adapter.setState(`${d.Id}.media.rating`, npi.CommunityRating, true); } else { adapter.setState(`${d.Id}.media.rating`, '', true); } if (typeof npi.ProductionYear !== 'undefined') { adapter.setState(`${d.Id}.media.year`, npi.ProductionYear, true); } else { adapter.setState(`${d.Id}.media.year`, '', true); } if (typeof npi.Taglines !== 'undefined') { adapter.setState(`${d.Id}.media.tags`, npi.Taglines.join(','), true); } else { adapter.setState(`${d.Id}.media.tags`, '', true); } if (typeof npi.Genres !== 'undefined') { adapter.setState(`${d.Id}.media.genres`, npi.Genres.join(','), true); } else { adapter.setState(`${d.Id}.media.genres`, '', true); } if (typeof npi.BackdropImageTags !== 'undefined') { adapter.setState(`${d.Id}.media.backdropimage`, npi.BackdropImageTags.join(','), true); } else { adapter.setState(`${d.Id}.media.backdropimage`, '', true); } if (typeof npi.PrimaryImageAspectRatio != 'undefined') { adapter.setState(`${d.Id}.media.ratio`, npi.PrimaryImageAspectRatio, true); } else { adapter.setState(`${d.Id}.media.ratio`, '', true); } if (typeof npi.OfficialRating != 'undefined') { adapter.setState(`${d.Id}.media.agerating`, npi.OfficialRating, true); } else { adapter.setState(`${d.Id}.media.agerating`, '', true); } if (typeof npi.OriginalTitle != 'undefined') { adapter.setState(`${d.Id}.media.originaltitle`, npi.OriginalTitle, true); } else { adapter.setState(`${d.Id}.media.originaltitle`, '', true); } const prefix = adapter.config.isSSL ? 'https://' : 'http://'; const basePoster = `${prefix + adapter.config.ip}/Items/`; switch (npi.Type) { case 'Episode': adapter.setState(`${d.Id}.posters.main`, `${basePoster + npi.SeriesId}/Images/Primary`, true); adapter.setState(`${d.Id}.posters.season`, `${basePoster + npi.SeasonId}/Images/Primary`, true); adapter.setState(`${d.Id}.posters.episode`, `${basePoster + npi.Id}/Images/Primary`, true); break; case 'Movie': adapter.setState(`${d.Id}.posters.main`, `${basePoster + npi.Id}/Images/Primary`, true); adapter.setState(`${d.Id}.posters.season`, '', true); adapter.setState(`${d.Id}.posters.episode`, '', true); break; default: adapter.setState(`${d.Id}.posters.main`, '', true); adapter.setState(`${d.Id}.posters.season`, '', true); adapter.setState(`${d.Id}.posters.episode`, '', true); break; } if (typeof npi.SeasonName !== 'undefined') { adapter.setState(`${d.Id}.media.seasonName`, npi.SeasonName, true); adapter.setState(`${d.Id}.media.seriesName`, npi.SeriesName, true); } else { adapter.setState(`${d.Id}.media.seasonName`, '', true); adapter.setState(`${d.Id}.media.seriesName`, '', true); } //var ispaused; if (typeof d.PlayState.MediaSourceId !== 'undefined') { if (d.PlayState.IsPaused) { changeState(d.Id, 'paused'); } else { //adapter.setState(d.Id + ".media.state", "paused", true); changeState(d.Id, 'playing'); } //adapter.setState(d.Id + ".media.state", "playing", true); } else { changeState(d.Id, 'paused'); //adapter.setState(d.Id + ".media.state", "paused", true); } adapter.setState(`${d.Id}.media.isMuted`, d.PlayState.IsMuted, true); } else { adapter.setState(`${d.Id}.media.title`, '', true); adapter.setState(`${d.Id}.media.description`, '', true); adapter.setState(`${d.Id}.media.seasonName`, '', true); adapter.setState(`${d.Id}.media.seriesName`, '', true); adapter.setState(`${d.Id}.media.type`, 'None', true); adapter.setState(`${d.Id}.media.endtime`, '', true); adapter.setState(`${d.Id}.posters.main`, '', true); adapter.setState(`${d.Id}.posters.season`, '', true); adapter.setState(`${d.Id}.posters.episode`, '', true); changeState(d.Id, 'idle'); //adapter.setState(d.Id + ".media.state", "idle", true); } } else { adapter.log.debug(`Device skipped: ${d.DeviceName}`); } } } function changeState(id, state) { if (laststates[id] == state) { return; } if (state == 'playing') { clearTimeout(timeoutplays[id]); timeoutstarted[id] = false; adapter.setState(`${id}.media.state`, state, true); } else { timeoutstates[id] = state; if (!timeoutstarted[id]) { timeoutstarted[id] = true; timeoutplays[id] = setTimeout(function () { adapter.setState(`${id}.media.state`, timeoutstates[id], true); timeoutstarted[id] = false; }, adapter.config.timeout); } } laststates[id] = state; } function createDevice(device) { const sid = `${adapter.namespace}.${device.Id}`; adapter.setObjectNotExists(sid, { type: 'device', common: { name: device.DeviceName, }, native: {}, }); adapter.setObjectNotExists(`${sid}.info`, { type: 'channel', common: { name: 'Info', }, native: {}, }); adapter.setObjectNotExists(`${sid}.media`, { type: 'channel', common: { name: 'Media Info', }, native: {}, }); adapter.setObjectNotExists(`${sid}.posters`, { type: 'channel', common: { name: 'Media Posters', }, native: {}, }); adapter.setObjectNotExists(`${sid}.info.deviceName`, { type: 'state', common: { name: 'Name of the Device now playing', role: 'info.name', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.info.userName`, { type: 'state', common: { name: 'Logged in User', role: 'info.name', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.info.supportedCommands`, { type: 'state', common: { name: 'List of supported Commands', role: 'info.name', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.state`, { type: 'state', common: { name: 'State of the Client', role: 'media.state', type: 'string', read: true, write: true, states: { idle: 'Idle', paused: 'Paused', playing: 'Playing', }, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.isMuted`, { type: 'state', common: { name: 'If Player is muted', role: 'media.mute', type: 'boolean', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.title`, { type: 'state', common: { name: 'Title of file playing', role: 'media.title', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.seriesName`, { type: 'state', common: { name: 'Name of serie now playing', role: 'media.title', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.seasonName`, { type: 'state', common: { name: 'Name of season now playing', role: 'media.season', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.type`, { type: 'state', common: { name: 'Type of file playing', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.description`, { type: 'state', common: { name: 'Description of file playing', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.endtime`, { type: 'state', common: { name: 'Endtime of playing video/audio', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.rating`, { type: 'state', common: { name: 'Community Rating', role: 'state', type: 'number', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.year`, { type: 'state', common: { name: 'Production Year', role: 'state', type: 'number', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.tags`, { type: 'state', common: { name: 'Tags', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.genres`, { type: 'state', common: { name: 'Genres', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.backdropimage`, { type: 'state', common: { name: 'BackdropImageTags', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.ratio`, { type: 'state', common: { name: 'Ratio', role: 'state', type: 'number', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.agerating`, { type: 'state', common: { name: 'Age Rating', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.media.originaltitle`, { type: 'state', common: { name: 'Original Title', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.posters.main`, { type: 'state', common: { name: 'Main Poster', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.posters.episode`, { type: 'state', common: { name: 'Episode Poster', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); adapter.setObjectNotExists(`${sid}.posters.season`, { type: 'state', common: { name: 'Season Poster', role: 'state', type: 'string', read: true, write: false, }, native: {}, }); if (!device.SupportsRemoteControl) { return; } adapter.setObjectNotExists(`${sid}.command`, { type: 'channel', common: { name: 'Controll Client', }, native: {}, }); adapter.setObjectNotExists(`${sid}.command.pause`, { type: 'state', common: { name: 'Pause', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); adapter.setObjectNotExists(`${sid}.command.play`, { type: 'state', common: { name: 'Play', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); adapter.setObjectNotExists(`${sid}.command.toggleplay`, { type: 'state', common: { name: 'Toggle Play', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); adapter.setState(`${sid}.info.supportedCommands`, JSON.stringify(device.SupportedCommands), true); for (let i = 0; i < device.SupportedCommands.length; i++) { switch (device.SupportedCommands[i]) { case 'DisplayMessage': adapter.setObjectNotExists(`${sid}.command.message`, { type: 'state', common: { name: 'Message', role: 'state', type: 'string', read: false, write: true, }, native: {}, }); adapter.setObjectNotExists(`${sid}.command.dialog`, { type: 'state', common: { name: 'Message Dialog', role: 'state', type: 'string', read: false, write: true, }, native: {}, }); break; case 'GoHome': adapter.setObjectNotExists(`${sid}.command.goHome`, { type: 'state', common: { name: 'GoHome', role: 'button', type: 'boolean', read: false, write: true, }, native: {}, }); break; case 'SetVolume': adapter.setObjectNotExists(`${sid}.command.volume`, { type: 'state', common: { name: 'Set Volume (causes crash on some devices)', role: 'level.volume', type: 'number', read: true, write: true, }, native: {}, }); break; case 'GoToSearch': adapter.setObjectNotExists(`${sid}.command.goToSearch`, { type: 'state', common: { name: 'Go to search', role: 'button', type: 'boolean', read: false, write: true, }, native: {}, }); break; case 'Back': adapter.setObjectNotExists(`${sid}.command.back`, { type: 'state', common: { name: 'Back', role: 'button', type: 'boolean', read: false, write: true, }, native: {}, }); break; case 'Mute': adapter.setObjectNotExists(`${sid}.command.mute`, { type: 'state', common: { name: 'Mute', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); break; case 'UnMute': adapter.setObjectNotExists(`${sid}.command.unmute`, { type: 'state', common: { name: 'Unmute', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); break; case 'ToggleMute': adapter.setObjectNotExists(`${sid}.command.togglemute`, { type: 'state', common: { name: 'Toggle Mute', role: 'button', type: 'button', read: false, write: true, }, native: {}, }); break; } } } if (module && module.parent) { module.exports = startAdapter; } else { startAdapter(); }