iobroker.javascript 10.2.3 → 10.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +6 -8
  2. package/admin/assets/{AiChatPanel-DsQB1Hb5.js → AiChatPanel-oBeXbUFv.js} +8 -8
  3. package/admin/assets/{AiChatService-Bsp_rhVx.js → AiChatService-BOL3J2Jd.js} +1 -1
  4. package/admin/assets/AiDiffView-C-KKjnJD.js +1 -0
  5. package/admin/assets/{AiInlineProvider-BxOW88xU.js → AiInlineProvider-BIlcrTkS.js} +1 -1
  6. package/admin/assets/Debugger-DwHHsh9O.js +15 -0
  7. package/admin/assets/Documentation-Bc0RmoEF.js +4339 -0
  8. package/admin/assets/Error-DZ4vCykD.js +1 -0
  9. package/admin/assets/Import-Cu02ceLW.js +1 -0
  10. package/admin/assets/RulesEditor-D3kTbD_T.css +1 -0
  11. package/admin/assets/RulesEditor-QCbi1lV_.js +1 -0
  12. package/admin/assets/{ScriptEditor-BkuL2lZ1.js → ScriptEditor-CEHpEFp7.js} +2 -2
  13. package/admin/assets/ScriptEditorVanillaMonaco-ChQmnk0p.js +3 -0
  14. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-CM95r358.js +1 -0
  15. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BEpNZh4U.js +1217 -0
  16. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-FeM77flT.js +9 -0
  17. package/admin/assets/aiPromptBuilder-DKler0UK.js +1 -0
  18. package/admin/assets/{blocks_action-EYzXGRHi.js → blocks_action-DywZVfRY.js} +1 -1
  19. package/admin/assets/{blocks_convert-BewF036e.js → blocks_convert-BP2RjhQH.js} +1 -1
  20. package/admin/assets/{blocks_logic-BEcVdiZ4.js → blocks_logic-CdTl5YZw.js} +1 -1
  21. package/admin/assets/{blocks_number-Cp7J-juL.js → blocks_number-DXFd9Rva.js} +1 -1
  22. package/admin/assets/{blocks_object-CCe0acSu.js → blocks_object-by1uBDHi.js} +1 -1
  23. package/admin/assets/{blocks_procedures-BzFeWYcX.js → blocks_procedures-BcJ7N70D.js} +1 -1
  24. package/admin/assets/{blocks_sendto-EBwcaHpm.js → blocks_sendto-qIKzOAcV.js} +1 -1
  25. package/admin/assets/{blocks_switch-CjohzLLS.js → blocks_switch-dLF6hSJl.js} +1 -1
  26. package/admin/assets/{blocks_system-D9V3f3Vv.js → blocks_system-DmX-SBy8.js} +1 -1
  27. package/admin/assets/{blocks_text-CnEbzx6V.js → blocks_text-BI4zNU3o.js} +1 -1
  28. package/admin/assets/{blocks_time-B5OcgKMo.js → blocks_time-BgYA4Y_4.js} +1 -1
  29. package/admin/assets/{blocks_timeout-ONu3mIX3.js → blocks_timeout-DUzgMyAu.js} +1 -1
  30. package/admin/assets/{blocks_trigger-3DujkROX.js → blocks_trigger-CnmXLCUE.js} +1 -1
  31. package/admin/assets/{cronHoverProvider-BZMOFHO_.js → cronHoverProvider-CcIFAhVy.js} +1 -1
  32. package/admin/assets/{field_cron-CxKi__cX.js → field_cron-Ba46q8Qc.js} +1 -1
  33. package/admin/assets/{field_oid-CJIYiGxY.js → field_oid-lWCjOzuq.js} +1 -1
  34. package/admin/assets/{field_script-CehplM7L.js → field_script-BCWZHhnW.js} +1 -1
  35. package/admin/assets/{helpers-BiP5KGR8.js → helpers-CnEjYAup.js} +1 -1
  36. package/admin/assets/{hostInit-Bs-bStPF.js → hostInit-CVybS7BB.js} +1 -1
  37. package/admin/assets/iconLibraryData-B5fiyl1C.js +1 -0
  38. package/admin/assets/index-B3s0zmWC.js +871 -0
  39. package/admin/assets/{inlineChatWidget-NIvdRGmU.js → inlineChatWidget-N8xIkvxX.js} +2 -2
  40. package/admin/assets/{inlineDiffController-Qh402KhK.js → inlineDiffController-BuqYsKKq.js} +2 -2
  41. package/admin/assets/{mf-entry-bootstrap-0-4b82aeb4.js → mf-entry-bootstrap-0-a5a39621.js} +3 -3
  42. package/admin/assets/{pendingShares-B3t6ewri.js → pendingShares-DOAkyFMS.js} +1 -1
  43. package/admin/assets/stateHoverProvider-BYAbe1Ay.js +11 -0
  44. package/admin/custom/assets/Components-FkGDIlId.js +1 -0
  45. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-Cs88NUCd.js +1 -0
  46. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-P-8ffWXw.js +1 -0
  47. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-ZVuRAEo5.js +1 -0
  48. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DVb0ZZ1Q.js +1217 -0
  49. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-Dna3WF04.js +1 -0
  50. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_mui_mf_1_x_mf_2_date_mf_2_pickers__loadShare__.js-DcjdCGAx.js +1 -0
  51. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__date_mf_2_fns__loadShare__.js-CnhNjamd.js +1 -0
  52. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__leaflet__loadShare__.js-im3edKNs.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__leaflet__loadShare__.js-Dz7BFJqH.js} +1 -1
  53. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-B0olko2J.js +1 -0
  54. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-CzN4reh_.js +122 -0
  55. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__leaflet__prebuild__-xPcBaiTQ.js +1 -0
  56. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-Byvr2uOb.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-CXpyx8Vy.js} +1 -1
  57. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_2_dom_mf_1_client__prebuild__-DuFBE5fh.js +9 -0
  58. package/admin/custom/assets/{hostInit-IJMNq3h7.js → hostInit-6oh4UY2k.js} +1 -1
  59. package/admin/custom/assets/iconLibraryData-B5fiyl1C.js +1 -0
  60. package/admin/custom/assets/index-BrlyEy6J.js +1 -0
  61. package/admin/custom/assets/lib-BmDlTzq2.js +3 -0
  62. package/admin/custom/assets/{mf-entry-bootstrap-0-f149bc46.js → mf-entry-bootstrap-0-ad1cd7d0.js} +3 -3
  63. package/admin/custom/assets/pendingShares-kV2kwejX.js +1 -0
  64. package/admin/custom/assets/rolldown-runtime-Dqfqb5bO.js +1 -0
  65. package/admin/custom/customComponents.js +3 -2
  66. package/admin/custom/i18n/en.json +39 -39
  67. package/admin/custom/i18n/es.json +1 -1
  68. package/admin/custom/i18n/fr.json +2 -2
  69. package/admin/custom/i18n/it.json +1 -1
  70. package/admin/custom/i18n/nl.json +2 -2
  71. package/admin/custom/i18n/pt.json +2 -2
  72. package/admin/custom/i18n/ru.json +2 -2
  73. package/admin/custom/i18n/uk.json +1 -1
  74. package/admin/custom/i18n/zh-cn.json +1 -1
  75. package/admin/i18n/de.json +1 -1
  76. package/admin/i18n/en.json +115 -115
  77. package/admin/i18n/fr.json +115 -115
  78. package/admin/i18n/it.json +115 -115
  79. package/admin/i18n/nl.json +115 -115
  80. package/admin/i18n/pl.json +1 -1
  81. package/admin/i18n/pt.json +115 -115
  82. package/admin/i18n/ru.json +115 -115
  83. package/admin/i18n/uk.json +115 -115
  84. package/admin/i18n/zh-cn.json +115 -115
  85. package/admin/mf-manifest.json +1 -1
  86. package/admin/mf-stats.json +1 -1
  87. package/admin/remoteEntry.js +2 -2
  88. package/admin/tab.html +4 -4
  89. package/build/lib/inspect.js +547 -632
  90. package/build/lib/inspect.js.map +1 -1
  91. package/build/lib/javascript.d.ts +16 -16
  92. package/build/main.js +8 -13
  93. package/build/main.js.map +1 -1
  94. package/docs/de/README.md +1 -1
  95. package/docs/de/javascript.md +2167 -0
  96. package/docs/en/javascript.md +28 -30
  97. package/io-package.json +27 -27
  98. package/package.json +8 -8
  99. package/admin/assets/AiDiffView-Cri2QFh1.js +0 -1
  100. package/admin/assets/Debugger-CcDNCkmw.js +0 -15
  101. package/admin/assets/Error-Ch2nx5uC.js +0 -1
  102. package/admin/assets/Import-XKVP-nRT.js +0 -1
  103. package/admin/assets/RulesEditor-B6YQNZfq.js +0 -1
  104. package/admin/assets/RulesEditor-BwQ6Bo1V.css +0 -1
  105. package/admin/assets/ScriptEditorVanillaMonaco-DYNr4-5w.js +0 -3
  106. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-BMAFQFoH.js +0 -1
  107. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__113183188387379__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-D-gi6Mty.js +0 -1217
  108. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__113183188387379__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-CDctpiPx.js +0 -9
  109. package/admin/assets/aiPromptBuilder-db3Z4xhV.js +0 -1
  110. package/admin/assets/index-CRX4LAhD.js +0 -871
  111. package/admin/assets/stateHoverProvider-BkW15VMN.js +0 -9
  112. package/admin/assets/virtualExposes-DgGB-18P.js +0 -1
  113. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-CiidVc0O.js +0 -1
  114. package/admin/assets/virtual_mf-exposes___mfe_internal__iobroker_javascript__remoteEntry_js-ChiJXsfH.js +0 -1
  115. package/admin/custom/assets/Components-SGnA-3W_.js +0 -1
  116. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-Bx3X6TLB.js +0 -1
  117. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-Cc7MmJgr.js +0 -1
  118. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-CdvEvX5z.js +0 -1
  119. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-D5NaoeJi.js +0 -1217
  120. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-Cbgw_a3f.js +0 -1
  121. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_mui_mf_1_x_mf_2_date_mf_2_pickers__loadShare__.js-CMr75K5w.js +0 -1
  122. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__date_mf_2_fns__loadShare__.js-DKIvPDPX.js +0 -1
  123. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-D50aYKgD.js +0 -1
  124. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-CEzQFX7o.js +0 -122
  125. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__leaflet__prebuild__-BUDmnAcx.js +0 -1
  126. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__react_mf_2_dom_mf_1_client__prebuild__-D_1oh1WH.js +0 -9
  127. package/admin/custom/assets/index-7r4VklMp.js +0 -1
  128. package/admin/custom/assets/lib-DDU2Oe48.js +0 -3
  129. package/admin/custom/assets/pendingShares-DarGQhyO.js +0 -1
  130. package/admin/custom/assets/rolldown-runtime-BcKkbAw3.js +0 -1
  131. package/admin/custom/assets/virtualExposes-C0IqMANQ.js +0 -1
  132. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-CqKvh5Xu.js +0 -1
  133. package/admin/custom/assets/virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-DrBN_S5o.js +0 -2
  134. package/build/lib/debugger.js +0 -1135
  135. package/build/lib/debugger.js.map +0 -1
@@ -0,0 +1,4339 @@
1
+ import{$n as e,Bt as t,Er as n,Hn as r,Ht as i,Rt as a,Sr as o,St as s,Vt as c,Wt as l,_n as u,_r as d,an as f,bn as p,er as m,hn as h,m as g,nn as ee,rn as _,tn as v,tr as y,xr as b,xt as x,yr as S}from"./_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BEpNZh4U.js";var te=r([m(`path`,{d:`M21 5c-1.11-.35-2.33-.5-3.5-.5-1.95 0-4.05.4-5.5 1.5-1.45-1.1-3.55-1.5-5.5-1.5S2.45 4.9 1 6v14.65c0 .25.25.5.5.5.1 0 .15-.05.25-.05C3.1 20.45 5.05 20 6.5 20c1.95 0 4.05.4 5.5 1.5 1.35-.85 3.8-1.5 5.5-1.5 1.65 0 3.35.3 4.75 1.05.1.05.15.05.25.05.25 0 .5-.25.5-.5V6c-.6-.45-1.25-.75-2-1m0 13.5c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5z`},`0`),m(`path`,{d:`M17.5 10.5c.88 0 1.73.09 2.5.26V9.24c-.79-.15-1.64-.24-2.5-.24-1.7 0-3.24.29-4.5.83v1.66c1.13-.64 2.7-.99 4.5-.99M13 12.49v1.66c1.13-.64 2.7-.99 4.5-.99.88 0 1.73.09 2.5.26V11.9c-.79-.15-1.64-.24-2.5-.24-1.7 0-3.24.3-4.5.83m4.5 1.84c-1.7 0-3.24.29-4.5.83v1.66c1.13-.64 2.7-.99 4.5-.99.88 0 1.73.09 2.5.26v-1.52c-.79-.16-1.64-.24-2.5-.24`},`1`)],`MenuBook`);n();var C=/^(#{1,6})\s+(.*?)\s*#*\s*$/,w=/^\s*```/,T=/^(\s*)[-*+]\s+(.*)$/,E=/^(\s*)(\d+)\.\s+(.*)$/,D=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/,O=/`([^`]+)`|\\([!-/:-@[-`{-~])|\*\*\*(\S(?:.*?\S)??)\*\*\*|\*\*(\S(?:.*?\S)??)\*\*|\*([^*\s](?:[^*]*?[^*\s])?)\*|\[([^\]]+)\]\(([^)\s]+)\)|(https?:\/\/[^\s<>()`]*[^\s<>()`.,;:!?'"*])/g;function k(e){let t=[],n=new RegExp(O.source,`g`),r=0,i,a=e=>{let n=t[t.length-1];(n==null?void 0:n.type)===`text`?n.text+=e:e&&t.push({type:`text`,text:e})};for(;(i=n.exec(e))!==null;){a(e.slice(r,i.index));let[,n,o,s,c,l,u,d,f]=i;n===void 0?o===void 0?s===void 0?c===void 0?l===void 0?f===void 0?t.push({type:`link`,href:d,children:k(u)}):t.push({type:`link`,href:f,children:[{type:`text`,text:f}]}):t.push({type:`em`,children:k(l)}):t.push({type:`strong`,children:k(c)}):t.push({type:`strong`,children:[{type:`em`,children:k(s)}]}):a(o):t.push({type:`code`,text:n}),r=i.index+i[0].length}return a(e.slice(r)),t}function A(e){return(typeof e==`string`?k(e):e).map(e=>`children`in e?A(e.children):e.text).join(``)}function j(e,t){let n=e.toLowerCase().replace(/[^\p{L}\p{M}\p{N}\p{Pc} -]/gu,``).replace(/ /g,`-`),r=n;for(;t.has(r);){let e=(t.get(n)||0)+1;t.set(n,e),r=`${n}-${e}`}return t.set(r,0),r}function M(e){return e.trim().replace(/^\|/,``).replace(/(?<!\\)\|$/,``).split(/(?<!\\)\|/).map(e=>e.trim())}function N(e,t){return e[t].trim().startsWith(`|`)&&D.test(e[t+1]||``)}function P(e,t){let n=e[t];return C.test(n)||w.test(n)||N(e,t)||T.test(n)||E.test(n)}var F=e=>e.replace(/\t/g,` `).length;function I(e){let t=e.split(/\r?\n/),n=[],r=new Map;for(let e=0;e<t.length;e++){let i=t[e];if(!i.trim())continue;if(w.test(i)){let r=[];for(e++;e<t.length&&!w.test(t[e]);e++)r.push(t[e]);n.push({type:`code`,text:r.join(`
2
+ `)});continue}let a=C.exec(i);if(a){let e=A(a[2]);n.push({type:`heading`,level:a[1].length,source:a[2],text:e,anchor:j(e,r)});continue}if(N(t,e)){let r=M(i),a=[];for(e+=2;e<t.length&&t[e].trim().startsWith(`|`);e++)a.push(M(t[e]));e--,n.push({type:`table`,header:r,rows:a});continue}let o=E.exec(i);if(o||T.test(i)){let r=!!o,a=r?E:T,s=F((o||T.exec(i))[1]),c=[];for(;e<t.length&&t[e].trim();e++){let n=a.exec(t[e]);if(n){let e=F(n[1]);c.push({text:n[n.length-1],depth:e>s?Math.ceil((e-s)/4):0})}else if(P(t,e))break;else c[c.length-1].text+=` ${t[e].trim()}`}e--,n.push({type:`list`,ordered:r,start:o?parseInt(o[2],10):1,items:c});continue}let s=[i.trim()];for(e++;e<t.length&&t[e].trim()&&!P(t,e);e++)s.push(t[e].trim());e--,n.push({type:`paragraph`,text:s.join(` `)})}return n}function L(e){let t=e.findIndex(e=>e.type===`heading`&&/^((table of )?contents?|inhalt(sverzeichnis)?)$/i.test(e.text.trim()));if(t===-1)return e;let{level:n}=e[t],r=t+1;for(;r<e.length&&!(e[r].type===`heading`&&e[r].level<=n);){if(e[r].type!==`list`)return e;r++}return[...e.slice(0,t),...e.slice(r)]}var R=/^([A-Za-z_$][\w$]*)(?:\s+-\s+(.*))?$/;function z(e){return e.filter(e=>e.type===`heading`&&e.level<=3).map(e=>{let t=e.text.replace(/:\s*$/,``),n=e.level>=3?R.exec(t):null;return{level:e.level,anchor:e.anchor,name:n?n[1]:t,description:(n==null?void 0:n[2])||``,text:t,code:!!n}})}function B(e,t){if(!t)return null;let n=e.find(e=>e.level>=3&&e.name===t);return n?n.anchor:null}function ne(e,t){let n=t.toLowerCase().split(/\s+/).filter(Boolean);return n.length?e.filter(e=>{let t=e.text.toLowerCase();return n.every(e=>t.includes(e))}):e}function re(e,t){return e.startsWith(`#`)?{anchor:decodeURIComponent(e.slice(1)).toLowerCase()}:/^[a-z][a-z\d+.-]*:/i.test(e)?{url:e}:{url:new URL(e,t).href}}var V=`doc-`,H={fontFamily:`ui-monospace, "Cascadia Code", Consolas, monospace`,fontSize:`0.85em`,px:.6,py:.15,borderRadius:.5,bgcolor:`action.hover`},U={1:{fontSize:`1.6rem`,fontWeight:500,mt:0,mb:1.5},2:{fontSize:`1.4rem`,fontWeight:500,mt:5,mb:1,pb:.5,borderBottom:1,borderColor:`divider`},3:{fontSize:`1.15rem`,fontWeight:600,mt:4,mb:.75},4:{fontSize:`1rem`,fontWeight:600,mt:2.5,mb:.5},5:{fontSize:`0.95rem`,fontWeight:600,mt:2,mb:.5},6:{fontSize:`0.9rem`,fontWeight:600,mt:2,mb:.5}};function W(e,t,n){return e.map((e,r)=>{let a=`${t}-${r}`;switch(e.type){case`text`:return e.text;case`code`:return m(i,{component:`code`,sx:H,children:e.text},a);case`strong`:return m(`strong`,{children:W(e.children,a,n)},a);case`em`:return m(`em`,{children:W(e.children,a,n)},a);case`link`:{let t=re(e.href,n.base);return`anchor`in t?m(p,{href:`#${t.anchor}`,onClick:e=>{e.preventDefault(),n.onAnchor(t.anchor)},children:W(e.children,a,n)},a):m(p,{href:t.url,target:`_blank`,rel:`noreferrer`,children:W(e.children,a,n)},a)}default:return null}})}function G(t){let n=(e,n)=>W(k(e),n,t);return m(e,{children:t.blocks.map((e,t)=>{let r=`b${t}`;switch(e.type){case`heading`:return m(c,{id:`${V}${e.anchor}`,"data-level":e.level,component:`h${e.level}`,sx:{...U[e.level],...t===0?{mt:0}:void 0,scrollMarginTop:12},children:n(e.source,r)},r);case`paragraph`:return m(c,{sx:{my:1.25,lineHeight:1.65},children:n(e.text,r)},r);case`code`:return m(i,{component:`pre`,sx:{...H,px:1.5,py:1.25,my:1.5,overflowX:`auto`,fontSize:12.5,lineHeight:1.5,border:1,borderColor:`divider`},children:e.text},r);case`table`:return m(i,{sx:{my:1.5,overflowX:`auto`},children:y(i,{component:`table`,sx:{borderCollapse:`collapse`,width:`100%`,fontSize:14,"& td, & th":{border:1,borderColor:`divider`,px:1,py:.6,textAlign:`left`,verticalAlign:`top`},"& th":{bgcolor:`action.hover`,fontWeight:500}},children:[e.header.some(e=>e)?m(`thead`,{children:m(`tr`,{children:e.header.map((e,t)=>m(`th`,{children:n(e,`${r}h${t}`)},t))})}):null,m(`tbody`,{children:e.rows.map((e,t)=>m(`tr`,{children:e.map((e,i)=>m(`td`,{children:n(e,`${r}r${t}c${i}`)},i))},t))})]})},r);case`list`:return m(i,{component:e.ordered?`ol`:`ul`,start:e.ordered&&e.start!==1?e.start:void 0,sx:{my:1.25,pl:3,lineHeight:1.65,"& li":{mb:.5}},children:e.items.map((e,t)=>m(i,{component:`li`,sx:e.depth?{ml:e.depth*3}:void 0,children:n(e.text,`${r}i${t}`)},t))},r);default:return null}})})}var K=`## Content
3
+ - [Global functions](#global-functions)
4
+ - [Best practice](#best-practice)
5
+
6
+ - [Functions](#the-following-functions-can-be-used-in-scripts)
7
+ - [require - load some module](#require---load-some-module)
8
+ - [console - Gives out the message into log](#console---gives-out-the-message-into-log)
9
+ - [exec - execute some OS command, like "cp file1 file2"](#exec---execute-some-os-command-like-cp-file1-file2)
10
+ - [on - Subscribe on changes or updates of some state](#on---subscribe-on-changes-or-updates-of-some-state)
11
+ - [once](#once)
12
+ - [subscribe - same as on](#subscribe---same-as-on)
13
+ - [unsubscribe](#unsubscribe)
14
+ - [getSubscriptions](#getsubscriptions)
15
+ - [getFileSubscriptions](#getfilesubscriptions)
16
+ - [schedule](#schedule)
17
+ - [Time schedule](#time-schedule)
18
+ - [Astro-function](#astro-function)
19
+ - [scheduleById](#schedulebyid)
20
+ - [getSchedules](#getschedules)
21
+ - [clearSchedule](#clearschedule)
22
+ - [getAttr](#getattr)
23
+ - [getAstroDate](#getastrodate)
24
+ - [isAstroDay](#isastroday)
25
+ - [compareTime](#comparetime)
26
+ - [setState](#setstate)
27
+ - [setStateAsync](#setstateasync)
28
+ - [setStateDelayed](#setstatedelayed)
29
+ - [clearStateDelayed](#clearstatedelayed)
30
+ - [getStateDelayed](#getstatedelayed)
31
+ - [getState](#getstate)
32
+ - [getStateAsync](#getstateasync)
33
+ - [existsState](#existsState)
34
+ - [getObject](#getobject)
35
+ - [setObject](#setobject)
36
+ - [existsObject](#existsObject)
37
+ - [extendObject](#extendobject)
38
+ - [deleteObject](#deleteobject)
39
+ - [getIdByName](#getidbyname)
40
+ - [getEnums](#getenums)
41
+ - [createState](#createstate)
42
+ - [createStateAsync](#createstateasync)
43
+ - [deleteState](#deletestate)
44
+ - [deleteStateAsync](#deletestateasync)
45
+ - [sendTo](#sendto)
46
+ - [sendToAsync](#sendtoasync)
47
+ - [sendToHost](#sendtohost)
48
+ - [sendToHostAsync](#sendtohostasync)
49
+ - [setInterval](#setinterval)
50
+ - [clearInterval](#clearinterval)
51
+ - [setTimeout](#settimeout)
52
+ - [clearTimeout](#cleartimeout)
53
+ - [setImmediate](#setImmediate)
54
+ - [formatDate](#formatdate)
55
+ - [formatTimeDiff](#formattimediff)
56
+ - [getDateObject](#getDateObject)
57
+ - [formatValue](#formatvalue)
58
+ - [adapterSubscribe](#adaptersubscribe)
59
+ - [adapterUnsubscribe](#adapterunsubscribe)
60
+ - [$ - Selector](#---selector)
61
+ - [readFile](#readfile)
62
+ - [writeFile](#writefile)
63
+ - [delFile](#delFile)
64
+ - [renameFile](#renameFile)
65
+ - [onFile](#onFile)
66
+ - [offFile](#offFile)
67
+ - [onStop](#onstop)
68
+ - [getHistory](#gethistory)
69
+ - [runScript](#runscript)
70
+ - [runScriptAsync](#runScriptAsync)
71
+ - [startScript](#startscript)
72
+ - [startScriptAsync](#startscriptasync)
73
+ - [stopScript](#stopscript)
74
+ - [stopScriptAsync](#stopScriptAsync)
75
+ - [isScriptActive](#isscriptactive)
76
+ - [name](#scriptName)
77
+ - [instance](#instance)
78
+ - [SECRETS](#secrets)
79
+ - [messageTo](#messageto)
80
+ - [messageToAsync](#messagetoasync)
81
+ - [onMessage](#onmessage)
82
+ - [onMessageUnregister](#onmessageunregister)
83
+ - [onLog](#onlog)
84
+ - [onLogUnregister](#onlogunregister)
85
+ - [wait](#wait)
86
+ - [sleep](#sleep)
87
+ - [httpGet](#httpget)
88
+ - [httpPost](#httppost)
89
+ - [createTempFile](#createtempfile)
90
+ - [registerNotification](#registerNotification)
91
+
92
+ - [Scripts activity](#scripts-activity)
93
+
94
+ ## Global functions
95
+ You can define the global scripts in the \`global\` folder.
96
+ All global scripts are available on all instances. If a global script is disabled, it will not be used.
97
+ Global script will be just prepended to the normal script and compiled, so you cannot share data between scripts via global scripts. Use states for it.
98
+
99
+ To use global functions in TypeScript, you have to \`declare\` them first, so the compiler knows about the global functions. Example:
100
+ \`\`\`typescript
101
+ // global script:
102
+ // ==============
103
+ function globalFn(arg: string): void {
104
+ // actual implementation
105
+ }
106
+
107
+ // normal script:
108
+ // ==============
109
+ declare function globalFn(arg: string): void;
110
+ // use as normal:
111
+ globalFn('test');
112
+ \`\`\`
113
+
114
+ #### Best practice:
115
+ Create two instances of javascript adapter: one "test" and one "production".
116
+ After the script is tested in the "test" instance, it can be moved to "production". By that you can restart the "test" instance as you want.
117
+
118
+ ## The following functions can be used in scripts:
119
+
120
+ ### require - load some module
121
+ \`\`\`js
122
+ const mod = require('module_name');
123
+ \`\`\`
124
+ The following modules are preloaded: \`node:dgram\`, \`node:crypto\`, \`node:dns\`, \`node:events\`, \`node:fs\`, \`node:http\`, \`node:https\`, \`node:http2\`, \`node:net\`, \`node:os\`, \`node:path\`, \`node:util\`, \`node:stream\`, \`node:zlib\`, \`suncalc2\`, \`axios\`, \`wake_on_lan\`, \`request\` (deprecated)
125
+
126
+ To use other modules, enter the name (and version) of the module in the instance configuration. ioBroker will install the module. You can require and use it in your scripts afterwards.
127
+
128
+ ### console - Gives out the message into log
129
+ Usage is the same as in \`javascript\`
130
+
131
+ ### exec - execute some OS command, like \`cp file1 file2\`
132
+ \`\`\`js
133
+ exec(cmd, [options], callback);
134
+ \`\`\`
135
+
136
+ Execute system command and get the outputs.
137
+
138
+ \`\`\`js
139
+ // Get the list of files and directories in /var/log
140
+ exec('ls /var/log', (error, stdout, stderr) => {
141
+ log('stdout: ' + stdout);
142
+ });
143
+ \`\`\`
144
+
145
+ Node.js uses /bin/sh to execute commands. If you want to use another shell, you can use the option object as described in the [Node.js documentation](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) for child_process.exec.
146
+ It is the best practice to always use fill path names to commands to make sure the right command is executed.
147
+
148
+ **Notice:** you must enable *Enable command "exec"* option to call it.
149
+
150
+ ### on - Subscribe on changes or updates of some state
151
+ \`\`\`js
152
+ on(pattern, callbackOrId, value);
153
+ \`\`\`
154
+
155
+ The callback function will return the object as parameter with the following content:
156
+ \`\`\`js
157
+ {
158
+ id: 'javascript.0.myplayer',
159
+ state: {
160
+ val: 'new state',
161
+ ts: 1416149118,
162
+ ack: true,
163
+ lc: 1416149118,
164
+ from: 'system.adapter.sonos.0'
165
+ },
166
+ oldState: {
167
+ val: 'old state',
168
+ ts: 1416148233,
169
+ ack: true,
170
+ lc: 1416145154,
171
+ from: 'system.adapter.sonos.0'
172
+ }
173
+ }
174
+ \`\`\`
175
+
176
+ **Note:** \`state\` was previously called \`newState\`. That is still working.
177
+
178
+ Example:
179
+ \`\`\`js
180
+ let timer;
181
+
182
+ // Create state "javascript.0.counter"
183
+ createState('counter', 0);
184
+
185
+ // On change
186
+ on('adapter.0.device.channel.sensor', (data) => {
187
+ // But not ofter than 30 seconds
188
+ if (!timer) {
189
+ timer = setTimeout(() => {
190
+ timer = null;
191
+ }, 30000);
192
+
193
+ // Set acknowledged value
194
+ setState('counter', 1 + getState('counter'), true);
195
+
196
+ // Or to set unacknowledged command
197
+ setState('adapter.0.device.channel.actor', true);
198
+ }
199
+ });
200
+ \`\`\`
201
+
202
+ You can use the following parameters to specify the trigger:
203
+
204
+ | parameter | type/value | description |
205
+ |-------------|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
206
+ | logic | string | "and" or "or" logic to combine the conditions \\(default: "and"\\) |
207
+ | | | |
208
+ | id | string | id is equal to given one |
209
+ | | RegExp | id matched to regular expression |
210
+ | | Array | id matched to a list of allowed IDs |
211
+ | | | |
212
+ | name | string | name is equal to given one |
213
+ | | RegExp | name matched to regular expression |
214
+ | | Array | name matched to a list of allowed names |
215
+ | | | |
216
+ | change | string | "eq", "ne", "gt", "ge", "lt", "le", "any" |
217
+ | | "eq" | (equal) New value must be equal to old one (state.val == oldState.val) |
218
+ | | "ne" | (not equal) New value must be not equal to the old one (state.val != oldState.val) **If pattern is id-string this value is used by default** |
219
+ | | "gt" | (greater) New value must be greater than old value (state.val > oldState.val) |
220
+ | | "ge" | (greater or equal) New value must be greater or equal to old one (state.val >= oldState.val) |
221
+ | | "lt" | (smaller) New value must be smaller than old one (state.val < oldState.val) |
222
+ | | "le" | (smaller or equal) New value must be smaller or equal to old value (state.val <= oldState.val) |
223
+ | | "any" | Trigger will be raised if just the new value comes |
224
+ | | | |
225
+ | val | mixed | New value must be equal to given one |
226
+ | valNe | mixed | New value must be not equal to given one |
227
+ | valGt | mixed | New value must be greater than given one |
228
+ | valGe | mixed | New value must be greater or equal to given one |
229
+ | valLt | mixed | New value must be smaller than given one |
230
+ | valLe | mixed | New value must be smaller or equal to given one |
231
+ | | | |
232
+ | ack | boolean | Acknowledged state of new value is equal to given one |
233
+ | q | number | Quality code state of new value is equal to given one. You can use '*' for matching to any code. **If not provided q = 0 is set as pattern!** |
234
+ | | | |
235
+ | oldVal | mixed | Previous value must be equal to given one |
236
+ | oldValNe | mixed | Previous value must be not equal to given one |
237
+ | oldValGt | mixed | Previous value must be greater than given one |
238
+ | oldValGe | mixed | Previous value must be greater or equal to given one |
239
+ | oldValLt | mixed | Previous value must be smaller than given one |
240
+ | oldValLe | mixed | Previous value must be smaller or equal to given one |
241
+ | | | |
242
+ | oldAck | bool | Acknowledged state of previous value is equal to given one |
243
+ | oldQ | number | Quality code state of previous value is equal to given one. You can use '*' for matching to any code |
244
+ | | | |
245
+ | ts | number | New value time stamp must be equal to given one (state.ts == ts) |
246
+ | tsGt | number | New value time stamp must be greater than given one (state.ts > ts) |
247
+ | tsGe | number | New value time stamp must be greater or equal to given one (state.ts >= ts) |
248
+ | tsLt | number | New value time stamp must be smaller than given one (state.ts < ts) |
249
+ | tsLe | number | New value time stamp must be smaller or equal to given one (state.ts <= ts) |
250
+ | | | |
251
+ | oldTs | number | Previous time stamp must be equal to given one (oldState.ts == ts) |
252
+ | oldTsGt | number | Previous time stamp must be greater than given one (oldState.ts > ts) |
253
+ | oldTsGe | number | Previous time stamp must be greater or equal to given one (oldState.ts >= ts) |
254
+ | oldTsLt | number | Previous time stamp must be smaller than given one (oldState.ts < ts) |
255
+ | oldTsLe | number | Previous time stamp must be smaller or equal to given one (oldState.ts <= ts) |
256
+ | | | |
257
+ | lc | number | Last change time stamp must be equal to given one (state.lc == lc) |
258
+ | lcGt | number | Last change time stamp must be greater than given one (state.lc > lc) |
259
+ | lcGe | number | Last change time stamp must be greater or equal to given one (state.lc >= lc) |
260
+ | lcLt | number | Last change time stamp must be smaller than given one (state.lc < lc) |
261
+ | lcLe | number | Last change time stamp must be smaller or equal to given one (state.lc <= lc) |
262
+ | | | |
263
+ | oldLc | number | Previous last change time stamp must be equal to given one (oldState.lc == lc) |
264
+ | oldLcGt | number | Previous last change time stamp must be greater than given one (oldState.lc > lc) |
265
+ | oldLcGe | number | Previous last change time stamp must be greater or equal to given one (oldState.lc >= lc) |
266
+ | oldLcLt | number | Previous last change time stamp must be smaller than given one (oldState.lc < lc) |
267
+ | oldLcLe | number | Previous last change time stamp must be smaller or equal to given one (oldState.lc <= lc) |
268
+ | | | |
269
+ | channelId | string | Channel ID must be equal to given one |
270
+ | | RegExp | Channel ID matched to regular expression |
271
+ | | Array | Channel ID matched to a list of allowed channel IDs |
272
+ | | | |
273
+ | channelName | string | Channel name must be equal to given one |
274
+ | | RegExp | Channel name matched to regular expression |
275
+ | | Array | Channel name matched to a list of allowed channel names |
276
+ | | | |
277
+ | deviceId | string | Device ID must be equal to given one |
278
+ | | RegExp | Device ID matched to regular expression |
279
+ | | Array | Device ID matched to a list of allowed device IDs |
280
+ | | | |
281
+ | deviceName | string | Device name must be equal to given one |
282
+ | | RegExp | Device name matched to regular expression |
283
+ | | Array | Device name matched to a list of allowed device names |
284
+ | | | |
285
+ | enumId | string | State belongs to given enum |
286
+ | | RegExp | One enum ID of the state satisfies the given regular expression |
287
+ | | Array | One enum ID of the state is in the given list of enum IDs |
288
+ | | | |
289
+ | enumName | string | State belongs to given enum |
290
+ | | RegExp | One enum name of the state satisfies the given regular expression |
291
+ | | Array | One enum name of the state is in the given list of enum names |
292
+ | | | |
293
+ | from | string | New value is from defined adapter |
294
+ | | RegExp | New value is from an adapter that matches the regular expression |
295
+ | | Array | New value is from an adapter that appears in the given list of allowed adapters |
296
+ | | | |
297
+ | fromNe | string | New value is not from defined adapter |
298
+ | | RegExp | New value is not from an adapter that matches the regular expression |
299
+ | | Array | New value is not from an adapter that appears in the given list of forbidden adapters |
300
+ | | | |
301
+ | oldFrom | string | Old value is from defined adapter |
302
+ | | RegExp | Old value is from an adapter that matches the regular expression |
303
+ | | Array | Old value is from an adapter that appears in the given list of allowed adapters |
304
+ | | | |
305
+ | oldFromNe | string | Old value is not from defined adapter |
306
+ | | RegExp | Old value is not from an adapter that matches the regular expression |
307
+ | | Array | Old value is not from an adapter that appears in the given list of forbidden adapters |
308
+
309
+ Examples:
310
+ Trigger on all states with ID \`'*.STATE'\` if they are acknowledged and have new value \`true\`.
311
+
312
+ \`\`\`js
313
+ {
314
+ "id": /\\.STATE$/,
315
+ "val": true,
316
+ "ack": true,
317
+ "logic": "and"
318
+ }
319
+ \`\`\`
320
+
321
+ **Note:** you can use RegExp directly:
322
+
323
+ \`\`\`js
324
+ on(/^system\\.adapter\\..*\\.\\d+\\.memRss$/, function (obj) {
325
+ });
326
+
327
+ // same as
328
+ on({id: /^system\\.adapter\\..*\\.\\d+\\.memRss$/, change: "ne"}, function (obj) {
329
+ });
330
+ \`\`\`
331
+
332
+ To simply connect two states with each other, write:
333
+ \`\`\`js
334
+ on('stateId1', 'stateId2');
335
+ \`\`\`
336
+
337
+ All changes of *stateId1* will be written to *stateId2*.
338
+
339
+ If the \`value\` parameter is set in combination with state id as the second parameter, on any change the state will filled with the \`value\`.
340
+ \`\`\`js
341
+ on('stateId1', 'stateId2', 'triggered');
342
+ setState('stateId1', 'new value');
343
+
344
+ // stateId2 will be set to 'triggered'.
345
+ \`\`\`
346
+
347
+ Function \`on\` returns handler back. This handler can be used by unsubscribing.
348
+
349
+ *Notice:* By default only states with quality 0x00 will be passed to callback function. If you want to get all events, add \`{q: '*'}\` to pattern structure.
350
+
351
+ *Notice:* Please note, that by default "change" is equal to "any", except when only id as string is set (like \`on('id', () => {});\`). In last case change will be set to "ne".
352
+
353
+ *Notice:* If you want to also get state deletions/expires as trigger, you need to use change with \`ne\` or \`any\` AND q with \`*\` as filter!
354
+
355
+ *Notice:* from 4.3.2 it is possible to write a type of trigger as second parameter: \`on('my.id.0', 'any', obj => log(obj.state.val));\`
356
+
357
+ ### once
358
+ Registers a one-time subscription which automatically unsubscribes after the first invocation. Same as [on](#on---subscribe-on-changes-or-updates-of-some-state), but just executed once.
359
+
360
+ \`\`\`js
361
+ once(pattern, callback);
362
+ \`\`\`
363
+
364
+ ### subscribe - same as **[on](#on---subscribe-on-changes-or-updates-of-some-state)**
365
+
366
+ ### unsubscribe
367
+ \`\`\`js
368
+ unsubscribe(id);
369
+ // or
370
+ unsubscribe(handler);
371
+ \`\`\`
372
+
373
+ Remove all subscriptions for given object ID or for given handler.
374
+
375
+ \`\`\`js
376
+ // By handler
377
+ let mySubscription = on({ id: 'javascript.0.myState', change: 'any' }, (data) => {
378
+ // unsubscribe after first trigger
379
+ if (unsubscribe(mySubscription)) {
380
+ log('Subscription deleted');
381
+ }
382
+ });
383
+
384
+ // by Object ID
385
+ on({ id: 'javascript.0.myState1', change: 'ne' }, (data) => {
386
+ log('Some event');
387
+ });
388
+
389
+ on({ id: 'javascript.0.myState1', change: 'any' }, (data) => {
390
+ // unsubscribe
391
+ if (unsubscribe('javascript.0.myState1')) {
392
+ log('All subscriptions deleted');
393
+ }
394
+ });
395
+ \`\`\`
396
+
397
+ ### getSubscriptions
398
+ Get the list of subscriptions.
399
+
400
+ Example of a result:
401
+ \`\`\`js
402
+ {
403
+ 'megad.0.dataPointName': [
404
+ {
405
+ name : 'script.js.NameOfScript',
406
+ pattern : {
407
+ id : 'megad.0.dataPointName',
408
+ change : 'ne'
409
+ }
410
+ }
411
+ ]
412
+ }
413
+ \`\`\`
414
+
415
+ ### getFileSubscriptions
416
+ Get the list of file subscriptions.
417
+
418
+ Example of a result:
419
+ \`\`\`js
420
+ {
421
+ 'vis.0$%$main/*': [
422
+ {
423
+ name : 'script.js.NameOfScript',
424
+ id : 'vis.0',
425
+ fileNamePattern: 'main/*'
426
+ }
427
+ ]
428
+ }
429
+ \`\`\`
430
+
431
+ ### schedule
432
+ \`\`\`js
433
+ schedule(pattern, callback);
434
+ \`\`\`
435
+
436
+ Time scheduler with astro-function.
437
+
438
+ #### Time schedule
439
+ Pattern can be a string with [Cron-Syntax](http://en.wikipedia.org/wiki/Cron), which consists of 5 (without seconds) or 6 (with seconds) digits:
440
+ \`\`\`
441
+ * * * * * *
442
+ │ │ │ │ │ │
443
+ │ │ │ │ │ │
444
+ │ │ │ │ │ └───── day of week (0 - 6) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0)
445
+ │ │ │ │ └────────── month (1 - 12)
446
+ │ │ │ └─────────────── day of month (1 - 31)
447
+ │ │ └──────────────────── hour (0 - 23)
448
+ │ └───────────────────────── min (0 - 59)
449
+ └───────────────────────────── [optional] sec (0 - 59)
450
+ \`\`\`
451
+
452
+ \`\`\`js
453
+ // Example with 5 digits:
454
+ schedule('*/2 * * * *', () => {
455
+ log('Will be triggered every 2 minutes!');
456
+ });
457
+
458
+ // Example with 6 digits:
459
+ schedule('*/3 * * * * *', () => {
460
+ log('Will be triggered every 3 seconds!');
461
+ });
462
+ \`\`\`
463
+
464
+ The pattern can also be an object, it is used especially if seconds are required:
465
+
466
+ the object could have the following properties:
467
+ - \`second\`
468
+ - \`minute\`
469
+ - \`hour\`
470
+ - \`date\`
471
+ - \`month\`
472
+ - \`year\`
473
+ - \`dayOfWeek\`
474
+
475
+ \`\`\`js
476
+ schedule({ second: [20, 25] }, () => {
477
+ log('Will be triggered at xx:xx:20 and xx:xx:25 of every minute!');
478
+ });
479
+
480
+ schedule({ hour: 12, minute: 30 }, () => {
481
+ log('Will be triggered at 12:30!');
482
+ });
483
+ \`\`\`
484
+ Pattern can be a Javascript Date object (some specific time point) - in this case only it will be triggered only one time.
485
+
486
+ If start or end times for a schedule are needed, this could also be implemented with usage of an object. In this scenario, the object has the properties:
487
+ - \`start\`
488
+ - \`end\`
489
+ - \`rule\`
490
+
491
+ start and end defines a Date object a DateString or a number of milliseconds since 01 January 1970 00:00:00 UTC.
492
+ Rule is a schedule string with [Cron-Syntax](http://en.wikipedia.org/wiki/Cron) or an object:
493
+ \`\`\`js
494
+ let startTime = new Date(Date.now() + 5000);
495
+ let endTime = new Date(startTime.getTime() + 5000);
496
+ schedule({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, () => {
497
+ log('It will run after 5 seconds and stop after 10 seconds');
498
+ });
499
+ \`\`\`
500
+
501
+ The rule itself could be also an object:
502
+
503
+ \`\`\`js
504
+ let today = new Date();
505
+ let startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
506
+ let endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
507
+ let ruleData = { hour: 12, minute: 30 };
508
+ schedule({ start: startTime, end: endTime, rule: ruleData }, () => {
509
+ log('Will be triggered at 12:30, starting tomorow, ending in 7 days');
510
+ });
511
+ \`\`\`
512
+
513
+ #### Astro-function
514
+
515
+ Astro-function can be used via "astro" attribute:
516
+
517
+ \`\`\`js
518
+ schedule({ astro: 'sunrise' }, () => {
519
+ log("Sunrise!");
520
+ });
521
+
522
+ schedule({ astro: 'sunset', shift: 10 }, () => {
523
+ log("10 minutes after sunset!");
524
+ });
525
+ \`\`\`
526
+
527
+ The attribute "shift" is the offset in minutes. It can be negative, too, to define time before astro event.
528
+
529
+ The following values can be used as attribute in astro-function:
530
+
531
+ - \`"sunrise"\`: sunrise (top edge of the sun appears on the horizon)
532
+ - \`"sunriseEnd"\`: sunrise ends (bottom edge of the sun touches the horizon)
533
+ - \`"goldenHourEnd"\`: morning golden hour (soft light, the best time for photography) ends
534
+ - \`"solarNoon"\`: solar noon (sun is in the highest position)
535
+ - \`"goldenHour"\`: evening golden hour starts
536
+ - \`"sunsetStart"\`: sunset starts (bottom edge of the sun touches the horizon)
537
+ - \`"sunset"\`: sunset (sun disappears below the horizon, evening civil twilight starts)
538
+ - \`"dusk"\`: dusk (evening nautical twilight starts)
539
+ - \`"nauticalDusk"\`: nautical dusk (evening astronomical twilight starts)
540
+ - \`"night"\`: night starts (dark enough for astronomical observations)
541
+ - \`"nightEnd"\`: night ends (morning astronomical twilight starts)
542
+ - \`"nauticalDawn"\`: nautical dawn (morning nautical twilight starts)
543
+ - \`"dawn"\`: dawn (morning nautical twilight ends, morning civil twilight starts)
544
+ - \`"nadir"\`: nadir (the darkest moment of the night, sun is in the lowest position)
545
+
546
+ **Note:** to use "astro"-function the "latitude" and "longitude" must be defined in javascript adapter settings.
547
+
548
+ **Note:** in some places sometimes it could be so that no night/nightEnd exists. Please read [here](https://github.com/mourner/suncalc/issues/70) about it.
549
+
550
+ **Note:** you can use "on" function for schedule with small modification:
551
+ \`\`\`js
552
+ on({ time: '*/2 * * * *' }, () => {
553
+ log((new Date()).toString() + " - Will be triggered every 2 minutes!");
554
+ });
555
+
556
+ on({ time: { hour: 12, minute: 30 }}, () => {
557
+ log((new Date()).toString() + " - Will be triggered at 12:30!");
558
+ });
559
+
560
+ on({ astro: 'sunset', shift: 10 }, () => {
561
+ log((new Date()).toString() + " - 10 minutes after sunset!");
562
+ });
563
+ \`\`\`
564
+
565
+ ### scheduleById
566
+ \`\`\`js
567
+ scheduleById(id, callback);
568
+ scheduleById(id, ack, callback);
569
+ \`\`\`
570
+
571
+ Allows creating a schedule based on a state value. If the state value changes, the old schedule will be deleted and a new schedule is created automatically.
572
+
573
+ Supported formats:
574
+
575
+ - \`[h]h:[m]m:ss\` (e.g. \`12:42:15\`, \`15:3:12\`, \`3:10:25\`)
576
+ - \`[h]h:[m]m\` (e.g. \`13:37\`, \`9:40\`)
577
+
578
+ \`\`\`js
579
+ scheduleById('0_userdata.0.configurableTimeFormat', () => {
580
+ log('Executed!');
581
+ });
582
+ \`\`\`
583
+
584
+ Example: Create state and register schedule on changes:
585
+
586
+ \`\`\`js
587
+ createState(
588
+ '0_userdata.0.myTime',
589
+ '00:00:00', // default value
590
+ {
591
+ type: 'string',
592
+ read: true,
593
+ write: true
594
+ },
595
+ () => {
596
+ scheduleById('0_userdata.0.myTime', () => {
597
+ log('Executed!');
598
+ });
599
+ }
600
+ );
601
+ \`\`\`
602
+
603
+ ### getSchedules
604
+ \`\`\`js
605
+ const list = getSchedules(true);
606
+ \`\`\`
607
+ Returns the list of all CRON jobs and schedules (except astro).
608
+ Argument must be \`true\` if you want to get the list for **every running script**. Otherwise, only schedules in the current script will be returned.
609
+
610
+ \`\`\`js
611
+ const list = getSchedules(true);
612
+ list.forEach(schedule => log(JSON.stringify(schedule)));
613
+
614
+ // clear all schedules in all scripts!
615
+ list.forEach(schedule => clearSchedule(schedule));
616
+ \`\`\`
617
+
618
+ Example output:
619
+ \`\`\`
620
+ 2020-11-01 20:15:19.929 - {"type":"cron","pattern":"0 * * * *","scriptName":"script.js.Heizung","id":"cron_1604258108384_74924"}
621
+ 2020-11-01 20:15:19.931 - {"type":"schedule","schedule":"{"period":{}}","scriptName":"script.js.Heizung","id":"schedule_19576"}
622
+ \`\`\`
623
+
624
+ ### clearSchedule
625
+ If **no** "astro" function is used, you can cancel the schedule later. To allow this, the schedule object must be saved:
626
+
627
+ \`\`\`js
628
+ let sch = schedule('*/2 * * * *', () => { /* ... */ });
629
+
630
+ // later:
631
+ clearSchedule(sch);
632
+ \`\`\`
633
+
634
+ \`clearSchedule\` accepts everything \`schedule\` returns (a CRON job object or the ID of a schedule of the time wizard)
635
+ and also the entries of [getSchedules](#getschedules):
636
+
637
+ \`\`\`js
638
+ // Clear all schedules of this script
639
+ getSchedules().forEach(sch => clearSchedule(sch));
640
+ \`\`\`
641
+
642
+ It returns \`true\` if the schedule was found and cleared, otherwise \`false\`.
643
+ Schedules created with the astro option cannot be cleared this way.
644
+
645
+ ### getAttr
646
+ \`\`\`js
647
+ getAttr({ attr1: { attr2: 5 } }, 'attr1.attr2');
648
+ \`\`\`
649
+ Returns an attribute of the object. Path to attribute can be nested, like in the example.
650
+
651
+ If the first attribute is string, the function will try to parse the string as JSON string.
652
+
653
+ ### getAstroDate
654
+ \`\`\`js
655
+ getAstroDate(pattern, date, offsetMinutes);
656
+ \`\`\`
657
+ Returns a javascript Date object for the specified astro-name (e.g. \`"sunrise"\` or \`"sunriseEnd"\`). For valid values, see the list of allowed values in the [Astro](#astro-function) section in the *schedule* function.
658
+
659
+ The returned Date object is calculated for the passed *date*. If no date is provided, the current day is used.
660
+
661
+ \`\`\`js
662
+ let sunriseEnd = getAstroDate('sunriseEnd');
663
+ log(\`Sunrise ends today at \${sunriseEnd.toLocaleTimeString()}\`);
664
+
665
+ let today = new Date();
666
+ let tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
667
+ let tomorrowNight = getAstroDate('night', tomorrow);
668
+ \`\`\`
669
+
670
+ **Note: Depending on your geographical location, there can be cases where e.g. 'night'/'nightEnd' do not exist on certain time points (e.g. locations north in May/June each year!**
671
+
672
+ You can use webpages like [suncalc.net](http://suncalc.net) to check if the time points are correct.
673
+
674
+ ### isAstroDay
675
+ \`\`\`js
676
+ isAstroDay();
677
+ \`\`\`
678
+ Returns \`true\` if the current time is between the astro sunrise and sunset.
679
+
680
+ ### compareTime
681
+ \`\`\`js
682
+ compareTime(startTime, endTime, operation, timeToCompare);
683
+ \`\`\`
684
+ Compare given time with limits.
685
+
686
+ If \`timeToCompare\` is not given, so the actual time will be used.
687
+
688
+ The following operations are possible:
689
+
690
+ - \`">"\` - if given time is greater than \`startTime\`
691
+ - \`">="\` - if given time is greater or equal to \`startTime\`
692
+ - \`"<"\` - if given time is less than \`startTime\`
693
+ - \`"<="\` - if given time is less or equal to \`startTime\`
694
+ - \`"=="\` - if given time is equal to \`startTime\`
695
+ - \`"<>"\` - if given time is not equal to \`startTime\`
696
+ - \`"between"\` - if given time is between \`startTime\` and \`endTime\`
697
+ - \`"not between"\` - if given time is not between \`startTime\` and \`endTime\`
698
+
699
+ Time can be Date object or Date with time or just time.
700
+
701
+ You can use astro-names for the time definition. All 3 parameters can be set as astro time.
702
+ Following values are possible: \`sunrise\`, \`sunset\`, \`sunriseEnd\`, \`sunsetStart\`, \`dawn\`, \`dusk\`, \`nauticalDawn\`, \`nauticalDusk\`, \`nightEnd\`, \`night\`, \`goldenHourEnd\`, \`goldenHour\`.
703
+ See [Astro](#astro-function) for detail.
704
+
705
+ \`\`\`js
706
+ log(compareTime('sunsetStart', 'sunsetEnd', 'between') ? 'Now is sunrise' : 'Now is no sunrise');
707
+ \`\`\`
708
+
709
+ It is possible to define the time with offset too:
710
+
711
+ \`\`\`js
712
+ log(compareTime({ astro: 'sunsetStart', offset: 30 }, { astro: 'sunrise', offset: -30 }, '>') ? 'Now is at least 30 minutes after sunset' : 'No idea');
713
+ \`\`\`
714
+
715
+ Structure of an astro object.
716
+
717
+ \`\`\`js
718
+ {
719
+ astro: 'sunsetStart',// mandatory, can be written as string and not as object if offset and date are default
720
+ offset: 30, // optional
721
+ date: new Date() // optional
722
+ }
723
+ \`\`\`
724
+
725
+ ### setState
726
+ \`\`\`js
727
+ setState(id, state, ack, callback);
728
+ \`\`\`
729
+
730
+ *Note*: The following commands are identical
731
+
732
+ \`\`\`js
733
+ setState('myState', 1, false);
734
+ setState('myState', { val: 1, ack: false });
735
+ setState('myState', 1);
736
+ \`\`\`
737
+
738
+ Please refer to https://github.com/ioBroker/ioBroker/wiki/Adapter-Development-Documentation#commands-and-statuses for usage of \`ack\`.
739
+ Short:
740
+ - \`ack\` = \`false\` : Script wants to send a command to be executed by the target device/adapter
741
+ - \`ack\` = \`true\` : Command was successfully executed, and state is updated as a positive result
742
+
743
+ ### setStateAsync
744
+ \`\`\`js
745
+ await setStateAsync(id, state, ack);
746
+ \`\`\`
747
+ Same as setState, but with \`promise\`.
748
+
749
+ ### setStateDelayed
750
+ \`\`\`js
751
+ setStateDelayed(id, state, isAck, delay, clearRunning, callback);
752
+ \`\`\`
753
+
754
+ Same as setState but with delay in milliseconds. You can clear all running delays for this ID (by default). E.g.
755
+
756
+ \`\`\`js
757
+ // Switch ON the light in the kitchen in one second
758
+ setStateDelayed('Kitchen.Light.Lamp', true, 1000);
759
+
760
+ // Switch OFF the light in the kitchen in 5 seconds and let first timeout run.
761
+ setStateDelayed('Kitchen.Light.Lamp', false, 5000, false, () => {
762
+ log('Lamp is OFF');
763
+ });
764
+ \`\`\`
765
+ This function returns the handler of the timer, and this timer can be individually stopped by clearStateDelayed
766
+
767
+ ### setStateChanged
768
+ \`\`\`js
769
+ await setStateChanged(id, state, ack);
770
+ \`\`\`
771
+ Same as setState, but set value only if the value is really changed.
772
+
773
+ ### setStateChangedAsync
774
+ \`\`\`js
775
+ await setStateChangedAsync(id, state, ack);
776
+ \`\`\`
777
+ Same as setStateChanged, but with \`promise\`.
778
+
779
+ ### clearStateDelayed
780
+ \`\`\`js
781
+ clearStateDelayed(id);
782
+ \`\`\`
783
+
784
+ Clears all delayed tasks for specified state ID or some specific delayed task.
785
+
786
+ \`\`\`js
787
+ setStateDelayed('Kitchen.Light.Lamp', false, 10000); // Switch OFF the light in the kitchen in ten second
788
+ let timer = setStateDelayed('Kitchen.Light.Lamp', true, 5000, false); // Switch ON the light in the kitchen in five second
789
+ clearStateDelayed('Kitchen.Light.Lamp', timer); // Nothing will be switched on
790
+ clearStateDelayed('Kitchen.Light.Lamp'); // Clear all running delayed tasks for this ID
791
+ \`\`\`
792
+
793
+ ### getStateDelayed
794
+ \`\`\`js
795
+ getStateDelayed(id);
796
+ \`\`\`
797
+
798
+ This is a synchronous call, and you will get the list of all running timers (setStateDelayed) for this id.
799
+ You can call this function without id and get timers for all IDs.
800
+ In case you call this function for some specific object ID, you will get the following answer:
801
+
802
+ \`\`\`js
803
+ getStateDelayed('hm-rpc.0.LQE91119.1.STATE');
804
+
805
+ // returns an array like
806
+ [
807
+ { timerId: 1, left: 1123, delay: 5000, val: true, ack: false },
808
+ { timerId: 2, left: 12555, delay: 15000, val: false, ack: false },
809
+ ]
810
+ \`\`\`
811
+
812
+ If you ask for all IDs, the answer will look like:
813
+
814
+ \`\`\`js
815
+ getStateDelayed();
816
+
817
+ // returns an object like
818
+ {
819
+ 'hm-rpc.0.LQE91119.1.STATE': [
820
+ { timerId: 1, left: 1123, delay: 5000, val: true, ack: false },
821
+ { timerId: 2, left: 12555, delay: 15000, val: false, ack: false },
822
+ ],
823
+ 'hm-rpc.0.LQE91119.2.LEVEL': [
824
+ { timerId: 3, left: 5679, delay: 10000, val: 100, ack: false },
825
+ ],
826
+ }
827
+ \`\`\`
828
+
829
+ - \`left\` is the time left in milliseconds
830
+ - \`delay\` is the initial delay value in milliseconds
831
+
832
+ You can ask by timerId directly. In this case, the answer will be:
833
+
834
+ \`\`\`js
835
+ getStateDelayed(3);
836
+
837
+ // returns an object like
838
+ { id: 'hm-rpc.0.LQE91119.2.LEVEL', left: 5679, delay: 10000, val: 100, ack: false }
839
+ \`\`\`
840
+
841
+ ### getState
842
+ \`\`\`js
843
+ getState(id);
844
+ \`\`\`
845
+
846
+ Returns state with the given id in the following form:
847
+
848
+ \`\`\`js
849
+ {
850
+ val: value,
851
+ ack: true/false,
852
+ ts: timestamp,
853
+ lc: lastchanged,
854
+ from: origin
855
+ }
856
+ \`\`\`
857
+
858
+ If state does not exist, a warning will be printed in the logs and the object \`{ val: null, notExist: true }\` will be returned.
859
+ To suppress the warning, check if the state exists before calling getState (see [existsState](#existsState)).
860
+
861
+ ### getStateAsync
862
+ \`\`\`js
863
+ const stateObject = await getStateAsync(id);
864
+ \`\`\`
865
+ Same as getState, but with \`promise\`.
866
+
867
+ ### existsState
868
+ \`\`\`js
869
+ existsState(id, (err, isExists) => {});
870
+ \`\`\`
871
+
872
+ If option "Do not subscribe all states on start" is deactivated, you can use simpler call:
873
+
874
+ \`\`\`js
875
+ existsState(id)
876
+ \`\`\`
877
+ the function returns in this case true or false.
878
+
879
+ Check if a state exists.
880
+
881
+ ### getObject
882
+ \`\`\`js
883
+ getObject(id, enumName);
884
+ \`\`\`
885
+ Get description of object id as stored in a system.
886
+ You can specify the enumeration name. If this is defined, two additional attributes will be added to result: enumIds and enumNames.
887
+ These arrays have all enumerations, where ID is a member of. E.g.:
888
+
889
+ \`\`\`js
890
+ getObject('adapter.N.objectName', 'rooms');
891
+ \`\`\`
892
+
893
+ gives back in enumIds all rooms, where the requested object is a member. You can define "true" as enumName to get back *all* enumerations.
894
+
895
+ ### setObject
896
+ \`\`\`js
897
+ setObject(id, obj, callback);
898
+ \`\`\`
899
+ Write an object into DB. This command can be disabled in adapter's settings. Use this function carefully, while the global settings can be damaged.
900
+
901
+ You should use it to **modify** an existing object you read beforehand, e.g.:
902
+ \`\`\`js
903
+ const obj = getObject('adapter.N.objectName');
904
+ obj.native.settings = 1;
905
+ setObject('adapter.N.objectName', obj, (err) => {
906
+ if (err) log('Cannot write object: ' + err);
907
+ });
908
+ \`\`\`
909
+
910
+ ### existsObject
911
+ \`\`\`js
912
+ existsObject(id, function (err, isExists) {});
913
+ \`\`\`
914
+
915
+ If the option "Do not subscribe all states on start" is deactivated, you can use simpler call:
916
+
917
+ \`\`\`js
918
+ existsObject(id)
919
+ \`\`\`
920
+ the function returns in this case true or false.
921
+
922
+ Check if an object exists.
923
+
924
+
925
+ ### extendObject
926
+ \`\`\`js
927
+ extendObject(id, obj, callback);
928
+ \`\`\`
929
+
930
+ It is almost the same as \`setObject\`, but first it reads the object and tries to merge all settings together.
931
+
932
+ Use it like this:
933
+ \`\`\`js
934
+ // Stop instance
935
+ extendObject('system.adapter.sayit.0', {common: {enabled: false}});
936
+ \`\`\`
937
+
938
+ ### deleteObject
939
+ \`\`\`js
940
+ deleteObject(id, isRecursive, callback);
941
+ \`\`\`
942
+
943
+ Delete an object from DB by ID. If the object has type \`state\`, the state value will be deleted too.
944
+
945
+ Additional parameter \`isRecursive\` could be provided, so all children of given ID will be deleted. Very dangerous!
946
+
947
+ Use it like this:
948
+ \`\`\`js
949
+ // Delete state
950
+ deleteObject('javascript.0.createdState');
951
+ \`\`\`
952
+
953
+ *Notice: \`isRecursive\` option is available only with js-controller >= 2.2.x*
954
+
955
+ ### getIdByName
956
+ \`\`\`js
957
+ getIdByName(name, alwaysArray);
958
+ \`\`\`
959
+
960
+ Returns id of the object with given name.
961
+ If there is more than one object with this name, the result will be an array.
962
+ If \`alwaysArray\` flag is set, the result will always be an array if some ID found.
963
+ ### getEnums
964
+ \`\`\`js
965
+ getEnums(enumName);
966
+ \`\`\`
967
+
968
+ Get the list of existing enumerations with members, like:
969
+
970
+ \`\`\`js
971
+ getEnums('rooms');
972
+
973
+ // returns all rooms - e.g.:
974
+ [
975
+ {
976
+ id: 'enum.rooms.LivingRoom',
977
+ members: [ 'hm-rpc.0.JEQ0024123.1', 'hm-rpc.0.BidCoS-RF.4' ],
978
+ name: 'Living room'
979
+ },
980
+ {
981
+ id: 'enum.rooms.Bath',
982
+ members: [ 'hm-rpc.0.JEQ0024124.1', 'hm-rpc.0.BidCoS-RF.5' ],
983
+ name: 'Bath'
984
+ }
985
+ ]
986
+
987
+ getEnums('functions');
988
+
989
+ // returns all functions - e.g.:
990
+ [
991
+ {
992
+ id: 'enum.functions.light',
993
+ members: [
994
+ '0_userdata.0.AnotherOne',
995
+ '0_userdata.0.MyLigh'
996
+ ],
997
+ name: {
998
+ en: 'Light',
999
+ ru: 'Свет',
1000
+ de: 'Licht',
1001
+ fr: 'Lumière',
1002
+ it: 'Leggero',
1003
+ nl: 'Licht',
1004
+ pl: 'Lekki',
1005
+ pt: 'Luz',
1006
+ es: 'Luz',
1007
+ 'zh-cn': '光'
1008
+ }
1009
+ }
1010
+ ]
1011
+ \`\`\`
1012
+
1013
+ ### createState
1014
+ \`\`\`js
1015
+ createState(name, initialValue, forceCreation, common, native, callback);
1016
+ \`\`\`
1017
+ Create state and object in javascript space if it does not exist, e.g. \`javascript.0.mystate\`.
1018
+
1019
+ !! Prefer to create own data points with the full ID \`0_userdata.0.mystate\` !!!
1020
+
1021
+ #### Parameters:
1022
+
1023
+ - \`name\`: name of the state without namespace, e.g. \`mystate\`
1024
+ - \`initialValue\`: variable can be initialized after created. Value "undefined" means do not initialize value.
1025
+ - \`forceCreation\`: create/overwrite state independent of if state yet exists or not.
1026
+ - \`common\`: common description of object see description [here](https://github.com/ioBroker/ioBroker/blob/master/doc/SCHEMA.md#state)
1027
+ - \`native\`: native description of an object. Any specific information.
1028
+ - \`callback\`: called after state is created and initialized.
1029
+
1030
+ If you set in \`common\` the flag \`alias\` to \`true\`, then alias will be created with the same name (but in \`alias.0\` namespace) as the state.
1031
+ Alias is created only if it does not exist yet.
1032
+
1033
+ The following settings for aliases are valid too:
1034
+ \`\`\`js
1035
+ common => {
1036
+ alias: {
1037
+ id: 'alias.0.myOtherState', // will be created automatically if not already exists
1038
+ write: 'val * 1000', // convert function for write to created state
1039
+ read: 'val / 1000' // convert function to read from created state
1040
+ }
1041
+ }
1042
+ \`\`\`
1043
+
1044
+ or
1045
+
1046
+ \`\`\`js
1047
+ common => {
1048
+ alias: {
1049
+ id: 'alias.0.myOtherState', // will be created automatically if not already exists
1050
+ }
1051
+ }
1052
+ \`\`\`
1053
+
1054
+ It is possible short type of createState:
1055
+
1056
+ - \`createState('myDatapoint')\` - simply create datapoint if it does not exist
1057
+ - \`createState('myDatapoint', 1)\` - create datapoint if it does not exist and initialize it with value 1
1058
+ - \`createState('myDatapoint', { type: 'string', role: 'json', read: true, write: false }, () => { log('created'); });\` - with common definitions like type, read, write and role
1059
+ - \`createState('myDatapoint', { name: 'My own datapoint', unit: '°C' }, () => { log('created'); });\`
1060
+ - \`createState('myDatapoint', 1, { name: 'My own datapoint', unit: '°C' })\` - create datapoint if it does not exist with specific name and units
1061
+
1062
+ #### An object in the second position is always the \`common\`
1063
+
1064
+ These short forms are the reason why an object in the second position is **never** read as an initial
1065
+ value. \`createState('myDatapoint', {}, { type: 'object' })\` therefore does not do what it looks like:
1066
+ the \`{}\` becomes the \`common\`, and \`{ type: 'object' }\` moves on to the \`native\`.
1067
+
1068
+ To give a state an initial value that is not a primitive, put it into \`common.def\`:
1069
+
1070
+ \`\`\`js
1071
+ createState('0_userdata.0.myObject', { name: 'My object', type: 'object', read: true, write: true, def: {} });
1072
+ \`\`\`
1073
+
1074
+ A state of type \`object\`, \`json\` or \`array\` keeps its value as JSON, so the state above starts out
1075
+ with the string \`'{}'\` - just as \`setState('0_userdata.0.myObject', {})\` would store it. The default
1076
+ is stringified for you; writing \`def: '{}'\` yourself works as well.
1077
+
1078
+ ### createStateAsync
1079
+ \`\`\`js
1080
+ await createStateAsync(name, initialValue, forceCreation, common, native);
1081
+ \`\`\`
1082
+
1083
+ Same as \`createState\`, but the promise will be returned.
1084
+
1085
+ ### deleteState
1086
+ \`\`\`js
1087
+ deleteState(name, callback);
1088
+ \`\`\`
1089
+ Delete state and object in javascript space, e.g. \`javascript.0.mystate\`. States from other adapters cannot be deleted.
1090
+
1091
+ \`\`\`js
1092
+ deleteState('myDatapoint')
1093
+ \`\`\`
1094
+ simply delete datapoint if exists.
1095
+
1096
+ ### deleteStateAsync
1097
+ \`\`\`js
1098
+ await deleteStateAsync(name);
1099
+ \`\`\`
1100
+
1101
+ Same as \`deleteState\`, but the promise will be returned.
1102
+
1103
+ ### createAlias
1104
+ \`\`\`js
1105
+ createAlias(name, alias, forceCreation, common, native, callback);
1106
+ \`\`\`
1107
+
1108
+ Create alias in \`alias.0\` space if it does not exist, e.g. \`javascript.0.myalias\` and reference to a state or read/write states.
1109
+ The common definition is taken from the read alias id object, but a provided common takes precedence.
1110
+
1111
+ #### Parameters:
1112
+
1113
+ - \`name\`: id of the new alias state with (possible without alias namespace), e.g. \`test.mystate\` (namespace \`alias.0.\` will be added = \`alias.0.test.mystate\`)
1114
+ - \`alias\`: can be either an existing state id as string or an object with full alias definition including read/write ids and read/write functions. Note: Alias definitions can not be set as part of the common parameter!
1115
+ - \`forceCreation\`: create/overwrite alias independent of if state yet exists or not.
1116
+ - \`common\`: common description of alias object see description [here](https://github.com/ioBroker/ioBroker/blob/master/doc/SCHEMA.md#state). Values provided here will take precedence over the common definition of the read alias id object. Not: Alias definitions can not be set as part of this common parameter, see alias parameter!
1117
+ - \`native\`: native description of an object. Any specific information.
1118
+ - \`callback\`: called after state is created and initialized.
1119
+
1120
+ It is possible a short type of createAlias:
1121
+
1122
+ - \`createAlias('myAlias', 'myDatapoint')\` - simply create alias.0.myAlias that refernces to javascript.X.myDatapoint if it does not exist
1123
+ - \`createAlias('myAlias', { id: { read: 'myReadDatapoint', write: 'myWriteDatapoint' }})\` - creates alias and reference to different read/write states
1124
+
1125
+ For other details, see createState, it is similar.
1126
+
1127
+ ### createAliasAsync
1128
+ \`\`\`js
1129
+ await createAliasAsync(name, alias, forceCreation, common, native);
1130
+ \`\`\`
1131
+
1132
+ Same as \`createAlias\`, but the promise will be returned.
1133
+
1134
+ ### sendTo
1135
+ \`\`\`js
1136
+ sendTo(adapter, command, message, callback);
1137
+ sendTo(adapter, command, message, options, callback);
1138
+ \`\`\`
1139
+
1140
+ Send a message to a specific or all adapter instances. When using the adapter name, the message is sent to all instances.
1141
+
1142
+ To get specific information about messages, you must read the documentation for a particular adapter.
1143
+
1144
+ Example (with custom timeout):
1145
+
1146
+ \`\`\`js
1147
+ sendTo('telegram', { user: 'UserName', text: 'Test message' }, { timeout: 2000 });
1148
+ \`\`\`
1149
+
1150
+ Some adapters also support responses to the sent messages. (e.g. history, sql, telegram)
1151
+ The response is only returned to the callback if the message is sent to a specific instance!
1152
+
1153
+ Example (with callback):
1154
+
1155
+ \`\`\`js
1156
+ sendTo('telegram.0', { user: 'UserName', text: 'Test message' }, (res) => {
1157
+ log(\`Sent to \${res} users\`);
1158
+ });
1159
+ \`\`\`
1160
+
1161
+ *Default timeout is 20000 milliseconds (if a callback function has been defined)*
1162
+
1163
+ \`\`\`js
1164
+ sendTo('telegram.0', { user: 'UserName', text: 'Test message' }, { timeout: 2000 }, (res) => {
1165
+ log(\`Sent to \${res} users\`);
1166
+ });
1167
+ \`\`\`
1168
+
1169
+ ### sendToAsync
1170
+ \`\`\`js
1171
+ await sendToAsync(adapter, command, message);
1172
+ await sendToAsync(adapter, command, message, options);
1173
+ \`\`\`
1174
+ Same as sendTo, but with \`promise\`.
1175
+
1176
+ Example:
1177
+
1178
+ \`\`\`js
1179
+ const res = await sendToAsync('sql.0', 'getEnabledDPs', {});
1180
+ log(JSON.stringify(res));
1181
+ \`\`\`
1182
+
1183
+ ### sendToHost
1184
+ \`\`\`js
1185
+ sendToHost(hostName, command, message, callback);
1186
+ \`\`\`
1187
+
1188
+ Send a message to controller instance.
1189
+
1190
+ The following commands are supported:
1191
+ - \`'cmdExec'\`
1192
+ - \`'getRepository'\`
1193
+ - \`'getInstalled'\`
1194
+ - \`'getVersion'\`
1195
+ - \`'getDiagData'\`
1196
+ - \`'getLocationOnDisk'\`
1197
+ - \`'getDevList'\`
1198
+ - \`'getLogs'\`
1199
+ - \`'getLogFile'\`
1200
+ - \`'getLogFiles'\`
1201
+ - \`'delLogs'\`
1202
+ - \`'getHostInfo'\`
1203
+ - \`'getHostInfoShort'\`
1204
+ - \`'updateMultihost'\`
1205
+ - \`'upgradeController'\` - Upgrade js-controller to newest version
1206
+ - \`'getInterfaces'\` - Returns all available network interfaces of the system
1207
+ - \`'upload'\` - Starts an adapter upload
1208
+ - \`'rebuildAdapter'\`
1209
+ - \`'readBaseSettings'\`
1210
+ - \`'writeBaseSettings'\`
1211
+ - \`'addNotification'\`
1212
+ - \`'clearNotifications'\`
1213
+ - \`'getNotifications'\`
1214
+ - \`'updateLicenses'\` - read licenses from iobroker.net
1215
+ - \`'upgradeOsPackages'\`
1216
+ - \`'restartController'\`
1217
+
1218
+ It is rather specific commands and are not required often.
1219
+
1220
+ Example:
1221
+
1222
+ \`\`\`js
1223
+ sendToHost('myComputer', 'cmdExec', { data: 'ls /' }, (res) => {
1224
+ log('List of files: ' + res.data);
1225
+ });
1226
+ \`\`\`
1227
+
1228
+ **Notice:** you must enable *Enable command "sendToHost"* option to call it.
1229
+
1230
+ ### sendToHostAsync
1231
+ \`\`\`js
1232
+ await sendToHostAsync(hostName, command, message);
1233
+ \`\`\`
1234
+ Same as sendToHost, but with \`promise\`.
1235
+
1236
+ ### setInterval
1237
+ \`\`\`js
1238
+ setInterval(callback, ms, arg1, arg2, arg3, arg4);
1239
+ \`\`\`
1240
+
1241
+ Same as javascript \`setInterval\`.
1242
+
1243
+ ### clearInterval
1244
+ \`\`\`js
1245
+ clearInterval(id);
1246
+ \`\`\`
1247
+
1248
+ Same as javascript \`clearInterval\`.
1249
+
1250
+ ### setTimeout
1251
+ \`\`\`js
1252
+ setTimeout(callback, ms, arg1, arg2, arg3, arg4);
1253
+ \`\`\`
1254
+ Same as javascript \`setTimeout\`.
1255
+
1256
+ ### clearTimeout
1257
+ \`\`\`js
1258
+ clearTimeout(id);
1259
+ \`\`\`
1260
+
1261
+ Same as javascript \`clearTimeout\`.
1262
+
1263
+ ### setImmediate
1264
+ \`\`\`js
1265
+ setImmediate(callback, arg1, arg2, arg3, arg4);
1266
+ \`\`\`
1267
+
1268
+ Same as javascript \`setImmediate\` and almost the same as \`setTimeout(callback, 0, arg1, arg2, arg3, arg4)\` but with higher priority.
1269
+
1270
+ ### formatDate
1271
+ \`\`\`js
1272
+ formatDate(millisecondsOrDate, format);
1273
+ \`\`\`
1274
+
1275
+ #### Parameters:
1276
+
1277
+ - \`millisecondsOrDate\`: number of milliseconds from state.ts or state.lc (Number milliseconds from 1970.01.01 00:00:00) or javascript *new Date()* object or number of milliseconds from *(new Date().getTime())*
1278
+ - \`format\`: Can be \`null\`, so the system time format will be used, otherwise
1279
+
1280
+ * YYYY, JJJJ, ГГГГ - full year, e.g 2015
1281
+ * YY, JJ, ГГ - short year, e.g 15
1282
+ * MM, ММ(cyrillic) - full month, e.g. 01
1283
+ * M, М(cyrillic) - short month, e.g., 1
1284
+ * DD, TT, ДД - full day, e.g. 02
1285
+ * D, T, Д - short day, e.g., 2
1286
+ * hh, SS, чч - full hours, e.g. 03
1287
+ * h, S, ч - short hours, e.g. 3
1288
+ * mm, мм(cyrillic) - full minutes, e.g. 04
1289
+ * m, м(cyrillic) - short minutes, e.g., 4
1290
+ * ss, сс(cyrillic) - full seconds, e.g. 05
1291
+ * s, с(cyrillic) - short seconds, e.g., 5
1292
+ * sss, ссс(cyrillic) - milliseconds
1293
+ * WW, НН(cyrillic) - full week day as text
1294
+ * W, Н(cyrillic) - short week day as text
1295
+ * OO, ОО(cyrillic) - full month as text
1296
+ * OOO, ООО(cyrillic) - full month as text as genitiv
1297
+ * O, О(cyrillic) - short month as text
1298
+
1299
+ #### Example
1300
+
1301
+ \`\`\`js
1302
+ formatDate(new Date(), "YYYY-MM-DD") // => Date "2015-02-24"
1303
+ formatDate(new Date(), "hh:mm") // => Hours and minutes "17:41"
1304
+ formatDate(state.ts) // => "24.02.2015"
1305
+ formatDate(state.ts, "JJJJ.MM.TT SS:mm:ss.sss") // => "2015.02.15 17:41:98.123"
1306
+ formatDate(new Date(), "WW") // => Day of week "Tuesday"
1307
+ formatDate(new Date(), "W") // => Day of week "Tu"
1308
+ \`\`\`
1309
+
1310
+ ### formatTimeDiff
1311
+ \`\`\`js
1312
+ formatTimeDiff(milliseconds, format);
1313
+ \`\`\`
1314
+
1315
+ #### Parameters:
1316
+
1317
+ - \`milliseconds\`: difference in milliseconds*
1318
+ - \`format\`: Can be \`null\`, so the \`hh:mm:ss\` format will be used, otherwise
1319
+
1320
+ * DD, TT, ДД - full day, e.g. "02"
1321
+ * D, T, Д - short day, e.g., "2"
1322
+ * hh, SS, чч - full hours, e.g. "03"
1323
+ * h, S, ч - short hours, e.g. "3"
1324
+ * mm, мм(cyrillic) - full minutes, e.g. "04"
1325
+ * m, м(cyrillic) - short minutes, e.g., "4"
1326
+ * ss, сс(cyrillic) - full seconds, e.g. "05"
1327
+ * s, с(cyrillic) - short seconds, e.g., "5"
1328
+
1329
+ You can use escape charachter \`\\\` to avoid the replacement. e.g. \`DD \\Day\\s, h \\hour\\s, m \\minute, ss \\second\\s\`
1330
+
1331
+ #### Example
1332
+
1333
+ \`\`\`js
1334
+ formatTimeDiff(60000, "mm:ss") // => "01:00"
1335
+
1336
+ const diff = 172800000 + 10800000 + 540000 + 15000; // 2 days, 3 hours, 9 minutes + 15 secoonds
1337
+ formatTimeDiff(diff); // "51:09:15"
1338
+ formatTimeDiff(diff, 'DD hh:mm'); // "02 03:09"
1339
+ formatTimeDiff(diff, 'D hh:mm'); // "2 03:09"
1340
+ formatTimeDiff(diff, 'hh:mm:ss'); // "51:09:15"
1341
+ formatTimeDiff(diff, 'h:m:s'); // "51:9:15"
1342
+ formatTimeDiff(diff, 'hh:mm'); // "51:09"
1343
+ formatTimeDiff(diff, 'mm:ss'); // "3069:15"
1344
+ formatTimeDiff(diff, 'hh'); // "51"
1345
+ formatTimeDiff(diff, 'mm'); // "3069"
1346
+ \`\`\`
1347
+
1348
+ ### getDateObject
1349
+ \`\`\`js
1350
+ getDateObject(stringOrNumber);
1351
+ \`\`\`
1352
+
1353
+ Converts string or number to a Date object.
1354
+ If only hours are given, it will add current date to it and will try to convert.
1355
+
1356
+ \`\`\`js
1357
+ getDateObject('20:00'); // 2024-05-18T18:00:00.000Z
1358
+ getDateObject('2024-01-01'); // 2024-01-01T00:00:00.000Z
1359
+ \`\`\`
1360
+
1361
+ ### formatValue
1362
+ \`\`\`js
1363
+ formatValue(value, decimals, format);
1364
+ \`\`\`
1365
+
1366
+ Formats any value (strings too) to number. Replaces point with comma if configured in system.
1367
+ Decimals specify digits after comma. The default value is 2.
1368
+ Format is optional:
1369
+ - '.,': 1234.567 => 1.234,56
1370
+ - ',.': 1234.567 => 1,234.56
1371
+ - ' .': 1234.567 => 1 234.56
1372
+
1373
+
1374
+ ### adapterSubscribe
1375
+ \`\`\`js
1376
+ adapterSubscribe(id);
1377
+ \`\`\`
1378
+
1379
+ Send to an adapter message "subscribe" to inform adapter. If adapter has the common flag "subscribable" in case of function "subscribe" this function will be called automatically.
1380
+
1381
+ ### adapterUnsubscribe
1382
+ \`\`\`js
1383
+ adapterUnsubscribe(id);
1384
+ \`\`\`
1385
+
1386
+ Sends to an adapter the message \`unsubscribe\` to inform adapter to not poll the values.
1387
+
1388
+ ### $ - Selector
1389
+ \`\`\`js
1390
+ $(selector).on((obj) => {}); // Register a subscription for each matching state
1391
+ $(selector).toArray(); // Get all matching object IDs of the selector expression (requires version >= 8.2.0)
1392
+ $(selector).each((id, i) => {}); // iterate over all matching states
1393
+ $(selector).setState(value, ack, callback); // set state value of all matching object IDs (callback is optional)
1394
+ $(selector).setStateAsync(value, ack); // set state value of all matching object IDs - returns a promise
1395
+ $(selector).setStateChanged(value, ack, callback); // set state value of all matching object IDs if value has changed (callback is optional)
1396
+ $(selector).setStateChangedAsync(value, ack, callback); // set state value of all matching object IDs if value has changed - returns a promise
1397
+ $(selector).setStateDelayed(state, isAck, delay, clearRunning, callback); // // set state value of all matching object IDs with a given delay
1398
+ $(selector).getState(); // get all states
1399
+ $(selector).getStateAsync(); // get all states - returns a promise
1400
+ \`\`\`
1401
+
1402
+ Format of selector:
1403
+ \`\`\`js
1404
+ "name[commonAttr=something1](enumName=something2){nativeName=something3}[id=idfilter][state.id=idfilter]"
1405
+ \`\`\`
1406
+
1407
+ name can be: state, channel, device or schedule
1408
+ \`idfilter\` can have wildcards '*'
1409
+
1410
+ Prefixes ***(not implemented - should be discussed)*** :
1411
+
1412
+ * \\# - take by name and not by id
1413
+ * . - filter by role
1414
+ * § - filter by room
1415
+
1416
+ ***Example***:
1417
+
1418
+ - \`$('state[id=*.STATE]')\` or \`$('state[state.id=*.STATE]')\` or \`$('*.STATE')\` - select all states where id ends with ".STATE".
1419
+ - \`$('state[id='hm-rpc.0.*]')\` or \`$('hm-rpc.0.*')\` - returns all states of adapter instance hm-rpc.0
1420
+ - \`$('channel(rooms=Living room)')\` - all states in room "Living room"
1421
+ - \`$('channel{TYPE=BLIND}[state.id=*.LEVEL]')\` - Get all shutters of Homematic
1422
+ - \`$('channel[role=switch](rooms=Living room)[state.id=*.STATE]').setState(false)\` - Switch all states with .STATE of channels with role "switch" in "Living room" to false
1423
+ - \`$('channel[state.id=*.STATE](functions=Windows)').each(function (id, i) {log(id);});\` - print all states of enum "windows" in log
1424
+ - \`$('schedule[id=*65]').each(function (id, i) {log(id);});\` - print all schedules with 65 at the end
1425
+ - \`$('.switch §"Living room")\` - Take states with all switches in 'Living room' ***(not implemented - should be discussed)***
1426
+ - \`$('channel .switch §"Living room")\` - Take states with all switches in 'Living room' ***(not implemented - should be discussed)***
1427
+
1428
+ ***Explanation***
1429
+ Lets take a look at:
1430
+ \`\`\`js
1431
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').on(obj => {
1432
+ log('New state ' + obj.id + ' = ' + obj.state.val);
1433
+ });
1434
+ \`\`\`
1435
+
1436
+ This code searches in channels.
1437
+ Find all channels with \`common.role="switch"\` and belongs to \`enum.rooms.Wohnzimmer\`.
1438
+ Take all their states, where id ends with \`".STATE"\` and make subscription on all these states.
1439
+ If some of these states change, the callback will be called like for "on" function.
1440
+
1441
+ Following functions are possible, setState, getState (only from first), on, each, toArray
1442
+
1443
+ \`\`\`js
1444
+ // Switch on all switches in "Wohnzimmer"
1445
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').setState(true);
1446
+ \`\`\`
1447
+
1448
+ You can interrupt the "each" loop by returning the false value, like:
1449
+ \`\`\`js
1450
+ // print two first IDs of on all switches in "Wohnzimmer"
1451
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').each((id, i) => {
1452
+ log(id);
1453
+ if (i == 1) {
1454
+ return false;
1455
+ }
1456
+ });
1457
+ \`\`\`
1458
+ Or you can get a an usual array of ids and process it your own way:
1459
+ \`\`\`js
1460
+ // get some state and filter only which has an \`true\` value
1461
+ const enabled = $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').toArray().filter((id) => getState(id)?.val === true);
1462
+ \`\`\`
1463
+
1464
+ ### readFile
1465
+ \`\`\`js
1466
+ readFile(adapter, fileName, (error, bytes) => {});
1467
+ \`\`\`
1468
+
1469
+ The result will be given in callback.
1470
+ Read file from DB from folder \`javascript.0\`.
1471
+
1472
+ Argument *adapter* can be omitted.
1473
+
1474
+ \`\`\`js
1475
+ // read vis views
1476
+ readFile('vis.0', '/main/vis-views.json', (error, data) => {
1477
+ log(data.substring(0, 50));
1478
+ });
1479
+
1480
+ // The same as
1481
+ //readFile('/../vis.0/main/vis-views.json', (error, data) => {
1482
+ // log(data.substring(0, 50));
1483
+ //});
1484
+ \`\`\`
1485
+
1486
+ By default, working directory/adapter is \`javascript.0\`.
1487
+
1488
+ ### writeFile
1489
+ \`\`\`js
1490
+ writeFile(adapter, fileName, bytes, (error) => {});
1491
+ \`\`\`
1492
+
1493
+ The optional error code will be given in callback. Argument *adapter* can be omitted.
1494
+ fileName is the name of file in DB. All files are stored in the folder "javascript".
1495
+ if you want to write to other folders, e.g. to "/vis.0/" use setFile for that.
1496
+
1497
+ The file that looks like \`'/subfolder/file.txt'\` will be stored under \`"/javascript/subfolder/file.txt"\` and can be accessed over web server with \`"http://ip:8082/javascript/subfolder/file.txt"\`
1498
+
1499
+ \`\`\`js
1500
+ // store screenshot in DB
1501
+ const fs = require('node:fs');
1502
+ let data = fs.readFileSync('/tmp/screenshot.png');
1503
+ writeFile(null, '/screenshots/1.png', data, (error) => {
1504
+ log('file written');
1505
+ });
1506
+
1507
+ // The same as
1508
+ //writeFile('/screenshots/1.png', data, function (error) {
1509
+ // log('file written');
1510
+ //});
1511
+ \`\`\`
1512
+
1513
+ \`\`\`js
1514
+ // store file in '/vis.0' in DB
1515
+ const fs = require('node:fs');
1516
+ let data = fs.readFileSync('/tmp/screenshot.png');
1517
+ writeFile('vis.0', '/screenshots/1.png', data, (error) => {
1518
+ log('file written');
1519
+ });
1520
+ \`\`\`
1521
+
1522
+ ### delFile
1523
+ \`\`\`js
1524
+ delFile(adapter, fileName, (error) => {});
1525
+ \`\`\`
1526
+
1527
+ Delete file or directory. fileName is the name of file or directory in DB.
1528
+
1529
+ The alternative name of this method is \`unlink\`
1530
+
1531
+ ### renameFile
1532
+ \`\`\`js
1533
+ renameFile(adapter, oldName, newName, (error) => {});
1534
+ \`\`\`
1535
+
1536
+ Rename file or directory. oldName is the name of file or directory in DB and is renamed to newName.
1537
+
1538
+ The alternative name of this method is \`rename\`
1539
+
1540
+ ### onFile
1541
+ \`\`\`js
1542
+ onFile(id, fileName, withFile, (id, fileName, size, fileData, mimeType) => {});
1543
+ // or
1544
+ onFile(id, fileName, (id, fileName, size) => {});
1545
+ \`\`\`
1546
+
1547
+ Subscribe to file changes:
1548
+ - \`id\` is ID of an object of type \`meta\`, like \`vis.0\`
1549
+ - \`fileName\` is file name or pattern, like \`main/*\` or \`main/vis-view.json\`
1550
+ - \`withFile\` if the content of file should be delivered in callback or not. the delivery of file content costs memory and time, so if you want to be just informed about changes, set \`withFile\`to false.
1551
+
1552
+ Arguments in callback:
1553
+ - \`id\` - ID of \`meta\` object;
1554
+ - \`fileName\` - file name (not pattern);
1555
+ - \`size\` - new file size;
1556
+ - \`fileData\` - file content of type \`Buffer\` if file is binary (detected by extension) or \`string\`. Delivered only if \`withFile\`;
1557
+ - \`mimeType\` - mime type of file, like \`image/jpeg\`. Delivered only if \`withFile\`;
1558
+
1559
+ **Important**: this functionality is only available with js-controller@4.1.x or newer.
1560
+
1561
+ ### offFile
1562
+ \`\`\`js
1563
+ offFile(id, fileName);
1564
+ // or
1565
+ onFile(id, fileName);
1566
+ \`\`\`
1567
+ Unsubscribe from file changes:
1568
+ - \`id\` is ID of an object of type \`meta\`, like \`vis.0\`
1569
+ - \`fileName\` is file name or pattern, like \`main/*\` or \`main/vis-view.json\`
1570
+
1571
+ **Important**: this functionality is only available with js-controller@4.1.x or newer.
1572
+
1573
+ ### onStop
1574
+ \`\`\`js
1575
+ onStop (() => { /* do something when script is stopped */ }, timeout);
1576
+ \`\`\`
1577
+ Install callback, that will be called if a script stopped. Used, e.g., to stop communication or to close connections.
1578
+
1579
+ \`\`\`js
1580
+ // establish connection
1581
+ const conn = require('net');
1582
+ // ...
1583
+
1584
+ // close connection if script stopped
1585
+ onStop((callback) => {
1586
+ if (conn) {
1587
+ // close connection
1588
+ conn.destroy();
1589
+ }
1590
+ callback();
1591
+ }, 2000 /*ms*/);
1592
+ \`\`\`
1593
+ \`timeout\` is 1000ms by default.
1594
+
1595
+ ### getHistory
1596
+ \`\`\`js
1597
+ getHistory(instance, options, (error, result, options, instance) => {});
1598
+ \`\`\`
1599
+
1600
+ Read history from specified instance. If no instance is specified, the system default history instance will be taken.
1601
+ \`\`\`js
1602
+ // Read history of 'system.adapter.admin.0.memRss' from sql driver
1603
+ const end = new Date().getTime();
1604
+ getHistory(
1605
+ 'sql.0',
1606
+ {
1607
+ id: 'system.adapter.admin.0.memRss',
1608
+ start: end - 3600000,
1609
+ end: end,
1610
+ aggregate: 'm4',
1611
+ timeout: 2000
1612
+ },
1613
+ (err, result) => {
1614
+ if (err) console.error(err);
1615
+ if (result) {
1616
+ for (let i = 0; i < result.length; i++) {
1617
+ log(result[i].id + ' ' + new Date(result[i].ts).toISOString());
1618
+ }
1619
+ }
1620
+ }
1621
+ );
1622
+ \`\`\`
1623
+
1624
+ Possible options you can find [here](https://github.com/ioBroker/ioBroker.history#access-values-from-javascript-adapter).
1625
+
1626
+ Additionally, to these parameters you must specify "id" and you may specify timeout (default: 20000ms).
1627
+
1628
+ One more example:
1629
+ \`\`\`js
1630
+ // Get last 50 entries from default history instance with no aggregation:
1631
+ getHistory({
1632
+ id: 'system.adapter.admin.0.alive',
1633
+ aggregate: 'none',
1634
+ count: 50
1635
+ }, (err, result) => {
1636
+ if (err) console.error(err);
1637
+ if (result) {
1638
+ for (let i = 0; i < result.length; i++) {
1639
+ log(result[i].id + ' ' + new Date(result[i].ts).toISOString());
1640
+ }
1641
+ }
1642
+ });
1643
+ \`\`\`
1644
+
1645
+ **Note: ** of course, history must be first enabled for selected ID in admin.
1646
+
1647
+ ### runScript
1648
+ \`\`\`js
1649
+ runScript('scriptName', () => {
1650
+ // Callback is optional
1651
+ log('Srcipt started, but not yet executed');
1652
+ });
1653
+ \`\`\`
1654
+
1655
+ Starts or restarts other scripts (and itself too) by name.
1656
+
1657
+ \`\`\`js
1658
+ // restart script
1659
+ runScript('groupName.scriptName1');
1660
+ \`\`\`
1661
+
1662
+ ### runScriptAsync
1663
+ Same as runScript, but with \`promise\`.
1664
+ \`\`\`js
1665
+ runScriptAsync('scriptName')
1666
+ .then(() => log('Script started, but not yet executed'));
1667
+
1668
+ // or
1669
+
1670
+ await runScriptAsync('scriptName');
1671
+ log(\`Script was restarted\`);
1672
+ \`\`\`
1673
+
1674
+ ### startScript
1675
+ \`\`\`js
1676
+ startScript('scriptName', ignoreIfStarted, callback);
1677
+ \`\`\`
1678
+
1679
+ Starts the script. If ignoreIfStarted set to true, nothing will be done if a script yet running, otherwise the script will be restarted.
1680
+
1681
+ \`\`\`js
1682
+ startScript('scriptName', true); // start script if not started
1683
+ \`\`\`
1684
+
1685
+ ### startScriptAsync
1686
+ Same as runScript, but with \`promise\`.
1687
+
1688
+ \`\`\`js
1689
+ startScriptAsync('scriptName', ignoreIfStarted)
1690
+ .then(started => log(\`Script was \${started ? 'started' : 'already started'}\`));
1691
+
1692
+ // or
1693
+
1694
+ const started = await startScriptAsync('scriptName', ignoreIfStarted);
1695
+ log(\`Script was \${started ? 'started' : 'already started'}\`);
1696
+ \`\`\`
1697
+
1698
+ Starts the script. If ignoreIfStarted set to true, nothing will be done if a script yet running, otherwise the script will be restarted.
1699
+
1700
+ \`\`\`js
1701
+ startScript('scriptName', true); // start script if not started
1702
+ \`\`\`
1703
+
1704
+ ### stopScript
1705
+ \`\`\`js
1706
+ stopScript('scriptName', callback);
1707
+ \`\`\`
1708
+
1709
+ If stopScript is called without arguments, it will stop itself:
1710
+
1711
+ \`\`\`js
1712
+ stopScript();
1713
+ \`\`\`
1714
+
1715
+ ### stopScriptAsync
1716
+ Same as stopScript, but with \`promise\`:
1717
+ \`\`\`js
1718
+ stopScriptAsync('scriptName')
1719
+ .then(stopped => log(\`Script was \${stopped ? 'stopped' : 'already stopped'}\`));
1720
+
1721
+ //or
1722
+ const stopped = await stopScriptAsync('scriptName');
1723
+ log(\`Script was \${stopped ? 'stopped' : 'already stopped'}\`);
1724
+ \`\`\`
1725
+
1726
+ If stopScript is called without arguments, it will stop itself:
1727
+
1728
+ \`\`\`js
1729
+ stopScript();
1730
+ \`\`\`
1731
+
1732
+ ### isScriptActive
1733
+ \`\`\`js
1734
+ isScriptActive('scriptName');
1735
+ \`\`\`
1736
+
1737
+ Returns if a script enabled or disabled. Please note that that does not give back if the script is now running or not.
1738
+ The script can be finished, but still activated.
1739
+
1740
+ It is not a function. It is a variable with javascript instance, that is visible in script's scope.
1741
+
1742
+ ### toInt
1743
+ ### toFloat
1744
+ ### toBoolean
1745
+ ### jsonataExpression
1746
+
1747
+ ### wait
1748
+ Just pause the execution of the script.
1749
+ Warning this function is \`promise\` and must be called as follows:
1750
+ \`\`\`js
1751
+ await wait(1000);
1752
+ \`\`\`
1753
+
1754
+ ### sleep
1755
+ Same as [wait](#wait)
1756
+
1757
+ ### messageTo
1758
+ \`\`\`js
1759
+ messageTo({ instance: 'instance', script: 'script.js.common.scriptName', message: 'messageName' }, data, { timeout: 1000 }, result =>
1760
+ log(JSON.stringify(result)));
1761
+ \`\`\`
1762
+
1763
+ Send via the "message bus" the message to some other script. Or even to some handler in the same script.
1764
+
1765
+ Timeout for callback is 5 seconds by default.
1766
+
1767
+ The target could be shorted to:
1768
+
1769
+ \`\`\`js
1770
+ messageTo('messageName', data, (result) => {
1771
+ log(JSON.stringify(result));
1772
+ });
1773
+ \`\`\`
1774
+
1775
+ Callback and options are optional and timeout is by default 5000 milliseconds (if callback provided).
1776
+
1777
+ \`\`\`js
1778
+ messageTo('messageName', dataWithNoResponse);
1779
+ \`\`\`
1780
+
1781
+ ### messageToAsync
1782
+ \`\`\`js
1783
+ onMessage('myTopic', async (data, callback) => {
1784
+ log(data);
1785
+
1786
+ if (!data.myPayload) {
1787
+ // return error (promise reject)
1788
+ callback({ error: 'something went wrong!!' });
1789
+ } else {
1790
+ // return result (promise resolve)
1791
+ callback({ result: 'ok' });
1792
+ }
1793
+ });
1794
+
1795
+ (async () => {
1796
+ try {
1797
+ const msg = await messageToAsync({ instance: 0, script: 'script.js.test2', message: 'myTopic' }, { myPayload: true }, { timeout: 1000 });
1798
+ log(\`Done with: \${JSON.stringify(msg)}\`);
1799
+ } catch (error) {
1800
+ // contents of result.error
1801
+ console.error(error);
1802
+ }
1803
+ })();
1804
+ \`\`\`
1805
+
1806
+ ### onMessage
1807
+ \`\`\`js
1808
+ onMessage('messageName', (data, callback) => {
1809
+ log(\`Received data: \${data}\`);
1810
+
1811
+ callback({ result: Date.now() });
1812
+ });
1813
+ \`\`\`
1814
+
1815
+ Subscribes on \`javascript\` adapter message bus and delivers response via callback.
1816
+ The response from script which sends response as first will be accepted as answer, all other answers will be ignored.
1817
+
1818
+ To send a message to a JavaScript script which is then received by this handler, use [messageTo](#messageTo).
1819
+
1820
+ To send a message from any other adapter use
1821
+
1822
+ \`\`\`js
1823
+ adapter.sendTo('javascript.0', 'toScript', {
1824
+ script: 'script.js.messagetest',
1825
+ message: 'messageName',
1826
+ data: {
1827
+ flag: true
1828
+ }
1829
+ });
1830
+ \`\`\`
1831
+
1832
+ to send a message from CLI use
1833
+
1834
+ \`\`\`bash
1835
+ iob message javascript.0 toScript '{"script": "script.js.messagetest", "message": "messageName", "data": { "flag": true }}'
1836
+ \`\`\`
1837
+
1838
+ ### onMessageUnregister
1839
+ \`\`\`js
1840
+ const id = onMessage('messageName', (data, callback) => {
1841
+ log(data);
1842
+ callback({ result: Date.now() });
1843
+ });
1844
+
1845
+ // unsubscribe specific handler
1846
+ onMessageUnregister(id);
1847
+ // or unsubscribe by name
1848
+ onMessageUnregister('messageName');
1849
+ \`\`\`
1850
+
1851
+ Unsubscribes from this message.
1852
+
1853
+ ### onLog
1854
+ \`\`\`js
1855
+ onLog('error', data => {
1856
+ sendTo('telegram.0', { user: 'UserName', text: data.message });
1857
+ log('Following was sent to telegram: ' + data.message);
1858
+ });
1859
+ \`\`\`
1860
+
1861
+ Subscribe on logs with specified severity.
1862
+
1863
+ *Important:* you cannot output logs in handler with the same severity to avoid infinite loops.
1864
+
1865
+ E.g., this will produce no logs:
1866
+ \`\`\`js
1867
+ onLog('error', data => {
1868
+ console.error('Error: ' + data.message);
1869
+ });
1870
+ \`\`\`
1871
+
1872
+ To receive all logs the \`*\` could be used. In this case, the log output in handler will be disabled completely.
1873
+
1874
+ \`\`\`js
1875
+ onLog('*', data => {
1876
+ console.error('Error: ' + data.message); // will produce no logs
1877
+ });
1878
+ \`\`\`
1879
+
1880
+ ### onLogUnregister
1881
+ \`\`\`js
1882
+ function logHandler(data) {
1883
+ console.error('Error: ' + data.message);
1884
+ }
1885
+ const id = onLog('warn', logHandler);
1886
+
1887
+ // unsubscribe by ID
1888
+ onLogUnregister(id);
1889
+ // or unsubscribe by function handler
1890
+ onLogUnregister(logHandler);
1891
+ // or unsubscribe all handlers with specific severity
1892
+ onLogUnregister('warn');
1893
+ \`\`\`
1894
+
1895
+ Unsubscribes from these logs.
1896
+
1897
+ ### httpGet
1898
+
1899
+ *Requires version >= 7.9.0*
1900
+
1901
+ \`\`\`js
1902
+ httpGet('http://jsonplaceholder.typicode.com/posts', (err, response) => {
1903
+ if (!err) {
1904
+ console.log(response.statusCode);
1905
+ console.log(response.data);
1906
+ } else {
1907
+ console.error(err);
1908
+ }
1909
+ });
1910
+ \`\`\`
1911
+
1912
+ The second parameter can be an object with further options (optional). All options are optional. Supported flags:
1913
+
1914
+ - \`timeout\` (number) - Timeout in milliseconds
1915
+ - \`responseType\` (string) - Supported values are \`text\` (default) or \`arraybuffer\` for binary data in the response
1916
+ - \`basicAuth\` (object) - HTTP basic authentication credentials. e.g. \`{ user: 'admin', password: 'iobroker' }\`
1917
+ - \`bearerAuth\` (string) - Token for bearer authentication
1918
+ - \`headers\` (object) - Additional custom HTTP headers e.g. \`{ 'Accept-Language': 'en-GB,en;q=0.9' }\`
1919
+ - \`validateCertificate\` (boolean) - Allows self-signed certificates when \`false\`
1920
+
1921
+ \`\`\`js
1922
+ httpGet('http://jsonplaceholder.typicode.com/posts', { timeout: 1000 }, (err, response) => {
1923
+ if (!err) {
1924
+ console.log(response.statusCode);
1925
+ console.log(response.data);
1926
+ } else {
1927
+ console.error(err);
1928
+ }
1929
+ });
1930
+ \`\`\`
1931
+
1932
+ Download file to ioBroker file system:
1933
+
1934
+ \`\`\`js
1935
+ httpGet('http://1.2.3.4/image.jpg', { responseType: 'arraybuffer' }, async (err, response) => {
1936
+ if (!err) {
1937
+ writeFile('0_userdata.0', 'test.jpg', response.data, (err) => {
1938
+ if (err) {
1939
+ console.error(err);
1940
+ }
1941
+ });
1942
+ } else {
1943
+ console.error(err);
1944
+ }
1945
+ });
1946
+ \`\`\`
1947
+
1948
+ Disable certificate validation - *Requires version >= 8.4.0*
1949
+
1950
+ \`\`\`js
1951
+ httpGet('http://jsonplaceholder.typicode.com/posts', { validateCertificate: false }, (err, response) => {
1952
+ if (!err) {
1953
+ console.log(response.statusCode);
1954
+ console.log(response.data);
1955
+ } else {
1956
+ console.error(err);
1957
+ }
1958
+ });
1959
+ \`\`\`
1960
+
1961
+ ### httpPost
1962
+
1963
+ *Requires version >= 7.9.0*
1964
+
1965
+ \`\`\`js
1966
+ httpPost('http://jsonplaceholder.typicode.com/posts', { title: 'foo', body: 'bar', userId: 1 }, (error, response) => {
1967
+ if (!error) {
1968
+ console.log(response.statusCode);
1969
+ console.log(response.data);
1970
+ console.log(response.headers);
1971
+ } else {
1972
+ console.error(error);
1973
+ }
1974
+ });
1975
+ \`\`\`
1976
+
1977
+ With custom headers and authentication
1978
+
1979
+ \`\`\`js
1980
+ httpPost(
1981
+ 'http://jsonplaceholder.typicode.com/posts',
1982
+ {
1983
+ title: 'foo',
1984
+ body: 'bar',
1985
+ userId: 1
1986
+ },
1987
+ {
1988
+ timeout: 2000,
1989
+ basicAuth: {
1990
+ user: 'admin',
1991
+ password: 'dg2LdALNznHFNo'
1992
+ },
1993
+ headers: {
1994
+ 'Cookie': 'PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1'
1995
+ }
1996
+ },
1997
+ (error, response) => {
1998
+ if (!error) {
1999
+ console.log(response.statusCode);
2000
+ console.log(response.data);
2001
+ console.log(response.headers);
2002
+ } else {
2003
+ console.error(error);
2004
+ }
2005
+ }
2006
+ );
2007
+ \`\`\`
2008
+
2009
+ ### createTempFile
2010
+
2011
+ *Requires version >= 8.3.0*
2012
+
2013
+ \`\`\`js
2014
+ httpGet('https://raw.githubusercontent.com/ioBroker/ioBroker.javascript/master/admin/javascript.svg', { responseType: 'arraybuffer' }, async (err, response) => {
2015
+ if (err) {
2016
+ console.error(err);
2017
+ } else {
2018
+ const tempFilePath = createTempFile('javascript.svg', response.data);
2019
+ console.log(\`Saved to \${tempFilePath}\`);
2020
+
2021
+ // Use the new path in other scripts (e.g. sendTo)
2022
+ }
2023
+ });
2024
+ \`\`\`
2025
+
2026
+ \`\`\`js
2027
+ onFile('0_userdata.0', '*.jpg', true, async (id, fileName, size, data, mimeType) => {
2028
+ const tempFilePath = createTempFile(fileName, response.data);
2029
+
2030
+ // Use the new path in other scripts (e.g. sendTo)
2031
+ });
2032
+ \`\`\`
2033
+
2034
+ \`\`\`js
2035
+ readFile('0_userdata.0', 'test.jpg', (err, data, mimeType) => {
2036
+ if (err) {
2037
+ console.error(err);
2038
+ } else {
2039
+ const tempFilePath = createTempFile('test.jpg', data);
2040
+
2041
+ // Use the new path in other scripts (e.g. sendTo)
2042
+ sendTo('telegram.0', 'send', {
2043
+ text: tempFilePath,
2044
+ caption: 'Just a test image',
2045
+ user: 'yourUsername',
2046
+ });
2047
+ }
2048
+ });
2049
+ \`\`\`
2050
+
2051
+ ### registerNotification
2052
+
2053
+ *Requires version >= 8.8.0*
2054
+
2055
+ \`\`\`js
2056
+ registerNotification('This is just an information'); // Notify
2057
+ registerNotification('This is an important message!', true); // Alert
2058
+ \`\`\`
2059
+
2060
+ ## Global script variables
2061
+ ### scriptName
2062
+ \`scriptName\` - The name of the script.
2063
+
2064
+ \`\`\`js
2065
+ log(\`Script \${scriptName} started!\`);
2066
+ \`\`\`
2067
+
2068
+ ### instance
2069
+ \`instance\` - The javascript instance where a script is executed (e.g. \`0\`).
2070
+
2071
+ \`\`\`js
2072
+ log(\`Script \${scriptName} started started by \${instance}\`);
2073
+ \`\`\`
2074
+
2075
+ ### defaultDataDir
2076
+ \`defaultDataDir\` - Absolute path to iobroker-data.
2077
+
2078
+ \`\`\`js
2079
+ log(\`Data dir: \${defaultDataDir}\`);
2080
+ \`\`\`
2081
+
2082
+ ### verbose
2083
+ \`verbose\` - Verbose mode enabled?
2084
+
2085
+ \`\`\`js
2086
+ log(\`Verbose mode: \${verbose ? 'enabled' : 'disabled'}\`);
2087
+
2088
+ // Example
2089
+ if (verbose) {
2090
+ log('...');
2091
+ }
2092
+ \`\`\`
2093
+
2094
+ ### SECRETS
2095
+ \`SECRETS\` - The credentials of the central ioBroker credential storage.
2096
+
2097
+ The credentials are managed in the admin UI under **Basic settings** -> **Credentials**. Every credential
2098
+ has an ID (like \`CameraPassword\`) and holds either a single **key** (e.g. an API key or a password) or a
2099
+ **login**/**password** pair. The secret fields are stored encrypted with the system secret and are handed
2100
+ to the scripts already decrypted:
2101
+
2102
+ \`\`\`js
2103
+ // credential of the type "key"
2104
+ httpGet(\`http://camera.local/snapshot?password=\${SECRETS.CameraPassword.key}\`, (err, result) => {
2105
+ // ...
2106
+ });
2107
+
2108
+ // credential of the type "login"
2109
+ log(\`Mail account: \${SECRETS.MyMailAccount.login} / \${SECRETS.MyMailAccount.password}\`);
2110
+
2111
+ // credential IDs that are no valid variable names
2112
+ log(SECRETS['My camera'].key);
2113
+ \`\`\`
2114
+
2115
+ \`SECRETS\` is read-only and always up to date: if a credential is added, changed or deleted in the admin UI,
2116
+ the new value is used immediately - neither the adapter nor the script must be restarted.
2117
+
2118
+ If a credential does not exist, \`undefined\` is returned:
2119
+
2120
+ \`\`\`js
2121
+ if (SECRETS.CameraPassword) {
2122
+ log('The camera password is defined');
2123
+ }
2124
+ \`\`\`
2125
+
2126
+ #### Which fields does a credential have?
2127
+
2128
+ Every credential has either a single \`key\` or a \`login\`/\`password\` pair. Three ways to find out which:
2129
+
2130
+ - In the instance settings of the JavaScript adapter, the section **Available credentials** lists every
2131
+ credential with its fields and the ready-to-copy expression.
2132
+ - In the editor, the auto-completion after \`SECRETS.\` offers the credentials that exist, and after the
2133
+ next dot exactly the fields that credential has.
2134
+ - In a script:
2135
+
2136
+ \`\`\`js
2137
+ log(JSON.stringify(Object.keys(SECRETS.CameraPassword))); // ["key"]
2138
+ log(JSON.stringify(Object.keys(SECRETS.MyMailAccount))); // ["login","password"]
2139
+ \`\`\`
2140
+
2141
+ Blockly has a **credential** block for the same purpose - see the
2142
+ [Blockly documentation](blockly.md#credential).
2143
+
2144
+ The access can be switched off with the instance option **Allow scripts to read the credentials**.
2145
+ \`SECRETS\` is then empty and a warning is written to the log.
2146
+
2147
+ **Note:** this requires js-controller 7.2 or newer.
2148
+
2149
+ ## Option - "Do not subscribe all states on start"
2150
+ There are two modes of subscribing to states:
2151
+
2152
+ 1. Adapter subscribes to all states at start and receives all changes of all states (it is easy to use getState(id), but requires more CPU and RAM):
2153
+
2154
+ \`\`\`js
2155
+ log(getState('someID').val);
2156
+ \`\`\`
2157
+
2158
+ 2. Adapter subscribes every time on specified ID when \`on/subscribe\` is called. In this mode, the adapter receives only updates for desired states. This option requires less RAM and is more efficient, but you cannot access states synchronously via getState. **You must use callbacks or promises to access the states**:
2159
+
2160
+ \`\`\`js
2161
+ getState('someID', (error, state) => {
2162
+ log(state.val);
2163
+ });
2164
+ \`\`\`
2165
+
2166
+ Reason: The adapter does not have the state's value in RAM and must request it from the central state database.
2167
+
2168
+ ## Scripts activity
2169
+
2170
+ There is a possibility to enable and disable scripts via states. For every script, the state will be created with the name \`javascript.INSTANCE.scriptEnabled.SCRIPT_NAME\`.
2171
+ Scripts can be activated and deactivated by controlling this state with \`ack=false\`.
2172
+ `,q=`## Inhalt
2173
+ - [Globale Funktionen](#globale-funktionen)
2174
+ - [Best Practice](#best-practice)
2175
+
2176
+ - [Funktionen](#die-folgenden-funktionen-können-in-skripten-verwendet-werden)
2177
+ - [require - Modul laden](#require---modul-laden)
2178
+ - [console - Gibt die Nachricht im Log aus](#console---gibt-die-nachricht-im-log-aus)
2179
+ - [exec - Führt ein Betriebssystem-Kommando aus, z. B. "cp file1 file2"](#exec---führt-ein-betriebssystem-kommando-aus-z-b-cp-file1-file2)
2180
+ - [on - Änderungen oder Aktualisierungen eines States abonnieren](#on---änderungen-oder-aktualisierungen-eines-states-abonnieren)
2181
+ - [once](#once)
2182
+ - [subscribe - wie on](#subscribe---wie-on)
2183
+ - [unsubscribe](#unsubscribe)
2184
+ - [getSubscriptions](#getsubscriptions)
2185
+ - [getFileSubscriptions](#getfilesubscriptions)
2186
+ - [schedule](#schedule)
2187
+ - [Zeitplan](#zeitplan)
2188
+ - [Astro-Funktion](#astro-funktion)
2189
+ - [scheduleById](#schedulebyid)
2190
+ - [getSchedules](#getschedules)
2191
+ - [clearSchedule](#clearschedule)
2192
+ - [getAttr](#getattr)
2193
+ - [getAstroDate](#getastrodate)
2194
+ - [isAstroDay](#isastroday)
2195
+ - [compareTime](#comparetime)
2196
+ - [setState](#setstate)
2197
+ - [setStateAsync](#setstateasync)
2198
+ - [setStateDelayed](#setstatedelayed)
2199
+ - [clearStateDelayed](#clearstatedelayed)
2200
+ - [getStateDelayed](#getstatedelayed)
2201
+ - [getState](#getstate)
2202
+ - [getStateAsync](#getstateasync)
2203
+ - [existsState](#existsstate)
2204
+ - [getObject](#getobject)
2205
+ - [setObject](#setobject)
2206
+ - [existsObject](#existsobject)
2207
+ - [extendObject](#extendobject)
2208
+ - [deleteObject](#deleteobject)
2209
+ - [getIdByName](#getidbyname)
2210
+ - [getEnums](#getenums)
2211
+ - [createState](#createstate)
2212
+ - [createStateAsync](#createstateasync)
2213
+ - [deleteState](#deletestate)
2214
+ - [deleteStateAsync](#deletestateasync)
2215
+ - [sendTo](#sendto)
2216
+ - [sendToAsync](#sendtoasync)
2217
+ - [sendToHost](#sendtohost)
2218
+ - [sendToHostAsync](#sendtohostasync)
2219
+ - [setInterval](#setinterval)
2220
+ - [clearInterval](#clearinterval)
2221
+ - [setTimeout](#settimeout)
2222
+ - [clearTimeout](#cleartimeout)
2223
+ - [setImmediate](#setimmediate)
2224
+ - [formatDate](#formatdate)
2225
+ - [formatTimeDiff](#formattimediff)
2226
+ - [getDateObject](#getdateobject)
2227
+ - [formatValue](#formatvalue)
2228
+ - [adapterSubscribe](#adaptersubscribe)
2229
+ - [adapterUnsubscribe](#adapterunsubscribe)
2230
+ - [$ - Selektor](#---selektor)
2231
+ - [readFile](#readfile)
2232
+ - [writeFile](#writefile)
2233
+ - [delFile](#delfile)
2234
+ - [renameFile](#renamefile)
2235
+ - [onFile](#onfile)
2236
+ - [offFile](#offfile)
2237
+ - [onStop](#onstop)
2238
+ - [getHistory](#gethistory)
2239
+ - [runScript](#runscript)
2240
+ - [runScriptAsync](#runscriptasync)
2241
+ - [startScript](#startscript)
2242
+ - [startScriptAsync](#startscriptasync)
2243
+ - [stopScript](#stopscript)
2244
+ - [stopScriptAsync](#stopscriptasync)
2245
+ - [isScriptActive](#isscriptactive)
2246
+ - [name](#scriptname)
2247
+ - [instance](#instance)
2248
+ - [SECRETS](#secrets)
2249
+ - [messageTo](#messageto)
2250
+ - [messageToAsync](#messagetoasync)
2251
+ - [onMessage](#onmessage)
2252
+ - [onMessageUnregister](#onmessageunregister)
2253
+ - [onLog](#onlog)
2254
+ - [onLogUnregister](#onlogunregister)
2255
+ - [wait](#wait)
2256
+ - [sleep](#sleep)
2257
+ - [httpGet](#httpget)
2258
+ - [httpPost](#httppost)
2259
+ - [createTempFile](#createtempfile)
2260
+ - [registerNotification](#registernotification)
2261
+
2262
+ - [Skriptaktivität](#skriptaktivität)
2263
+
2264
+ ## Globale Funktionen
2265
+ Globale Skripte können im Ordner \`global\` definiert werden.
2266
+ Alle globalen Skripte stehen in allen Instanzen zur Verfügung. Ist ein globales Skript deaktiviert, wird es nicht verwendet.
2267
+ Ein globales Skript wird einfach dem normalen Skript vorangestellt und zusammen mit diesem kompiliert. Daher können über globale Skripte keine Daten zwischen Skripten ausgetauscht werden. Dafür sollten States verwendet werden.
2268
+
2269
+ Um globale Funktionen in TypeScript zu verwenden, müssen sie zuerst mit \`declare\` deklariert werden, damit der Compiler die globalen Funktionen kennt. Beispiel:
2270
+ \`\`\`typescript
2271
+ // globales Skript:
2272
+ // ================
2273
+ function globalFn(arg: string): void {
2274
+ // eigentliche Implementierung
2275
+ }
2276
+
2277
+ // normales Skript:
2278
+ // ================
2279
+ declare function globalFn(arg: string): void;
2280
+ // wie gewohnt verwenden:
2281
+ globalFn('test');
2282
+ \`\`\`
2283
+
2284
+ #### Best Practice:
2285
+ Es empfiehlt sich, zwei Instanzen des JavaScript-Adapters anzulegen: eine "test"- und eine "production"-Instanz.
2286
+ Nachdem das Skript in der "test"-Instanz getestet wurde, kann es nach "production" verschoben werden. So lässt sich die "test"-Instanz beliebig neu starten.
2287
+
2288
+ ## Die folgenden Funktionen können in Skripten verwendet werden:
2289
+
2290
+ ### require - Modul laden
2291
+ \`\`\`js
2292
+ const mod = require('module_name');
2293
+ \`\`\`
2294
+ Folgende Module sind bereits vorgeladen: \`node:dgram\`, \`node:crypto\`, \`node:dns\`, \`node:events\`, \`node:fs\`, \`node:http\`, \`node:https\`, \`node:http2\`, \`node:net\`, \`node:os\`, \`node:path\`, \`node:util\`, \`node:stream\`, \`node:zlib\`, \`suncalc2\`, \`axios\`, \`wake_on_lan\`, \`request\` (veraltet)
2295
+
2296
+ Um andere Module zu verwenden, trägt man den Namen (und die Version) des Moduls in der Instanzkonfiguration ein. ioBroker installiert das Modul dann. Anschließend kann es in den Skripten geladen (require) und verwendet werden.
2297
+
2298
+ ### console - Gibt die Nachricht im Log aus
2299
+ Die Verwendung ist dieselbe wie in \`javascript\`
2300
+
2301
+ ### exec - Führt ein Betriebssystem-Kommando aus, z. B. \`cp file1 file2\`
2302
+ \`\`\`js
2303
+ exec(cmd, [options], callback);
2304
+ \`\`\`
2305
+
2306
+ Führt ein Systemkommando aus und liefert dessen Ausgaben.
2307
+
2308
+ \`\`\`js
2309
+ // Liste der Dateien und Verzeichnisse in /var/log abrufen
2310
+ exec('ls /var/log', (error, stdout, stderr) => {
2311
+ log('stdout: ' + stdout);
2312
+ });
2313
+ \`\`\`
2314
+
2315
+ Node.js verwendet /bin/sh zum Ausführen von Kommandos. Soll eine andere Shell verwendet werden, kann das Optionsobjekt genutzt werden, wie in der [Node.js-Dokumentation](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) zu child_process.exec beschrieben.
2316
+ Es empfiehlt sich, bei Kommandos immer vollständige Pfadnamen anzugeben, damit sichergestellt ist, dass das richtige Kommando ausgeführt wird.
2317
+
2318
+ **Hinweis:** Um diese Funktion aufzurufen, muss die Option *Kommando "exec" erlauben* aktiviert sein.
2319
+
2320
+ ### on - Änderungen oder Aktualisierungen eines States abonnieren
2321
+ \`\`\`js
2322
+ on(pattern, callbackOrId, value);
2323
+ \`\`\`
2324
+
2325
+ Die Callback-Funktion erhält als Parameter ein Objekt mit folgendem Inhalt:
2326
+ \`\`\`js
2327
+ {
2328
+ id: 'javascript.0.myplayer',
2329
+ state: {
2330
+ val: 'new state',
2331
+ ts: 1416149118,
2332
+ ack: true,
2333
+ lc: 1416149118,
2334
+ from: 'system.adapter.sonos.0'
2335
+ },
2336
+ oldState: {
2337
+ val: 'old state',
2338
+ ts: 1416148233,
2339
+ ack: true,
2340
+ lc: 1416145154,
2341
+ from: 'system.adapter.sonos.0'
2342
+ }
2343
+ }
2344
+ \`\`\`
2345
+
2346
+ **Hinweis:** \`state\` hieß früher \`newState\`. Diese Bezeichnung funktioniert weiterhin.
2347
+
2348
+ Beispiel:
2349
+ \`\`\`js
2350
+ let timer;
2351
+
2352
+ // State "javascript.0.counter" anlegen
2353
+ createState('counter', 0);
2354
+
2355
+ // Bei Änderung
2356
+ on('adapter.0.device.channel.sensor', (data) => {
2357
+ // Aber nicht öfter als alle 30 Sekunden
2358
+ if (!timer) {
2359
+ timer = setTimeout(() => {
2360
+ timer = null;
2361
+ }, 30000);
2362
+
2363
+ // Bestätigten Wert setzen
2364
+ setState('counter', 1 + getState('counter'), true);
2365
+
2366
+ // Oder ein unbestätigtes Kommando setzen
2367
+ setState('adapter.0.device.channel.actor', true);
2368
+ }
2369
+ });
2370
+ \`\`\`
2371
+
2372
+ Folgende Parameter können zur Definition des Triggers verwendet werden:
2373
+
2374
+ | Parameter | Typ/Wert | Beschreibung |
2375
+ |-------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
2376
+ | logic | string | Logik "and" oder "or" zur Verknüpfung der Bedingungen \\(Standard: "and"\\) |
2377
+ | | | |
2378
+ | id | string | ID ist gleich der angegebenen |
2379
+ | | RegExp | ID entspricht dem regulären Ausdruck |
2380
+ | | Array | ID ist in einer Liste erlaubter IDs enthalten |
2381
+ | | | |
2382
+ | name | string | Name ist gleich dem angegebenen |
2383
+ | | RegExp | Name entspricht dem regulären Ausdruck |
2384
+ | | Array | Name ist in einer Liste erlaubter Namen enthalten |
2385
+ | | | |
2386
+ | change | string | "eq", "ne", "gt", "ge", "lt", "le", "any" |
2387
+ | | "eq" | (gleich) Neuer Wert muss gleich dem alten sein (state.val == oldState.val) |
2388
+ | | "ne" | (ungleich) Neuer Wert muss ungleich dem alten sein (state.val != oldState.val) **Ist das Muster ein ID-String, wird dieser Wert standardmäßig verwendet** |
2389
+ | | "gt" | (größer) Neuer Wert muss größer als der alte sein (state.val > oldState.val) |
2390
+ | | "ge" | (größer oder gleich) Neuer Wert muss größer oder gleich dem alten sein (state.val >= oldState.val) |
2391
+ | | "lt" | (kleiner) Neuer Wert muss kleiner als der alte sein (state.val < oldState.val) |
2392
+ | | "le" | (kleiner oder gleich) Neuer Wert muss kleiner oder gleich dem alten sein (state.val <= oldState.val) |
2393
+ | | "any" | Trigger wird ausgelöst, sobald ein neuer Wert eintrifft |
2394
+ | | | |
2395
+ | val | mixed | Neuer Wert muss gleich dem angegebenen sein |
2396
+ | valNe | mixed | Neuer Wert muss ungleich dem angegebenen sein |
2397
+ | valGt | mixed | Neuer Wert muss größer als der angegebene sein |
2398
+ | valGe | mixed | Neuer Wert muss größer oder gleich dem angegebenen sein |
2399
+ | valLt | mixed | Neuer Wert muss kleiner als der angegebene sein |
2400
+ | valLe | mixed | Neuer Wert muss kleiner oder gleich dem angegebenen sein |
2401
+ | | | |
2402
+ | ack | boolean | Bestätigungsstatus (ack) des neuen Werts ist gleich dem angegebenen |
2403
+ | q | number | Qualitätscode des neuen Werts ist gleich dem angegebenen. Mit '*' passt jeder Code. **Ist q nicht angegeben, wird q = 0 als Muster gesetzt!** |
2404
+ | | | |
2405
+ | oldVal | mixed | Vorheriger Wert muss gleich dem angegebenen sein |
2406
+ | oldValNe | mixed | Vorheriger Wert muss ungleich dem angegebenen sein |
2407
+ | oldValGt | mixed | Vorheriger Wert muss größer als der angegebene sein |
2408
+ | oldValGe | mixed | Vorheriger Wert muss größer oder gleich dem angegebenen sein |
2409
+ | oldValLt | mixed | Vorheriger Wert muss kleiner als der angegebene sein |
2410
+ | oldValLe | mixed | Vorheriger Wert muss kleiner oder gleich dem angegebenen sein |
2411
+ | | | |
2412
+ | oldAck | bool | Bestätigungsstatus (ack) des vorherigen Werts ist gleich dem angegebenen |
2413
+ | oldQ | number | Qualitätscode des vorherigen Werts ist gleich dem angegebenen. Mit '*' passt jeder Code |
2414
+ | | | |
2415
+ | ts | number | Zeitstempel des neuen Werts muss gleich dem angegebenen sein (state.ts == ts) |
2416
+ | tsGt | number | Zeitstempel des neuen Werts muss größer als der angegebene sein (state.ts > ts) |
2417
+ | tsGe | number | Zeitstempel des neuen Werts muss größer oder gleich dem angegebenen sein (state.ts >= ts) |
2418
+ | tsLt | number | Zeitstempel des neuen Werts muss kleiner als der angegebene sein (state.ts < ts) |
2419
+ | tsLe | number | Zeitstempel des neuen Werts muss kleiner oder gleich dem angegebenen sein (state.ts <= ts) |
2420
+ | | | |
2421
+ | oldTs | number | Vorheriger Zeitstempel muss gleich dem angegebenen sein (oldState.ts == ts) |
2422
+ | oldTsGt | number | Vorheriger Zeitstempel muss größer als der angegebene sein (oldState.ts > ts) |
2423
+ | oldTsGe | number | Vorheriger Zeitstempel muss größer oder gleich dem angegebenen sein (oldState.ts >= ts) |
2424
+ | oldTsLt | number | Vorheriger Zeitstempel muss kleiner als der angegebene sein (oldState.ts < ts) |
2425
+ | oldTsLe | number | Vorheriger Zeitstempel muss kleiner oder gleich dem angegebenen sein (oldState.ts <= ts) |
2426
+ | | | |
2427
+ | lc | number | Zeitstempel der letzten Änderung muss gleich dem angegebenen sein (state.lc == lc) |
2428
+ | lcGt | number | Zeitstempel der letzten Änderung muss größer als der angegebene sein (state.lc > lc) |
2429
+ | lcGe | number | Zeitstempel der letzten Änderung muss größer oder gleich dem angegebenen sein (state.lc >= lc) |
2430
+ | lcLt | number | Zeitstempel der letzten Änderung muss kleiner als der angegebene sein (state.lc < lc) |
2431
+ | lcLe | number | Zeitstempel der letzten Änderung muss kleiner oder gleich dem angegebenen sein (state.lc <= lc) |
2432
+ | | | |
2433
+ | oldLc | number | Vorheriger Zeitstempel der letzten Änderung muss gleich dem angegebenen sein (oldState.lc == lc) |
2434
+ | oldLcGt | number | Vorheriger Zeitstempel der letzten Änderung muss größer als der angegebene sein (oldState.lc > lc) |
2435
+ | oldLcGe | number | Vorheriger Zeitstempel der letzten Änderung muss größer oder gleich dem angegebenen sein (oldState.lc >= lc) |
2436
+ | oldLcLt | number | Vorheriger Zeitstempel der letzten Änderung muss kleiner als der angegebene sein (oldState.lc < lc) |
2437
+ | oldLcLe | number | Vorheriger Zeitstempel der letzten Änderung muss kleiner oder gleich dem angegebenen sein (oldState.lc <= lc) |
2438
+ | | | |
2439
+ | channelId | string | Kanal-ID muss gleich der angegebenen sein |
2440
+ | | RegExp | Kanal-ID entspricht dem regulären Ausdruck |
2441
+ | | Array | Kanal-ID ist in einer Liste erlaubter Kanal-IDs enthalten |
2442
+ | | | |
2443
+ | channelName | string | Kanalname muss gleich dem angegebenen sein |
2444
+ | | RegExp | Kanalname entspricht dem regulären Ausdruck |
2445
+ | | Array | Kanalname ist in einer Liste erlaubter Kanalnamen enthalten |
2446
+ | | | |
2447
+ | deviceId | string | Geräte-ID muss gleich der angegebenen sein |
2448
+ | | RegExp | Geräte-ID entspricht dem regulären Ausdruck |
2449
+ | | Array | Geräte-ID ist in einer Liste erlaubter Geräte-IDs enthalten |
2450
+ | | | |
2451
+ | deviceName | string | Gerätename muss gleich dem angegebenen sein |
2452
+ | | RegExp | Gerätename entspricht dem regulären Ausdruck |
2453
+ | | Array | Gerätename ist in einer Liste erlaubter Gerätenamen enthalten |
2454
+ | | | |
2455
+ | enumId | string | State gehört zum angegebenen Enum |
2456
+ | | RegExp | Eine Enum-ID des States entspricht dem angegebenen regulären Ausdruck |
2457
+ | | Array | Eine Enum-ID des States ist in der angegebenen Liste von Enum-IDs enthalten |
2458
+ | | | |
2459
+ | enumName | string | State gehört zum angegebenen Enum |
2460
+ | | RegExp | Ein Enum-Name des States entspricht dem angegebenen regulären Ausdruck |
2461
+ | | Array | Ein Enum-Name des States ist in der angegebenen Liste von Enum-Namen enthalten |
2462
+ | | | |
2463
+ | from | string | Neuer Wert stammt vom angegebenen Adapter |
2464
+ | | RegExp | Neuer Wert stammt von einem Adapter, der dem regulären Ausdruck entspricht |
2465
+ | | Array | Neuer Wert stammt von einem Adapter aus der angegebenen Liste erlaubter Adapter |
2466
+ | | | |
2467
+ | fromNe | string | Neuer Wert stammt nicht vom angegebenen Adapter |
2468
+ | | RegExp | Neuer Wert stammt nicht von einem Adapter, der dem regulären Ausdruck entspricht |
2469
+ | | Array | Neuer Wert stammt nicht von einem Adapter aus der angegebenen Liste verbotener Adapter |
2470
+ | | | |
2471
+ | oldFrom | string | Alter Wert stammt vom angegebenen Adapter |
2472
+ | | RegExp | Alter Wert stammt von einem Adapter, der dem regulären Ausdruck entspricht |
2473
+ | | Array | Alter Wert stammt von einem Adapter aus der angegebenen Liste erlaubter Adapter |
2474
+ | | | |
2475
+ | oldFromNe | string | Alter Wert stammt nicht vom angegebenen Adapter |
2476
+ | | RegExp | Alter Wert stammt nicht von einem Adapter, der dem regulären Ausdruck entspricht |
2477
+ | | Array | Alter Wert stammt nicht von einem Adapter aus der angegebenen Liste verbotener Adapter |
2478
+
2479
+ Beispiele:
2480
+ Trigger auf alle States mit der ID \`'*.STATE'\`, wenn sie bestätigt (ack) sind und den neuen Wert \`true\` haben.
2481
+
2482
+ \`\`\`js
2483
+ {
2484
+ "id": /\\.STATE$/,
2485
+ "val": true,
2486
+ "ack": true,
2487
+ "logic": "and"
2488
+ }
2489
+ \`\`\`
2490
+
2491
+ **Hinweis:** RegExp kann direkt verwendet werden:
2492
+
2493
+ \`\`\`js
2494
+ on(/^system\\.adapter\\..*\\.\\d+\\.memRss$/, function (obj) {
2495
+ });
2496
+
2497
+ // entspricht
2498
+ on({id: /^system\\.adapter\\..*\\.\\d+\\.memRss$/, change: "ne"}, function (obj) {
2499
+ });
2500
+ \`\`\`
2501
+
2502
+ Um zwei States einfach miteinander zu verbinden, schreibt man:
2503
+ \`\`\`js
2504
+ on('stateId1', 'stateId2');
2505
+ \`\`\`
2506
+
2507
+ Alle Änderungen von *stateId1* werden in *stateId2* geschrieben.
2508
+
2509
+ Ist der Parameter \`value\` in Kombination mit einer State-ID als zweitem Parameter gesetzt, wird der State bei jeder Änderung mit \`value\` befüllt.
2510
+ \`\`\`js
2511
+ on('stateId1', 'stateId2', 'triggered');
2512
+ setState('stateId1', 'new value');
2513
+
2514
+ // stateId2 wird auf 'triggered' gesetzt.
2515
+ \`\`\`
2516
+
2517
+ Die Funktion \`on\` gibt einen Handler zurück. Dieser Handler kann zum Beenden des Abonnements verwendet werden.
2518
+
2519
+ *Hinweis:* Standardmäßig werden nur States mit der Qualität 0x00 an die Callback-Funktion übergeben. Um alle Ereignisse zu erhalten, muss \`{q: '*'}\` zur Muster-Struktur hinzugefügt werden.
2520
+
2521
+ *Hinweis:* Standardmäßig ist "change" gleich "any", außer wenn nur eine ID als String angegeben wird (wie \`on('id', () => {});\`). Im letzteren Fall wird change auf "ne" gesetzt.
2522
+
2523
+ *Hinweis:* Sollen auch das Löschen oder Ablaufen von States als Trigger erkannt werden, muss change mit \`ne\` oder \`any\` UND q mit \`*\` als Filter verwendet werden!
2524
+
2525
+ *Hinweis:* Ab Version 4.3.2 kann der Trigger-Typ als zweiter Parameter angegeben werden: \`on('my.id.0', 'any', obj => log(obj.state.val));\`
2526
+
2527
+ ### once
2528
+ Registriert ein einmaliges Abonnement, das nach dem ersten Aufruf automatisch beendet wird. Wie [on](#on---änderungen-oder-aktualisierungen-eines-states-abonnieren), wird aber nur einmal ausgeführt.
2529
+
2530
+ \`\`\`js
2531
+ once(pattern, callback);
2532
+ \`\`\`
2533
+
2534
+ ### subscribe - wie **[on](#on---änderungen-oder-aktualisierungen-eines-states-abonnieren)**
2535
+
2536
+ ### unsubscribe
2537
+ \`\`\`js
2538
+ unsubscribe(id);
2539
+ // oder
2540
+ unsubscribe(handler);
2541
+ \`\`\`
2542
+
2543
+ Entfernt alle Abonnements für die angegebene Objekt-ID oder den angegebenen Handler.
2544
+
2545
+ \`\`\`js
2546
+ // Per Handler
2547
+ let mySubscription = on({ id: 'javascript.0.myState', change: 'any' }, (data) => {
2548
+ // Abonnement nach dem ersten Trigger beenden
2549
+ if (unsubscribe(mySubscription)) {
2550
+ log('Subscription deleted');
2551
+ }
2552
+ });
2553
+
2554
+ // Per Objekt-ID
2555
+ on({ id: 'javascript.0.myState1', change: 'ne' }, (data) => {
2556
+ log('Some event');
2557
+ });
2558
+
2559
+ on({ id: 'javascript.0.myState1', change: 'any' }, (data) => {
2560
+ // Abonnement beenden
2561
+ if (unsubscribe('javascript.0.myState1')) {
2562
+ log('All subscriptions deleted');
2563
+ }
2564
+ });
2565
+ \`\`\`
2566
+
2567
+ ### getSubscriptions
2568
+ Liefert die Liste der Abonnements.
2569
+
2570
+ Beispiel für ein Ergebnis:
2571
+ \`\`\`js
2572
+ {
2573
+ 'megad.0.dataPointName': [
2574
+ {
2575
+ name : 'script.js.NameOfScript',
2576
+ pattern : {
2577
+ id : 'megad.0.dataPointName',
2578
+ change : 'ne'
2579
+ }
2580
+ }
2581
+ ]
2582
+ }
2583
+ \`\`\`
2584
+
2585
+ ### getFileSubscriptions
2586
+ Liefert die Liste der Datei-Abonnements.
2587
+
2588
+ Beispiel für ein Ergebnis:
2589
+ \`\`\`js
2590
+ {
2591
+ 'vis.0$%$main/*': [
2592
+ {
2593
+ name : 'script.js.NameOfScript',
2594
+ id : 'vis.0',
2595
+ fileNamePattern: 'main/*'
2596
+ }
2597
+ ]
2598
+ }
2599
+ \`\`\`
2600
+
2601
+ ### schedule
2602
+ \`\`\`js
2603
+ schedule(pattern, callback);
2604
+ \`\`\`
2605
+
2606
+ Zeitplaner mit Astro-Funktion.
2607
+
2608
+ #### Zeitplan
2609
+ Das Muster kann ein String mit [Cron-Syntax](http://en.wikipedia.org/wiki/Cron) sein, der aus 5 (ohne Sekunden) oder 6 (mit Sekunden) Stellen besteht:
2610
+ \`\`\`
2611
+ * * * * * *
2612
+ │ │ │ │ │ │
2613
+ │ │ │ │ │ │
2614
+ │ │ │ │ │ └───── Wochentag (0 - 6) (0 bis 6 entspricht Sonntag bis Samstag, alternativ Namen verwenden; 7 ist Sonntag, wie 0)
2615
+ │ │ │ │ └────────── Monat (1 - 12)
2616
+ │ │ │ └─────────────── Tag des Monats (1 - 31)
2617
+ │ │ └──────────────────── Stunde (0 - 23)
2618
+ │ └───────────────────────── Minute (0 - 59)
2619
+ └───────────────────────────── [optional] Sekunde (0 - 59)
2620
+ \`\`\`
2621
+
2622
+ \`\`\`js
2623
+ // Beispiel mit 5 Stellen:
2624
+ schedule('*/2 * * * *', () => {
2625
+ log('Will be triggered every 2 minutes!');
2626
+ });
2627
+
2628
+ // Beispiel mit 6 Stellen:
2629
+ schedule('*/3 * * * * *', () => {
2630
+ log('Will be triggered every 3 seconds!');
2631
+ });
2632
+ \`\`\`
2633
+
2634
+ Das Muster kann auch ein Objekt sein. Das wird vor allem verwendet, wenn Sekunden benötigt werden:
2635
+
2636
+ Das Objekt kann folgende Eigenschaften haben:
2637
+ - \`second\`
2638
+ - \`minute\`
2639
+ - \`hour\`
2640
+ - \`date\`
2641
+ - \`month\`
2642
+ - \`year\`
2643
+ - \`dayOfWeek\`
2644
+
2645
+ \`\`\`js
2646
+ schedule({ second: [20, 25] }, () => {
2647
+ log('Will be triggered at xx:xx:20 and xx:xx:25 of every minute!');
2648
+ });
2649
+
2650
+ schedule({ hour: 12, minute: 30 }, () => {
2651
+ log('Will be triggered at 12:30!');
2652
+ });
2653
+ \`\`\`
2654
+ Das Muster kann auch ein JavaScript-Date-Objekt (ein bestimmter Zeitpunkt) sein - in diesem Fall wird der Zeitplan nur ein einziges Mal ausgelöst.
2655
+
2656
+ Werden Start- oder Endzeiten für einen Zeitplan benötigt, lässt sich das ebenfalls mit einem Objekt umsetzen. In diesem Fall hat das Objekt folgende Eigenschaften:
2657
+ - \`start\`
2658
+ - \`end\`
2659
+ - \`rule\`
2660
+
2661
+ start und end legen jeweils ein Date-Objekt, einen Datums-String oder eine Anzahl von Millisekunden seit dem 01. Januar 1970 00:00:00 UTC fest.
2662
+ Die Regel (rule) ist ein Zeitplan-String mit [Cron-Syntax](http://en.wikipedia.org/wiki/Cron) oder ein Objekt:
2663
+ \`\`\`js
2664
+ let startTime = new Date(Date.now() + 5000);
2665
+ let endTime = new Date(startTime.getTime() + 5000);
2666
+ schedule({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, () => {
2667
+ log('It will run after 5 seconds and stop after 10 seconds');
2668
+ });
2669
+ \`\`\`
2670
+
2671
+ Die Regel selbst kann auch ein Objekt sein:
2672
+
2673
+ \`\`\`js
2674
+ let today = new Date();
2675
+ let startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
2676
+ let endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
2677
+ let ruleData = { hour: 12, minute: 30 };
2678
+ schedule({ start: startTime, end: endTime, rule: ruleData }, () => {
2679
+ log('Will be triggered at 12:30, starting tomorow, ending in 7 days');
2680
+ });
2681
+ \`\`\`
2682
+
2683
+ #### Astro-Funktion
2684
+
2685
+ Die Astro-Funktion kann über das Attribut "astro" verwendet werden:
2686
+
2687
+ \`\`\`js
2688
+ schedule({ astro: 'sunrise' }, () => {
2689
+ log("Sunrise!");
2690
+ });
2691
+
2692
+ schedule({ astro: 'sunset', shift: 10 }, () => {
2693
+ log("10 minutes after sunset!");
2694
+ });
2695
+ \`\`\`
2696
+
2697
+ Das Attribut "shift" ist der Versatz in Minuten. Er kann auch negativ sein, um einen Zeitpunkt vor dem Astro-Ereignis festzulegen.
2698
+
2699
+ Folgende Werte können als Attribut in der Astro-Funktion verwendet werden:
2700
+
2701
+ - \`"sunrise"\`: Sonnenaufgang (die Oberkante der Sonne erscheint am Horizont)
2702
+ - \`"sunriseEnd"\`: Ende des Sonnenaufgangs (die Unterkante der Sonne berührt den Horizont)
2703
+ - \`"goldenHourEnd"\`: Ende der morgendlichen goldenen Stunde (weiches Licht, die beste Zeit zum Fotografieren)
2704
+ - \`"solarNoon"\`: Sonnenmittag (die Sonne steht am höchsten)
2705
+ - \`"goldenHour"\`: Beginn der abendlichen goldenen Stunde
2706
+ - \`"sunsetStart"\`: Beginn des Sonnenuntergangs (die Unterkante der Sonne berührt den Horizont)
2707
+ - \`"sunset"\`: Sonnenuntergang (die Sonne verschwindet unter dem Horizont, die abendliche bürgerliche Dämmerung beginnt)
2708
+ - \`"dusk"\`: Abenddämmerung (die abendliche nautische Dämmerung beginnt)
2709
+ - \`"nauticalDusk"\`: nautische Abenddämmerung (die abendliche astronomische Dämmerung beginnt)
2710
+ - \`"night"\`: Beginn der Nacht (dunkel genug für astronomische Beobachtungen)
2711
+ - \`"nightEnd"\`: Ende der Nacht (die morgendliche astronomische Dämmerung beginnt)
2712
+ - \`"nauticalDawn"\`: nautische Morgendämmerung (die morgendliche nautische Dämmerung beginnt)
2713
+ - \`"dawn"\`: Morgendämmerung (die morgendliche nautische Dämmerung endet, die morgendliche bürgerliche Dämmerung beginnt)
2714
+ - \`"nadir"\`: Nadir (der dunkelste Moment der Nacht, die Sonne steht am tiefsten)
2715
+
2716
+ **Hinweis:** Um die "astro"-Funktion zu verwenden, müssen "latitude" und "longitude" (Breiten- und Längengrad) in den Einstellungen des JavaScript-Adapters definiert sein.
2717
+
2718
+ **Hinweis:** An manchen Orten kann es vorkommen, dass kein night/nightEnd existiert. Mehr dazu [hier](https://github.com/mourner/suncalc/issues/70).
2719
+
2720
+ **Hinweis:** Die Funktion "on" kann mit einer kleinen Anpassung auch für Zeitpläne verwendet werden:
2721
+ \`\`\`js
2722
+ on({ time: '*/2 * * * *' }, () => {
2723
+ log((new Date()).toString() + " - Will be triggered every 2 minutes!");
2724
+ });
2725
+
2726
+ on({ time: { hour: 12, minute: 30 }}, () => {
2727
+ log((new Date()).toString() + " - Will be triggered at 12:30!");
2728
+ });
2729
+
2730
+ on({ astro: 'sunset', shift: 10 }, () => {
2731
+ log((new Date()).toString() + " - 10 minutes after sunset!");
2732
+ });
2733
+ \`\`\`
2734
+ ### scheduleById
2735
+ \`\`\`js
2736
+ scheduleById(id, callback);
2737
+ scheduleById(id, ack, callback);
2738
+ \`\`\`
2739
+
2740
+ Ermöglicht das Anlegen eines Zeitplans auf Basis eines State-Werts. Ändert sich der State-Wert, wird der alte Zeitplan gelöscht und automatisch ein neuer Zeitplan angelegt.
2741
+
2742
+ Unterstützte Formate:
2743
+
2744
+ - \`[h]h:[m]m:ss\` (z. B. \`12:42:15\`, \`15:3:12\`, \`3:10:25\`)
2745
+ - \`[h]h:[m]m\` (z. B. \`13:37\`, \`9:40\`)
2746
+
2747
+ \`\`\`js
2748
+ scheduleById('0_userdata.0.configurableTimeFormat', () => {
2749
+ log('Executed!');
2750
+ });
2751
+ \`\`\`
2752
+
2753
+ Beispiel: State anlegen und bei Änderungen einen Zeitplan registrieren:
2754
+
2755
+ \`\`\`js
2756
+ createState(
2757
+ '0_userdata.0.myTime',
2758
+ '00:00:00', // Standardwert
2759
+ {
2760
+ type: 'string',
2761
+ read: true,
2762
+ write: true
2763
+ },
2764
+ () => {
2765
+ scheduleById('0_userdata.0.myTime', () => {
2766
+ log('Executed!');
2767
+ });
2768
+ }
2769
+ );
2770
+ \`\`\`
2771
+
2772
+ ### getSchedules
2773
+ \`\`\`js
2774
+ const list = getSchedules(true);
2775
+ \`\`\`
2776
+ Liefert die Liste aller CRON-Jobs und Zeitpläne (außer Astro).
2777
+ Das Argument muss \`true\` sein, wenn man die Liste für **jedes laufende Skript** erhalten möchte. Andernfalls werden nur die Zeitpläne des aktuellen Skripts zurückgegeben.
2778
+
2779
+ \`\`\`js
2780
+ const list = getSchedules(true);
2781
+ list.forEach(schedule => log(JSON.stringify(schedule)));
2782
+
2783
+ // Alle Zeitpläne in allen Skripten löschen!
2784
+ list.forEach(schedule => clearSchedule(schedule));
2785
+ \`\`\`
2786
+
2787
+ Beispielausgabe:
2788
+ \`\`\`
2789
+ 2020-11-01 20:15:19.929 - {"type":"cron","pattern":"0 * * * *","scriptName":"script.js.Heizung","id":"cron_1604258108384_74924"}
2790
+ 2020-11-01 20:15:19.931 - {"type":"schedule","schedule":"{"period":{}}","scriptName":"script.js.Heizung","id":"schedule_19576"}
2791
+ \`\`\`
2792
+
2793
+ ### clearSchedule
2794
+ Wenn **keine** "astro"-Funktion verwendet wird, kann man den Zeitplan später abbrechen. Dazu muss das Zeitplan-Objekt gespeichert werden:
2795
+
2796
+ \`\`\`js
2797
+ let sch = schedule('*/2 * * * *', () => { /* ... */ });
2798
+
2799
+ // später:
2800
+ clearSchedule(sch);
2801
+ \`\`\`
2802
+
2803
+ \`clearSchedule\` akzeptiert alles, was \`schedule\` zurückgibt (ein CRON-Job-Objekt oder die ID eines Zeitplans aus dem Zeit-Assistenten)
2804
+ und außerdem die Einträge von [getSchedules](#getschedules):
2805
+
2806
+ \`\`\`js
2807
+ // Alle Zeitpläne dieses Skripts löschen
2808
+ getSchedules().forEach(sch => clearSchedule(sch));
2809
+ \`\`\`
2810
+
2811
+ Die Funktion gibt \`true\` zurück, wenn der Zeitplan gefunden und gelöscht wurde, andernfalls \`false\`.
2812
+ Zeitpläne, die mit der Astro-Option angelegt wurden, können auf diese Weise nicht gelöscht werden.
2813
+
2814
+ ### getAttr
2815
+ \`\`\`js
2816
+ getAttr({ attr1: { attr2: 5 } }, 'attr1.attr2');
2817
+ \`\`\`
2818
+ Liefert ein Attribut des Objekts. Der Pfad zum Attribut kann verschachtelt sein, wie im Beispiel.
2819
+
2820
+ Ist das erste Attribut ein String, versucht die Funktion, den String als JSON zu parsen.
2821
+
2822
+ ### getAstroDate
2823
+ \`\`\`js
2824
+ getAstroDate(pattern, date, offsetMinutes);
2825
+ \`\`\`
2826
+ Liefert ein JavaScript-Date-Objekt für den angegebenen Astro-Namen (z. B. \`"sunrise"\` oder \`"sunriseEnd"\`). Die gültigen Werte sind in der Liste der erlaubten Werte im Abschnitt [Astro](#astro-funktion) der Funktion *schedule* aufgeführt.
2827
+
2828
+ Das zurückgegebene Date-Objekt wird für das übergebene *date* berechnet. Wird kein Datum angegeben, wird der aktuelle Tag verwendet.
2829
+
2830
+ \`\`\`js
2831
+ let sunriseEnd = getAstroDate('sunriseEnd');
2832
+ log(\`Sunrise ends today at \${sunriseEnd.toLocaleTimeString()}\`);
2833
+
2834
+ let today = new Date();
2835
+ let tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
2836
+ let tomorrowNight = getAstroDate('night', tomorrow);
2837
+ \`\`\`
2838
+
2839
+ **Hinweis: Abhängig vom geografischen Standort kann es vorkommen, dass z. B. 'night'/'nightEnd' zu bestimmten Zeitpunkten nicht existieren (z. B. an nördlichen Standorten jedes Jahr im Mai/Juni)!**
2840
+
2841
+ Mit Webseiten wie [suncalc.net](http://suncalc.net) kann man prüfen, ob die Zeitpunkte korrekt sind.
2842
+
2843
+ ### isAstroDay
2844
+ \`\`\`js
2845
+ isAstroDay();
2846
+ \`\`\`
2847
+ Gibt \`true\` zurück, wenn die aktuelle Zeit zwischen dem astronomischen Sonnenaufgang und Sonnenuntergang liegt.
2848
+
2849
+ ### compareTime
2850
+ \`\`\`js
2851
+ compareTime(startTime, endTime, operation, timeToCompare);
2852
+ \`\`\`
2853
+ Vergleicht die angegebene Zeit mit Grenzwerten.
2854
+
2855
+ Ist \`timeToCompare\` nicht angegeben, wird die aktuelle Zeit verwendet.
2856
+
2857
+ Folgende Operationen sind möglich:
2858
+
2859
+ - \`">"\` - wenn die angegebene Zeit größer als \`startTime\` ist
2860
+ - \`">="\` - wenn die angegebene Zeit größer oder gleich \`startTime\` ist
2861
+ - \`"<"\` - wenn die angegebene Zeit kleiner als \`startTime\` ist
2862
+ - \`"<="\` - wenn die angegebene Zeit kleiner oder gleich \`startTime\` ist
2863
+ - \`"=="\` - wenn die angegebene Zeit gleich \`startTime\` ist
2864
+ - \`"<>"\` - wenn die angegebene Zeit ungleich \`startTime\` ist
2865
+ - \`"between"\` - wenn die angegebene Zeit zwischen \`startTime\` und \`endTime\` liegt
2866
+ - \`"not between"\` - wenn die angegebene Zeit nicht zwischen \`startTime\` und \`endTime\` liegt
2867
+
2868
+ Die Zeit kann ein Date-Objekt, ein Datum mit Uhrzeit oder nur eine Uhrzeit sein.
2869
+
2870
+ Für die Zeitangabe kann man Astro-Namen verwenden. Alle 3 Parameter können als Astro-Zeit angegeben werden.
2871
+ Folgende Werte sind möglich: \`sunrise\`, \`sunset\`, \`sunriseEnd\`, \`sunsetStart\`, \`dawn\`, \`dusk\`, \`nauticalDawn\`, \`nauticalDusk\`, \`nightEnd\`, \`night\`, \`goldenHourEnd\`, \`goldenHour\`.
2872
+ Details siehe [Astro](#astro-funktion).
2873
+
2874
+ \`\`\`js
2875
+ log(compareTime('sunsetStart', 'sunsetEnd', 'between') ? 'Now is sunrise' : 'Now is no sunrise');
2876
+ \`\`\`
2877
+
2878
+ Die Zeit kann auch mit einem Offset angegeben werden:
2879
+
2880
+ \`\`\`js
2881
+ log(compareTime({ astro: 'sunsetStart', offset: 30 }, { astro: 'sunrise', offset: -30 }, '>') ? 'Now is at least 30 minutes after sunset' : 'No idea');
2882
+ \`\`\`
2883
+
2884
+ Struktur eines Astro-Objekts.
2885
+
2886
+ \`\`\`js
2887
+ {
2888
+ astro: 'sunsetStart',// Pflichtangabe, kann als String statt als Objekt angegeben werden, wenn offset und date Standardwerte haben
2889
+ offset: 30, // optional
2890
+ date: new Date() // optional
2891
+ }
2892
+ \`\`\`
2893
+
2894
+ ### setState
2895
+ \`\`\`js
2896
+ setState(id, state, ack, callback);
2897
+ \`\`\`
2898
+
2899
+ *Hinweis*: Die folgenden Befehle sind identisch
2900
+
2901
+ \`\`\`js
2902
+ setState('myState', 1, false);
2903
+ setState('myState', { val: 1, ack: false });
2904
+ setState('myState', 1);
2905
+ \`\`\`
2906
+
2907
+ Zur Verwendung von \`ack\` siehe https://github.com/ioBroker/ioBroker/wiki/Adapter-Development-Documentation#commands-and-statuses
2908
+ Kurz:
2909
+ - \`ack\` = \`false\` : Das Skript möchte ein Kommando senden, das vom Zielgerät/Adapter ausgeführt werden soll
2910
+ - \`ack\` = \`true\` : Das Kommando wurde erfolgreich ausgeführt, und der State wird als positives Ergebnis aktualisiert
2911
+
2912
+ ### setStateAsync
2913
+ \`\`\`js
2914
+ await setStateAsync(id, state, ack);
2915
+ \`\`\`
2916
+ Wie setState, aber mit \`promise\`.
2917
+
2918
+ ### setStateDelayed
2919
+ \`\`\`js
2920
+ setStateDelayed(id, state, isAck, delay, clearRunning, callback);
2921
+ \`\`\`
2922
+
2923
+ Wie setState, aber mit einer Verzögerung in Millisekunden. Dabei kann man (standardmäßig) alle laufenden Verzögerungen für diese ID löschen. Z. B.
2924
+
2925
+ \`\`\`js
2926
+ // Das Licht in der Küche in einer Sekunde EINschalten
2927
+ setStateDelayed('Kitchen.Light.Lamp', true, 1000);
2928
+
2929
+ // Das Licht in der Küche in 5 Sekunden AUSschalten und den ersten Timeout weiterlaufen lassen.
2930
+ setStateDelayed('Kitchen.Light.Lamp', false, 5000, false, () => {
2931
+ log('Lamp is OFF');
2932
+ });
2933
+ \`\`\`
2934
+ Die Funktion gibt den Handler des Timers zurück, und dieser Timer kann mit clearStateDelayed einzeln gestoppt werden.
2935
+
2936
+ ### setStateChanged
2937
+ \`\`\`js
2938
+ await setStateChanged(id, state, ack);
2939
+ \`\`\`
2940
+ Wie setState, setzt den Wert aber nur, wenn er sich tatsächlich geändert hat.
2941
+
2942
+ ### setStateChangedAsync
2943
+ \`\`\`js
2944
+ await setStateChangedAsync(id, state, ack);
2945
+ \`\`\`
2946
+ Wie setStateChanged, aber mit \`promise\`.
2947
+
2948
+ ### clearStateDelayed
2949
+ \`\`\`js
2950
+ clearStateDelayed(id);
2951
+ \`\`\`
2952
+
2953
+ Löscht alle verzögerten Aufgaben für die angegebene State-ID oder eine bestimmte verzögerte Aufgabe.
2954
+
2955
+ \`\`\`js
2956
+ setStateDelayed('Kitchen.Light.Lamp', false, 10000); // Das Licht in der Küche in zehn Sekunden AUSschalten
2957
+ let timer = setStateDelayed('Kitchen.Light.Lamp', true, 5000, false); // Das Licht in der Küche in fünf Sekunden EINschalten
2958
+ clearStateDelayed('Kitchen.Light.Lamp', timer); // Es wird nichts eingeschaltet
2959
+ clearStateDelayed('Kitchen.Light.Lamp'); // Alle laufenden verzögerten Aufgaben für diese ID löschen
2960
+ \`\`\`
2961
+
2962
+ ### getStateDelayed
2963
+ \`\`\`js
2964
+ getStateDelayed(id);
2965
+ \`\`\`
2966
+
2967
+ Dies ist ein synchroner Aufruf, der die Liste aller laufenden Timer (setStateDelayed) für diese ID liefert.
2968
+ Man kann die Funktion auch ohne ID aufrufen und erhält dann die Timer für alle IDs.
2969
+ Ruft man die Funktion für eine bestimmte Objekt-ID auf, erhält man folgende Antwort:
2970
+
2971
+ \`\`\`js
2972
+ getStateDelayed('hm-rpc.0.LQE91119.1.STATE');
2973
+
2974
+ // liefert ein Array wie
2975
+ [
2976
+ { timerId: 1, left: 1123, delay: 5000, val: true, ack: false },
2977
+ { timerId: 2, left: 12555, delay: 15000, val: false, ack: false },
2978
+ ]
2979
+ \`\`\`
2980
+
2981
+ Fragt man alle IDs ab, sieht die Antwort so aus:
2982
+
2983
+ \`\`\`js
2984
+ getStateDelayed();
2985
+
2986
+ // liefert ein Objekt wie
2987
+ {
2988
+ 'hm-rpc.0.LQE91119.1.STATE': [
2989
+ { timerId: 1, left: 1123, delay: 5000, val: true, ack: false },
2990
+ { timerId: 2, left: 12555, delay: 15000, val: false, ack: false },
2991
+ ],
2992
+ 'hm-rpc.0.LQE91119.2.LEVEL': [
2993
+ { timerId: 3, left: 5679, delay: 10000, val: 100, ack: false },
2994
+ ],
2995
+ }
2996
+ \`\`\`
2997
+
2998
+ - \`left\` ist die verbleibende Zeit in Millisekunden
2999
+ - \`delay\` ist der ursprüngliche Verzögerungswert in Millisekunden
3000
+
3001
+ Man kann auch direkt anhand der timerId abfragen. In diesem Fall lautet die Antwort:
3002
+
3003
+ \`\`\`js
3004
+ getStateDelayed(3);
3005
+
3006
+ // liefert ein Objekt wie
3007
+ { id: 'hm-rpc.0.LQE91119.2.LEVEL', left: 5679, delay: 10000, val: 100, ack: false }
3008
+ \`\`\`
3009
+
3010
+ ### getState
3011
+ \`\`\`js
3012
+ getState(id);
3013
+ \`\`\`
3014
+
3015
+ Liefert den State mit der angegebenen ID in folgender Form:
3016
+
3017
+ \`\`\`js
3018
+ {
3019
+ val: value,
3020
+ ack: true/false,
3021
+ ts: timestamp,
3022
+ lc: lastchanged,
3023
+ from: origin
3024
+ }
3025
+ \`\`\`
3026
+
3027
+ Existiert der State nicht, wird eine Warnung ins Log geschrieben und das Objekt \`{ val: null, notExist: true }\` zurückgegeben.
3028
+ Um die Warnung zu vermeiden, sollte man vor dem Aufruf von getState prüfen, ob der State existiert (siehe [existsState](#existsstate)).
3029
+
3030
+ ### getStateAsync
3031
+ \`\`\`js
3032
+ const stateObject = await getStateAsync(id);
3033
+ \`\`\`
3034
+ Wie getState, aber mit \`promise\`.
3035
+
3036
+ ### existsState
3037
+ \`\`\`js
3038
+ existsState(id, (err, isExists) => {});
3039
+ \`\`\`
3040
+
3041
+ Ist die Option "Nicht alle Zustände beim Start abonnieren" deaktiviert, kann man einen einfacheren Aufruf verwenden:
3042
+
3043
+ \`\`\`js
3044
+ existsState(id)
3045
+ \`\`\`
3046
+ In diesem Fall gibt die Funktion true oder false zurück.
3047
+
3048
+ Prüft, ob ein State existiert.
3049
+
3050
+ ### getObject
3051
+ \`\`\`js
3052
+ getObject(id, enumName);
3053
+ \`\`\`
3054
+ Liefert die Beschreibung der Objekt-ID, wie sie im System gespeichert ist.
3055
+ Man kann den Namen eines Enums angeben. Ist dieser angegeben, werden dem Ergebnis zwei zusätzliche Attribute hinzugefügt: enumIds und enumNames.
3056
+ Diese Arrays enthalten alle Enums, in denen die ID Mitglied ist. Z. B.:
3057
+
3058
+ \`\`\`js
3059
+ getObject('adapter.N.objectName', 'rooms');
3060
+ \`\`\`
3061
+
3062
+ liefert in enumIds alle Räume, in denen das angefragte Objekt Mitglied ist. Gibt man "true" als enumName an, erhält man *alle* Enums zurück.
3063
+
3064
+ ### setObject
3065
+ \`\`\`js
3066
+ setObject(id, obj, callback);
3067
+ \`\`\`
3068
+ Schreibt ein Objekt in die DB. Dieses Kommando kann in den Einstellungen des Adapters deaktiviert werden. Die Funktion sollte vorsichtig verwendet werden, da sonst die globalen Einstellungen beschädigt werden können.
3069
+
3070
+ Man sollte sie verwenden, um ein vorhandenes, zuvor gelesenes Objekt zu **ändern**, z. B.:
3071
+ \`\`\`js
3072
+ const obj = getObject('adapter.N.objectName');
3073
+ obj.native.settings = 1;
3074
+ setObject('adapter.N.objectName', obj, (err) => {
3075
+ if (err) log('Cannot write object: ' + err);
3076
+ });
3077
+ \`\`\`
3078
+
3079
+ ### existsObject
3080
+ \`\`\`js
3081
+ existsObject(id, function (err, isExists) {});
3082
+ \`\`\`
3083
+
3084
+ Ist die Option "Nicht alle Zustände beim Start abonnieren" deaktiviert, kann man einen einfacheren Aufruf verwenden:
3085
+
3086
+ \`\`\`js
3087
+ existsObject(id)
3088
+ \`\`\`
3089
+ In diesem Fall gibt die Funktion true oder false zurück.
3090
+
3091
+ Prüft, ob ein Objekt existiert.
3092
+
3093
+
3094
+ ### extendObject
3095
+ \`\`\`js
3096
+ extendObject(id, obj, callback);
3097
+ \`\`\`
3098
+
3099
+ Fast dasselbe wie \`setObject\`, allerdings wird das Objekt zuerst gelesen und dann versucht, alle Einstellungen zusammenzuführen.
3100
+
3101
+ Verwendung z. B. so:
3102
+ \`\`\`js
3103
+ // Instanz stoppen
3104
+ extendObject('system.adapter.sayit.0', {common: {enabled: false}});
3105
+ \`\`\`
3106
+
3107
+ ### deleteObject
3108
+ \`\`\`js
3109
+ deleteObject(id, isRecursive, callback);
3110
+ \`\`\`
3111
+
3112
+ Löscht ein Objekt anhand der ID aus der DB. Hat das Objekt den Typ \`state\`, wird auch der State-Wert gelöscht.
3113
+
3114
+ Zusätzlich kann der Parameter \`isRecursive\` angegeben werden, dann werden auch alle Kindobjekte der angegebenen ID gelöscht. Sehr gefährlich!
3115
+
3116
+ Verwendung z. B. so:
3117
+ \`\`\`js
3118
+ // State löschen
3119
+ deleteObject('javascript.0.createdState');
3120
+ \`\`\`
3121
+
3122
+ *Hinweis: Die Option \`isRecursive\` ist nur mit js-controller >= 2.2.x verfügbar*
3123
+
3124
+ ### getIdByName
3125
+ \`\`\`js
3126
+ getIdByName(name, alwaysArray);
3127
+ \`\`\`
3128
+
3129
+ Liefert die ID des Objekts mit dem angegebenen Namen.
3130
+ Gibt es mehr als ein Objekt mit diesem Namen, ist das Ergebnis ein Array.
3131
+ Ist das Flag \`alwaysArray\` gesetzt, ist das Ergebnis immer ein Array, sofern eine ID gefunden wurde.
3132
+ ### getEnums
3133
+ \`\`\`js
3134
+ getEnums(enumName);
3135
+ \`\`\`
3136
+
3137
+ Liefert die Liste der vorhandenen Enums mit ihren Mitgliedern, z. B.:
3138
+
3139
+ \`\`\`js
3140
+ getEnums('rooms');
3141
+
3142
+ // liefert alle Räume, z. B.:
3143
+ [
3144
+ {
3145
+ id: 'enum.rooms.LivingRoom',
3146
+ members: [ 'hm-rpc.0.JEQ0024123.1', 'hm-rpc.0.BidCoS-RF.4' ],
3147
+ name: 'Living room'
3148
+ },
3149
+ {
3150
+ id: 'enum.rooms.Bath',
3151
+ members: [ 'hm-rpc.0.JEQ0024124.1', 'hm-rpc.0.BidCoS-RF.5' ],
3152
+ name: 'Bath'
3153
+ }
3154
+ ]
3155
+
3156
+ getEnums('functions');
3157
+
3158
+ // liefert alle Funktionen, z. B.:
3159
+ [
3160
+ {
3161
+ id: 'enum.functions.light',
3162
+ members: [
3163
+ '0_userdata.0.AnotherOne',
3164
+ '0_userdata.0.MyLigh'
3165
+ ],
3166
+ name: {
3167
+ en: 'Light',
3168
+ ru: 'Свет',
3169
+ de: 'Licht',
3170
+ fr: 'Lumière',
3171
+ it: 'Leggero',
3172
+ nl: 'Licht',
3173
+ pl: 'Lekki',
3174
+ pt: 'Luz',
3175
+ es: 'Luz',
3176
+ 'zh-cn': '光'
3177
+ }
3178
+ }
3179
+ ]
3180
+ \`\`\`
3181
+
3182
+ ### createState
3183
+ \`\`\`js
3184
+ createState(name, initialValue, forceCreation, common, native, callback);
3185
+ \`\`\`
3186
+ Legt State und Objekt im javascript-Namensraum an, falls sie nicht existieren, z. B. \`javascript.0.mystate\`.
3187
+
3188
+ !! Eigene States sollten bevorzugt mit der vollständigen ID \`0_userdata.0.mystate\` angelegt werden !!!
3189
+
3190
+ #### Parameter:
3191
+
3192
+ - \`name\`: Name des States ohne Namensraum, z. B. \`mystate\`
3193
+ - \`initialValue\`: Die Variable kann nach dem Anlegen initialisiert werden. Der Wert "undefined" bedeutet, dass der Wert nicht initialisiert wird.
3194
+ - \`forceCreation\`: State anlegen/überschreiben, unabhängig davon, ob der State bereits existiert oder nicht.
3195
+ - \`common\`: common-Beschreibung des Objekts, siehe Beschreibung [hier](https://github.com/ioBroker/ioBroker/blob/master/doc/SCHEMA.md#state)
3196
+ - \`native\`: native-Beschreibung eines Objekts. Beliebige spezifische Informationen.
3197
+ - \`callback\`: wird aufgerufen, nachdem der State angelegt und initialisiert wurde.
3198
+
3199
+ Setzt man in \`common\` das Flag \`alias\` auf \`true\`, wird ein Alias mit demselben Namen wie der State (aber im Namensraum \`alias.0\`) angelegt.
3200
+ Der Alias wird nur angelegt, wenn er noch nicht existiert.
3201
+
3202
+ Folgende Einstellungen für Aliase sind ebenfalls gültig:
3203
+ \`\`\`js
3204
+ common => {
3205
+ alias: {
3206
+ id: 'alias.0.myOtherState', // wird automatisch angelegt, falls noch nicht vorhanden
3207
+ write: 'val * 1000', // Umrechnungsfunktion für das Schreiben in den angelegten State
3208
+ read: 'val / 1000' // Umrechnungsfunktion für das Lesen aus dem angelegten State
3209
+ }
3210
+ }
3211
+ \`\`\`
3212
+
3213
+ oder
3214
+
3215
+ \`\`\`js
3216
+ common => {
3217
+ alias: {
3218
+ id: 'alias.0.myOtherState', // wird automatisch angelegt, falls noch nicht vorhanden
3219
+ }
3220
+ }
3221
+ \`\`\`
3222
+
3223
+ Es gibt auch Kurzformen von createState:
3224
+
3225
+ - \`createState('myDatapoint')\` - legt den State einfach an, falls er nicht existiert
3226
+ - \`createState('myDatapoint', 1)\` - legt den State an, falls er nicht existiert, und initialisiert ihn mit dem Wert 1
3227
+ - \`createState('myDatapoint', { type: 'string', role: 'json', read: true, write: false }, () => { log('created'); });\` - mit common-Definitionen wie type, read, write und role
3228
+ - \`createState('myDatapoint', { name: 'My own datapoint', unit: '°C' }, () => { log('created'); });\`
3229
+ - \`createState('myDatapoint', 1, { name: 'My own datapoint', unit: '°C' })\` - legt den State mit bestimmtem Namen und bestimmter Einheit an, falls er nicht existiert
3230
+
3231
+ #### Ein Objekt an zweiter Position ist immer das \`common\`
3232
+
3233
+ Diese Kurzformen sind der Grund, warum ein Objekt an zweiter Position **nie** als Initialwert
3234
+ gelesen wird. \`createState('myDatapoint', {}, { type: 'object' })\` macht daher nicht das, wonach es aussieht:
3235
+ Das \`{}\` wird zu \`common\`, und \`{ type: 'object' }\` rückt an die Stelle von \`native\`.
3236
+
3237
+ Um einem State einen Initialwert zu geben, der kein primitiver Wert ist, gibt man ihn in \`common.def\` an:
3238
+
3239
+ \`\`\`js
3240
+ createState('0_userdata.0.myObject', { name: 'My object', type: 'object', read: true, write: true, def: {} });
3241
+ \`\`\`
3242
+
3243
+ Ein State vom Typ \`object\`, \`json\` oder \`array\` speichert seinen Wert als JSON, daher beginnt der obige State
3244
+ mit dem String \`'{}'\` - genau so, wie \`setState('0_userdata.0.myObject', {})\` ihn speichern würde. Der Standardwert
3245
+ wird automatisch in einen String umgewandelt; \`def: '{}'\` selbst anzugeben funktioniert ebenso.
3246
+
3247
+ ### createStateAsync
3248
+ \`\`\`js
3249
+ await createStateAsync(name, initialValue, forceCreation, common, native);
3250
+ \`\`\`
3251
+
3252
+ Wie \`createState\`, aber es wird ein Promise zurückgegeben.
3253
+
3254
+ ### deleteState
3255
+ \`\`\`js
3256
+ deleteState(name, callback);
3257
+ \`\`\`
3258
+ Löscht State und Objekt im javascript-Namensraum, z. B. \`javascript.0.mystate\`. States anderer Adapter können nicht gelöscht werden.
3259
+
3260
+ \`\`\`js
3261
+ deleteState('myDatapoint')
3262
+ \`\`\`
3263
+ löscht den State einfach, falls er existiert.
3264
+
3265
+ ### deleteStateAsync
3266
+ \`\`\`js
3267
+ await deleteStateAsync(name);
3268
+ \`\`\`
3269
+
3270
+ Wie \`deleteState\`, aber es wird ein Promise zurückgegeben.
3271
+
3272
+ ### createAlias
3273
+ \`\`\`js
3274
+ createAlias(name, alias, forceCreation, common, native, callback);
3275
+ \`\`\`
3276
+
3277
+ Legt einen Alias im Namensraum \`alias.0\` an, falls er nicht existiert, z. B. \`javascript.0.myalias\`, der auf einen State oder auf States zum Lesen/Schreiben verweist.
3278
+ Die common-Definition wird aus dem Objekt der Lese-Alias-ID übernommen, ein übergebenes common hat jedoch Vorrang.
3279
+
3280
+ #### Parameter:
3281
+
3282
+ - \`name\`: ID des neuen Alias-States (auch ohne Alias-Namensraum möglich), z. B. \`test.mystate\` (der Namensraum \`alias.0.\` wird ergänzt = \`alias.0.test.mystate\`)
3283
+ - \`alias\`: kann entweder eine vorhandene State-ID als String sein oder ein Objekt mit vollständiger Alias-Definition inklusive Lese-/Schreib-IDs und Lese-/Schreibfunktionen. Hinweis: Alias-Definitionen können nicht als Teil des Parameters common gesetzt werden!
3284
+ - \`forceCreation\`: Alias anlegen/überschreiben, unabhängig davon, ob der State bereits existiert oder nicht.
3285
+ - \`common\`: common-Beschreibung des Alias-Objekts, siehe Beschreibung [hier](https://github.com/ioBroker/ioBroker/blob/master/doc/SCHEMA.md#state). Hier angegebene Werte haben Vorrang vor der common-Definition des Objekts der Lese-Alias-ID. Hinweis: Alias-Definitionen können nicht als Teil dieses common-Parameters gesetzt werden, siehe Parameter alias!
3286
+ - \`native\`: native-Beschreibung eines Objekts. Beliebige spezifische Informationen.
3287
+ - \`callback\`: wird aufgerufen, nachdem der State angelegt und initialisiert wurde.
3288
+
3289
+ Es gibt auch Kurzformen von createAlias:
3290
+
3291
+ - \`createAlias('myAlias', 'myDatapoint')\` - legt einfach alias.0.myAlias an, das auf javascript.X.myDatapoint verweist, falls es nicht existiert
3292
+ - \`createAlias('myAlias', { id: { read: 'myReadDatapoint', write: 'myWriteDatapoint' }})\` - legt einen Alias an, der auf unterschiedliche States zum Lesen/Schreiben verweist
3293
+
3294
+ Weitere Details siehe createState, die Funktion arbeitet ähnlich.
3295
+
3296
+ ### createAliasAsync
3297
+ \`\`\`js
3298
+ await createAliasAsync(name, alias, forceCreation, common, native);
3299
+ \`\`\`
3300
+
3301
+ Wie \`createAlias\`, aber es wird ein Promise zurückgegeben.
3302
+ ### sendTo
3303
+ \`\`\`js
3304
+ sendTo(adapter, command, message, callback);
3305
+ sendTo(adapter, command, message, options, callback);
3306
+ \`\`\`
3307
+
3308
+ Sendet eine Nachricht an eine bestimmte oder an alle Instanzen eines Adapters. Wird nur der Adaptername angegeben, wird die Nachricht an alle Instanzen gesendet.
3309
+
3310
+ Welche Nachrichten konkret unterstützt werden, ist in der Dokumentation des jeweiligen Adapters beschrieben.
3311
+
3312
+ Beispiel (mit benutzerdefiniertem Timeout):
3313
+
3314
+ \`\`\`js
3315
+ sendTo('telegram', { user: 'UserName', text: 'Test message' }, { timeout: 2000 });
3316
+ \`\`\`
3317
+
3318
+ Einige Adapter unterstützen auch Antworten auf die gesendeten Nachrichten (z. B. history, sql, telegram).
3319
+ Die Antwort wird nur dann an den Callback zurückgegeben, wenn die Nachricht an eine bestimmte Instanz gesendet wird!
3320
+
3321
+ Beispiel (mit Callback):
3322
+
3323
+ \`\`\`js
3324
+ sendTo('telegram.0', { user: 'UserName', text: 'Test message' }, (res) => {
3325
+ log(\`Sent to \${res} users\`);
3326
+ });
3327
+ \`\`\`
3328
+
3329
+ *Der Standard-Timeout beträgt 20000 Millisekunden (sofern eine Callback-Funktion angegeben wurde)*
3330
+
3331
+ \`\`\`js
3332
+ sendTo('telegram.0', { user: 'UserName', text: 'Test message' }, { timeout: 2000 }, (res) => {
3333
+ log(\`Sent to \${res} users\`);
3334
+ });
3335
+ \`\`\`
3336
+
3337
+ ### sendToAsync
3338
+ \`\`\`js
3339
+ await sendToAsync(adapter, command, message);
3340
+ await sendToAsync(adapter, command, message, options);
3341
+ \`\`\`
3342
+ Wie sendTo, jedoch mit \`promise\`.
3343
+
3344
+ Beispiel:
3345
+
3346
+ \`\`\`js
3347
+ const res = await sendToAsync('sql.0', 'getEnabledDPs', {});
3348
+ log(JSON.stringify(res));
3349
+ \`\`\`
3350
+
3351
+ ### sendToHost
3352
+ \`\`\`js
3353
+ sendToHost(hostName, command, message, callback);
3354
+ \`\`\`
3355
+
3356
+ Sendet eine Nachricht an die Controller-Instanz.
3357
+
3358
+ Die folgenden Kommandos werden unterstützt:
3359
+ - \`'cmdExec'\`
3360
+ - \`'getRepository'\`
3361
+ - \`'getInstalled'\`
3362
+ - \`'getVersion'\`
3363
+ - \`'getDiagData'\`
3364
+ - \`'getLocationOnDisk'\`
3365
+ - \`'getDevList'\`
3366
+ - \`'getLogs'\`
3367
+ - \`'getLogFile'\`
3368
+ - \`'getLogFiles'\`
3369
+ - \`'delLogs'\`
3370
+ - \`'getHostInfo'\`
3371
+ - \`'getHostInfoShort'\`
3372
+ - \`'updateMultihost'\`
3373
+ - \`'upgradeController'\` - Aktualisiert den js-controller auf die neueste Version
3374
+ - \`'getInterfaces'\` - Liefert alle verfügbaren Netzwerkschnittstellen des Systems
3375
+ - \`'upload'\` - Startet einen Adapter-Upload
3376
+ - \`'rebuildAdapter'\`
3377
+ - \`'readBaseSettings'\`
3378
+ - \`'writeBaseSettings'\`
3379
+ - \`'addNotification'\`
3380
+ - \`'clearNotifications'\`
3381
+ - \`'getNotifications'\`
3382
+ - \`'updateLicenses'\` - Liest die Lizenzen von iobroker.net
3383
+ - \`'upgradeOsPackages'\`
3384
+ - \`'restartController'\`
3385
+
3386
+ Dabei handelt es sich um recht spezielle Kommandos, die nur selten benötigt werden.
3387
+
3388
+ Beispiel:
3389
+
3390
+ \`\`\`js
3391
+ sendToHost('myComputer', 'cmdExec', { data: 'ls /' }, (res) => {
3392
+ log('List of files: ' + res.data);
3393
+ });
3394
+ \`\`\`
3395
+
3396
+ **Hinweis:** Um diese Funktion aufzurufen, muss die Option *Kommando "sendToHost" erlauben* aktiviert sein.
3397
+
3398
+ ### sendToHostAsync
3399
+ \`\`\`js
3400
+ await sendToHostAsync(hostName, command, message);
3401
+ \`\`\`
3402
+ Wie sendToHost, jedoch mit \`promise\`.
3403
+
3404
+ ### setInterval
3405
+ \`\`\`js
3406
+ setInterval(callback, ms, arg1, arg2, arg3, arg4);
3407
+ \`\`\`
3408
+
3409
+ Wie \`setInterval\` in JavaScript.
3410
+
3411
+ ### clearInterval
3412
+ \`\`\`js
3413
+ clearInterval(id);
3414
+ \`\`\`
3415
+
3416
+ Wie \`clearInterval\` in JavaScript.
3417
+
3418
+ ### setTimeout
3419
+ \`\`\`js
3420
+ setTimeout(callback, ms, arg1, arg2, arg3, arg4);
3421
+ \`\`\`
3422
+ Wie \`setTimeout\` in JavaScript.
3423
+
3424
+ ### clearTimeout
3425
+ \`\`\`js
3426
+ clearTimeout(id);
3427
+ \`\`\`
3428
+
3429
+ Wie \`clearTimeout\` in JavaScript.
3430
+
3431
+ ### setImmediate
3432
+ \`\`\`js
3433
+ setImmediate(callback, arg1, arg2, arg3, arg4);
3434
+ \`\`\`
3435
+
3436
+ Wie \`setImmediate\` in JavaScript und nahezu identisch mit \`setTimeout(callback, 0, arg1, arg2, arg3, arg4)\`, jedoch mit höherer Priorität.
3437
+
3438
+ ### formatDate
3439
+ \`\`\`js
3440
+ formatDate(millisecondsOrDate, format);
3441
+ \`\`\`
3442
+
3443
+ #### Parameter:
3444
+
3445
+ - \`millisecondsOrDate\`: Anzahl der Millisekunden aus state.ts oder state.lc (Millisekunden seit 1970.01.01 00:00:00), ein JavaScript-Objekt *new Date()* oder die Anzahl der Millisekunden aus *(new Date().getTime())*
3446
+ - \`format\`: Kann \`null\` sein, dann wird das Zeitformat des Systems verwendet, andernfalls
3447
+
3448
+ * YYYY, JJJJ, ГГГГ - Jahr vierstellig, z. B. 2015
3449
+ * YY, JJ, ГГ - Jahr zweistellig, z. B. 15
3450
+ * MM, ММ(kyrillisch) - Monat zweistellig, z. B. 01
3451
+ * M, М(kyrillisch) - Monat ohne führende Null, z. B. 1
3452
+ * DD, TT, ДД - Tag zweistellig, z. B. 02
3453
+ * D, T, Д - Tag ohne führende Null, z. B. 2
3454
+ * hh, SS, чч - Stunden zweistellig, z. B. 03
3455
+ * h, S, ч - Stunden ohne führende Null, z. B. 3
3456
+ * mm, мм(kyrillisch) - Minuten zweistellig, z. B. 04
3457
+ * m, м(kyrillisch) - Minuten ohne führende Null, z. B. 4
3458
+ * ss, сс(kyrillisch) - Sekunden zweistellig, z. B. 05
3459
+ * s, с(kyrillisch) - Sekunden ohne führende Null, z. B. 5
3460
+ * sss, ссс(kyrillisch) - Millisekunden
3461
+ * WW, НН(kyrillisch) - Wochentag ausgeschrieben als Text
3462
+ * W, Н(kyrillisch) - Wochentag abgekürzt als Text
3463
+ * OO, ОО(kyrillisch) - Monatsname ausgeschrieben
3464
+ * OOO, ООО(kyrillisch) - Monatsname ausgeschrieben im Genitiv
3465
+ * O, О(kyrillisch) - Monatsname abgekürzt
3466
+
3467
+ #### Beispiel
3468
+
3469
+ \`\`\`js
3470
+ formatDate(new Date(), "YYYY-MM-DD") // => Datum "2015-02-24"
3471
+ formatDate(new Date(), "hh:mm") // => Stunden und Minuten "17:41"
3472
+ formatDate(state.ts) // => "24.02.2015"
3473
+ formatDate(state.ts, "JJJJ.MM.TT SS:mm:ss.sss") // => "2015.02.15 17:41:98.123"
3474
+ formatDate(new Date(), "WW") // => Wochentag "Tuesday"
3475
+ formatDate(new Date(), "W") // => Wochentag "Tu"
3476
+ \`\`\`
3477
+
3478
+ ### formatTimeDiff
3479
+ \`\`\`js
3480
+ formatTimeDiff(milliseconds, format);
3481
+ \`\`\`
3482
+
3483
+ #### Parameter:
3484
+
3485
+ - \`milliseconds\`: Differenz in Millisekunden*
3486
+ - \`format\`: Kann \`null\` sein, dann wird das Format \`hh:mm:ss\` verwendet, andernfalls
3487
+
3488
+ * DD, TT, ДД - Tage zweistellig, z. B. "02"
3489
+ * D, T, Д - Tage ohne führende Null, z. B. "2"
3490
+ * hh, SS, чч - Stunden zweistellig, z. B. "03"
3491
+ * h, S, ч - Stunden ohne führende Null, z. B. "3"
3492
+ * mm, мм(kyrillisch) - Minuten zweistellig, z. B. "04"
3493
+ * m, м(kyrillisch) - Minuten ohne führende Null, z. B. "4"
3494
+ * ss, сс(kyrillisch) - Sekunden zweistellig, z. B. "05"
3495
+ * s, с(kyrillisch) - Sekunden ohne führende Null, z. B. "5"
3496
+
3497
+ Mit dem Escape-Zeichen \`\\\` lässt sich die Ersetzung verhindern, z. B. \`DD \\Day\\s, h \\hour\\s, m \\minute, ss \\second\\s\`
3498
+
3499
+ #### Beispiel
3500
+
3501
+ \`\`\`js
3502
+ formatTimeDiff(60000, "mm:ss") // => "01:00"
3503
+
3504
+ const diff = 172800000 + 10800000 + 540000 + 15000; // 2 Tage, 3 Stunden, 9 Minuten + 15 Sekunden
3505
+ formatTimeDiff(diff); // "51:09:15"
3506
+ formatTimeDiff(diff, 'DD hh:mm'); // "02 03:09"
3507
+ formatTimeDiff(diff, 'D hh:mm'); // "2 03:09"
3508
+ formatTimeDiff(diff, 'hh:mm:ss'); // "51:09:15"
3509
+ formatTimeDiff(diff, 'h:m:s'); // "51:9:15"
3510
+ formatTimeDiff(diff, 'hh:mm'); // "51:09"
3511
+ formatTimeDiff(diff, 'mm:ss'); // "3069:15"
3512
+ formatTimeDiff(diff, 'hh'); // "51"
3513
+ formatTimeDiff(diff, 'mm'); // "3069"
3514
+ \`\`\`
3515
+
3516
+ ### getDateObject
3517
+ \`\`\`js
3518
+ getDateObject(stringOrNumber);
3519
+ \`\`\`
3520
+
3521
+ Wandelt einen String oder eine Zahl in ein Date-Objekt um.
3522
+ Wird nur die Uhrzeit angegeben, wird das aktuelle Datum ergänzt und anschließend die Umwandlung versucht.
3523
+
3524
+ \`\`\`js
3525
+ getDateObject('20:00'); // 2024-05-18T18:00:00.000Z
3526
+ getDateObject('2024-01-01'); // 2024-01-01T00:00:00.000Z
3527
+ \`\`\`
3528
+
3529
+ ### formatValue
3530
+ \`\`\`js
3531
+ formatValue(value, decimals, format);
3532
+ \`\`\`
3533
+
3534
+ Formatiert einen beliebigen Wert (auch Strings) als Zahl. Ersetzt den Punkt durch ein Komma, wenn dies im System so konfiguriert ist.
3535
+ Decimals gibt die Anzahl der Nachkommastellen an. Der Standardwert ist 2.
3536
+ Format ist optional:
3537
+ - '.,': 1234.567 => 1.234,56
3538
+ - ',.': 1234.567 => 1,234.56
3539
+ - ' .': 1234.567 => 1 234.56
3540
+
3541
+
3542
+ ### adapterSubscribe
3543
+ \`\`\`js
3544
+ adapterSubscribe(id);
3545
+ \`\`\`
3546
+
3547
+ Sendet an einen Adapter die Nachricht "subscribe", um den Adapter zu informieren. Hat der Adapter in common das Flag "subscribable", wird diese Funktion bei "subscribe" automatisch aufgerufen.
3548
+
3549
+ ### adapterUnsubscribe
3550
+ \`\`\`js
3551
+ adapterUnsubscribe(id);
3552
+ \`\`\`
3553
+
3554
+ Sendet an einen Adapter die Nachricht \`unsubscribe\`, um den Adapter zu informieren, dass er die Werte nicht mehr abfragen soll.
3555
+
3556
+ ### $ - Selektor
3557
+ \`\`\`js
3558
+ $(selector).on((obj) => {}); // Registriert ein Abonnement für jeden passenden State
3559
+ $(selector).toArray(); // Liefert alle zum Selektor-Ausdruck passenden Objekt-IDs (erfordert Version >= 8.2.0)
3560
+ $(selector).each((id, i) => {}); // Iteriert über alle passenden States
3561
+ $(selector).setState(value, ack, callback); // Setzt den State-Wert aller passenden Objekt-IDs (Callback ist optional)
3562
+ $(selector).setStateAsync(value, ack); // Setzt den State-Wert aller passenden Objekt-IDs - gibt ein Promise zurück
3563
+ $(selector).setStateChanged(value, ack, callback); // Setzt den State-Wert aller passenden Objekt-IDs, falls sich der Wert geändert hat (Callback ist optional)
3564
+ $(selector).setStateChangedAsync(value, ack, callback); // Setzt den State-Wert aller passenden Objekt-IDs, falls sich der Wert geändert hat - gibt ein Promise zurück
3565
+ $(selector).setStateDelayed(state, isAck, delay, clearRunning, callback); // Setzt den State-Wert aller passenden Objekt-IDs mit der angegebenen Verzögerung
3566
+ $(selector).getState(); // Liefert alle States
3567
+ $(selector).getStateAsync(); // Liefert alle States - gibt ein Promise zurück
3568
+ \`\`\`
3569
+
3570
+ Format des Selektors:
3571
+ \`\`\`js
3572
+ "name[commonAttr=something1](enumName=something2){nativeName=something3}[id=idfilter][state.id=idfilter]"
3573
+ \`\`\`
3574
+
3575
+ name kann sein: state, channel, device oder schedule
3576
+ \`idfilter\` kann Platzhalter '*' enthalten
3577
+
3578
+ Präfixe ***(nicht implementiert - sollte diskutiert werden)*** :
3579
+
3580
+ * \\# - nach Name statt nach ID auswählen
3581
+ * . - nach Rolle filtern
3582
+ * § - nach Raum filtern
3583
+
3584
+ ***Beispiel***:
3585
+
3586
+ - \`$('state[id=*.STATE]')\` oder \`$('state[state.id=*.STATE]')\` oder \`$('*.STATE')\` - wählt alle States aus, deren ID auf ".STATE" endet.
3587
+ - \`$('state[id='hm-rpc.0.*]')\` oder \`$('hm-rpc.0.*')\` - liefert alle States der Adapterinstanz hm-rpc.0
3588
+ - \`$('channel(rooms=Living room)')\` - alle States im Raum "Living room"
3589
+ - \`$('channel{TYPE=BLIND}[state.id=*.LEVEL]')\` - liefert alle Rollläden von Homematic
3590
+ - \`$('channel[role=switch](rooms=Living room)[state.id=*.STATE]').setState(false)\` - schaltet alle States mit .STATE von Kanälen mit der Rolle "switch" im Raum "Living room" auf false
3591
+ - \`$('channel[state.id=*.STATE](functions=Windows)').each(function (id, i) {log(id);});\` - gibt alle States des Enums "windows" im Log aus
3592
+ - \`$('schedule[id=*65]').each(function (id, i) {log(id);});\` - gibt alle Zeitpläne aus, deren ID auf 65 endet
3593
+ - \`$('.switch §"Living room")\` - nimmt die States aller Schalter im Raum 'Living room' ***(nicht implementiert - sollte diskutiert werden)***
3594
+ - \`$('channel .switch §"Living room")\` - nimmt die States aller Schalter im Raum 'Living room' ***(nicht implementiert - sollte diskutiert werden)***
3595
+
3596
+ ***Erklärung***
3597
+ Als Beispiel dient folgender Code:
3598
+ \`\`\`js
3599
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').on(obj => {
3600
+ log('New state ' + obj.id + ' = ' + obj.state.val);
3601
+ });
3602
+ \`\`\`
3603
+
3604
+ Dieser Code sucht in Kanälen.
3605
+ Er findet alle Kanäle mit \`common.role="switch"\`, die zu \`enum.rooms.Wohnzimmer\` gehören.
3606
+ Von diesen Kanälen werden alle States genommen, deren ID auf \`".STATE"\` endet, und alle diese States werden abonniert.
3607
+ Ändert sich einer dieser States, wird der Callback wie bei der Funktion "on" aufgerufen.
3608
+
3609
+ Folgende Funktionen sind möglich: setState, getState (nur vom ersten), on, each, toArray
3610
+
3611
+ \`\`\`js
3612
+ // Alle Schalter im "Wohnzimmer" einschalten
3613
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').setState(true);
3614
+ \`\`\`
3615
+
3616
+ Die "each"-Schleife lässt sich abbrechen, indem man false zurückgibt, z. B.:
3617
+ \`\`\`js
3618
+ // Die ersten zwei IDs aller Schalter im "Wohnzimmer" ausgeben
3619
+ $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').each((id, i) => {
3620
+ log(id);
3621
+ if (i == 1) {
3622
+ return false;
3623
+ }
3624
+ });
3625
+ \`\`\`
3626
+ Alternativ kann man ein gewöhnliches Array der IDs abrufen und es beliebig weiterverarbeiten:
3627
+ \`\`\`js
3628
+ // States holen und nur diejenigen mit dem Wert \`true\` herausfiltern
3629
+ const enabled = $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').toArray().filter((id) => getState(id)?.val === true);
3630
+ \`\`\`
3631
+
3632
+ ### readFile
3633
+ \`\`\`js
3634
+ readFile(adapter, fileName, (error, bytes) => {});
3635
+ \`\`\`
3636
+
3637
+ Das Ergebnis wird im Callback übergeben.
3638
+ Liest eine Datei aus der DB aus dem Ordner \`javascript.0\`.
3639
+
3640
+ Das Argument *adapter* kann weggelassen werden.
3641
+
3642
+ \`\`\`js
3643
+ // vis-Views lesen
3644
+ readFile('vis.0', '/main/vis-views.json', (error, data) => {
3645
+ log(data.substring(0, 50));
3646
+ });
3647
+
3648
+ // Dasselbe wie
3649
+ //readFile('/../vis.0/main/vis-views.json', (error, data) => {
3650
+ // log(data.substring(0, 50));
3651
+ //});
3652
+ \`\`\`
3653
+
3654
+ Standardmäßig ist das Arbeitsverzeichnis/der Adapter \`javascript.0\`.
3655
+
3656
+ ### writeFile
3657
+ \`\`\`js
3658
+ writeFile(adapter, fileName, bytes, (error) => {});
3659
+ \`\`\`
3660
+
3661
+ Der optionale Fehlercode wird im Callback übergeben. Das Argument *adapter* kann weggelassen werden.
3662
+ fileName ist der Name der Datei in der DB. Alle Dateien werden im Ordner "javascript" gespeichert.
3663
+ Um in andere Ordner zu schreiben, z. B. nach "/vis.0/", verwendet man setFile.
3664
+
3665
+ Eine Datei wie \`'/subfolder/file.txt'\` wird unter \`"/javascript/subfolder/file.txt"\` gespeichert und ist über den Webserver unter \`"http://ip:8082/javascript/subfolder/file.txt"\` erreichbar.
3666
+
3667
+ \`\`\`js
3668
+ // Screenshot in der DB speichern
3669
+ const fs = require('node:fs');
3670
+ let data = fs.readFileSync('/tmp/screenshot.png');
3671
+ writeFile(null, '/screenshots/1.png', data, (error) => {
3672
+ log('file written');
3673
+ });
3674
+
3675
+ // Dasselbe wie
3676
+ //writeFile('/screenshots/1.png', data, function (error) {
3677
+ // log('file written');
3678
+ //});
3679
+ \`\`\`
3680
+
3681
+ \`\`\`js
3682
+ // Datei in '/vis.0' in der DB speichern
3683
+ const fs = require('node:fs');
3684
+ let data = fs.readFileSync('/tmp/screenshot.png');
3685
+ writeFile('vis.0', '/screenshots/1.png', data, (error) => {
3686
+ log('file written');
3687
+ });
3688
+ \`\`\`
3689
+
3690
+ ### delFile
3691
+ \`\`\`js
3692
+ delFile(adapter, fileName, (error) => {});
3693
+ \`\`\`
3694
+
3695
+ Löscht eine Datei oder ein Verzeichnis. fileName ist der Name der Datei oder des Verzeichnisses in der DB.
3696
+
3697
+ Der alternative Name dieser Methode ist \`unlink\`
3698
+
3699
+ ### renameFile
3700
+ \`\`\`js
3701
+ renameFile(adapter, oldName, newName, (error) => {});
3702
+ \`\`\`
3703
+
3704
+ Benennt eine Datei oder ein Verzeichnis um. oldName ist der Name der Datei oder des Verzeichnisses in der DB, der in newName umbenannt wird.
3705
+
3706
+ Der alternative Name dieser Methode ist \`rename\`
3707
+
3708
+ ### onFile
3709
+ \`\`\`js
3710
+ onFile(id, fileName, withFile, (id, fileName, size, fileData, mimeType) => {});
3711
+ // oder
3712
+ onFile(id, fileName, (id, fileName, size) => {});
3713
+ \`\`\`
3714
+
3715
+ Abonniert Dateiänderungen:
3716
+ - \`id\` ist die ID eines Objekts vom Typ \`meta\`, z. B. \`vis.0\`
3717
+ - \`fileName\` ist ein Dateiname oder ein Muster, z. B. \`main/*\` oder \`main/vis-view.json\`
3718
+ - \`withFile\` gibt an, ob der Dateiinhalt im Callback übergeben werden soll oder nicht. Die Übergabe des Dateiinhalts kostet Speicher und Zeit; möchte man nur über Änderungen informiert werden, setzt man \`withFile\` auf false.
3719
+
3720
+ Argumente im Callback:
3721
+ - \`id\` - ID des \`meta\`-Objekts;
3722
+ - \`fileName\` - Dateiname (kein Muster);
3723
+ - \`size\` - neue Dateigröße;
3724
+ - \`fileData\` - Dateiinhalt vom Typ \`Buffer\`, wenn die Datei binär ist (anhand der Dateiendung erkannt), sonst \`string\`. Wird nur bei \`withFile\` übergeben;
3725
+ - \`mimeType\` - MIME-Typ der Datei, z. B. \`image/jpeg\`. Wird nur bei \`withFile\` übergeben;
3726
+
3727
+ **Wichtig**: Diese Funktionalität ist nur mit js-controller@4.1.x oder neuer verfügbar.
3728
+
3729
+ ### offFile
3730
+ \`\`\`js
3731
+ offFile(id, fileName);
3732
+ // oder
3733
+ onFile(id, fileName);
3734
+ \`\`\`
3735
+ Beendet das Abonnement von Dateiänderungen:
3736
+ - \`id\` ist die ID eines Objekts vom Typ \`meta\`, z. B. \`vis.0\`
3737
+ - \`fileName\` ist ein Dateiname oder ein Muster, z. B. \`main/*\` oder \`main/vis-view.json\`
3738
+
3739
+ **Wichtig**: Diese Funktionalität ist nur mit js-controller@4.1.x oder neuer verfügbar.
3740
+
3741
+ ### onStop
3742
+ \`\`\`js
3743
+ onStop (() => { /* etwas tun, wenn das Skript gestoppt wird */ }, timeout);
3744
+ \`\`\`
3745
+ Registriert einen Callback, der aufgerufen wird, wenn das Skript gestoppt wird. Wird z. B. verwendet, um die Kommunikation zu beenden oder Verbindungen zu schließen.
3746
+
3747
+ \`\`\`js
3748
+ // Verbindung aufbauen
3749
+ const conn = require('net');
3750
+ // ...
3751
+
3752
+ // Verbindung schließen, wenn das Skript gestoppt wird
3753
+ onStop((callback) => {
3754
+ if (conn) {
3755
+ // Verbindung schließen
3756
+ conn.destroy();
3757
+ }
3758
+ callback();
3759
+ }, 2000 /*ms*/);
3760
+ \`\`\`
3761
+ \`timeout\` beträgt standardmäßig 1000 ms.
3762
+
3763
+ ### getHistory
3764
+ \`\`\`js
3765
+ getHistory(instance, options, (error, result, options, instance) => {});
3766
+ \`\`\`
3767
+
3768
+ Liest die Historie aus der angegebenen Instanz. Ist keine Instanz angegeben, wird die im System eingestellte Standard-Historieninstanz verwendet.
3769
+ \`\`\`js
3770
+ // Historie von 'system.adapter.admin.0.memRss' aus dem SQL-Treiber lesen
3771
+ const end = new Date().getTime();
3772
+ getHistory(
3773
+ 'sql.0',
3774
+ {
3775
+ id: 'system.adapter.admin.0.memRss',
3776
+ start: end - 3600000,
3777
+ end: end,
3778
+ aggregate: 'm4',
3779
+ timeout: 2000
3780
+ },
3781
+ (err, result) => {
3782
+ if (err) console.error(err);
3783
+ if (result) {
3784
+ for (let i = 0; i < result.length; i++) {
3785
+ log(result[i].id + ' ' + new Date(result[i].ts).toISOString());
3786
+ }
3787
+ }
3788
+ }
3789
+ );
3790
+ \`\`\`
3791
+
3792
+ Die möglichen Optionen sind [hier](https://github.com/ioBroker/ioBroker.history#access-values-from-javascript-adapter) beschrieben.
3793
+
3794
+ Zusätzlich zu diesen Parametern muss "id" angegeben werden, optional kann auch timeout angegeben werden (Standard: 20000 ms).
3795
+
3796
+ Ein weiteres Beispiel:
3797
+ \`\`\`js
3798
+ // Die letzten 50 Einträge aus der Standard-Historieninstanz ohne Aggregation abrufen:
3799
+ getHistory({
3800
+ id: 'system.adapter.admin.0.alive',
3801
+ aggregate: 'none',
3802
+ count: 50
3803
+ }, (err, result) => {
3804
+ if (err) console.error(err);
3805
+ if (result) {
3806
+ for (let i = 0; i < result.length; i++) {
3807
+ log(result[i].id + ' ' + new Date(result[i].ts).toISOString());
3808
+ }
3809
+ }
3810
+ });
3811
+ \`\`\`
3812
+
3813
+ **Hinweis: ** Selbstverständlich muss die Historie für die gewählte ID zuerst im Admin aktiviert werden.
3814
+ ### runScript
3815
+ \`\`\`js
3816
+ runScript('scriptName', () => {
3817
+ // Callback ist optional
3818
+ log('Srcipt started, but not yet executed');
3819
+ });
3820
+ \`\`\`
3821
+
3822
+ Startet andere Skripte (und auch sich selbst) anhand des Namens bzw. startet sie neu.
3823
+
3824
+ \`\`\`js
3825
+ // Skript neu starten
3826
+ runScript('groupName.scriptName1');
3827
+ \`\`\`
3828
+
3829
+ ### runScriptAsync
3830
+ Wie runScript, aber mit \`promise\`.
3831
+ \`\`\`js
3832
+ runScriptAsync('scriptName')
3833
+ .then(() => log('Script started, but not yet executed'));
3834
+
3835
+ // oder
3836
+
3837
+ await runScriptAsync('scriptName');
3838
+ log(\`Script was restarted\`);
3839
+ \`\`\`
3840
+
3841
+ ### startScript
3842
+ \`\`\`js
3843
+ startScript('scriptName', ignoreIfStarted, callback);
3844
+ \`\`\`
3845
+
3846
+ Startet das Skript. Wenn ignoreIfStarted auf true gesetzt ist, passiert nichts, falls das Skript bereits läuft, andernfalls wird das Skript neu gestartet.
3847
+
3848
+ \`\`\`js
3849
+ startScript('scriptName', true); // Skript starten, falls es nicht gestartet ist
3850
+ \`\`\`
3851
+
3852
+ ### startScriptAsync
3853
+ Wie startScript, aber mit \`promise\`.
3854
+
3855
+ \`\`\`js
3856
+ startScriptAsync('scriptName', ignoreIfStarted)
3857
+ .then(started => log(\`Script was \${started ? 'started' : 'already started'}\`));
3858
+
3859
+ // oder
3860
+
3861
+ const started = await startScriptAsync('scriptName', ignoreIfStarted);
3862
+ log(\`Script was \${started ? 'started' : 'already started'}\`);
3863
+ \`\`\`
3864
+
3865
+ Startet das Skript. Wenn ignoreIfStarted auf true gesetzt ist, passiert nichts, falls das Skript bereits läuft, andernfalls wird das Skript neu gestartet.
3866
+
3867
+ \`\`\`js
3868
+ startScript('scriptName', true); // Skript starten, falls es nicht gestartet ist
3869
+ \`\`\`
3870
+
3871
+ ### stopScript
3872
+ \`\`\`js
3873
+ stopScript('scriptName', callback);
3874
+ \`\`\`
3875
+
3876
+ Wenn stopScript ohne Argumente aufgerufen wird, stoppt sich das Skript selbst:
3877
+
3878
+ \`\`\`js
3879
+ stopScript();
3880
+ \`\`\`
3881
+
3882
+ ### stopScriptAsync
3883
+ Wie stopScript, aber mit \`promise\`:
3884
+ \`\`\`js
3885
+ stopScriptAsync('scriptName')
3886
+ .then(stopped => log(\`Script was \${stopped ? 'stopped' : 'already stopped'}\`));
3887
+
3888
+ // oder
3889
+ const stopped = await stopScriptAsync('scriptName');
3890
+ log(\`Script was \${stopped ? 'stopped' : 'already stopped'}\`);
3891
+ \`\`\`
3892
+
3893
+ Wenn stopScript ohne Argumente aufgerufen wird, stoppt sich das Skript selbst:
3894
+
3895
+ \`\`\`js
3896
+ stopScript();
3897
+ \`\`\`
3898
+
3899
+ ### isScriptActive
3900
+ \`\`\`js
3901
+ isScriptActive('scriptName');
3902
+ \`\`\`
3903
+
3904
+ Gibt zurück, ob ein Skript aktiviert oder deaktiviert ist. Dabei ist zu beachten, dass damit nicht angegeben wird, ob das Skript gerade läuft oder nicht.
3905
+ Das Skript kann beendet, aber dennoch aktiviert sein.
3906
+
3907
+ Es ist keine Funktion. Es ist eine Variable mit der JavaScript-Instanz, die im Gültigkeitsbereich des Skripts sichtbar ist.
3908
+
3909
+ ### toInt
3910
+ ### toFloat
3911
+ ### toBoolean
3912
+ ### jsonataExpression
3913
+
3914
+ ### wait
3915
+ Pausiert einfach die Ausführung des Skripts.
3916
+ Achtung: Diese Funktion ist ein \`promise\` und muss wie folgt aufgerufen werden:
3917
+ \`\`\`js
3918
+ await wait(1000);
3919
+ \`\`\`
3920
+
3921
+ ### sleep
3922
+ Wie [wait](#wait)
3923
+
3924
+ ### messageTo
3925
+ \`\`\`js
3926
+ messageTo({ instance: 'instance', script: 'script.js.common.scriptName', message: 'messageName' }, data, { timeout: 1000 }, result =>
3927
+ log(JSON.stringify(result)));
3928
+ \`\`\`
3929
+
3930
+ Sendet die Nachricht über den "Nachrichtenbus" an ein anderes Skript. Oder sogar an einen Handler im selben Skript.
3931
+
3932
+ Das Timeout für den Callback beträgt standardmäßig 5 Sekunden.
3933
+
3934
+ Das Ziel kann verkürzt werden zu:
3935
+
3936
+ \`\`\`js
3937
+ messageTo('messageName', data, (result) => {
3938
+ log(JSON.stringify(result));
3939
+ });
3940
+ \`\`\`
3941
+
3942
+ Callback und Optionen sind optional, das Timeout beträgt standardmäßig 5000 Millisekunden (falls ein Callback angegeben ist).
3943
+
3944
+ \`\`\`js
3945
+ messageTo('messageName', dataWithNoResponse);
3946
+ \`\`\`
3947
+
3948
+ ### messageToAsync
3949
+ \`\`\`js
3950
+ onMessage('myTopic', async (data, callback) => {
3951
+ log(data);
3952
+
3953
+ if (!data.myPayload) {
3954
+ // Fehler zurückgeben (Promise reject)
3955
+ callback({ error: 'something went wrong!!' });
3956
+ } else {
3957
+ // Ergebnis zurückgeben (Promise resolve)
3958
+ callback({ result: 'ok' });
3959
+ }
3960
+ });
3961
+
3962
+ (async () => {
3963
+ try {
3964
+ const msg = await messageToAsync({ instance: 0, script: 'script.js.test2', message: 'myTopic' }, { myPayload: true }, { timeout: 1000 });
3965
+ log(\`Done with: \${JSON.stringify(msg)}\`);
3966
+ } catch (error) {
3967
+ // Inhalt von result.error
3968
+ console.error(error);
3969
+ }
3970
+ })();
3971
+ \`\`\`
3972
+
3973
+ ### onMessage
3974
+ \`\`\`js
3975
+ onMessage('messageName', (data, callback) => {
3976
+ log(\`Received data: \${data}\`);
3977
+
3978
+ callback({ result: Date.now() });
3979
+ });
3980
+ \`\`\`
3981
+
3982
+ Abonniert den Nachrichtenbus des \`javascript\`-Adapters und liefert die Antwort über den Callback.
3983
+ Die Antwort des Skripts, das als erstes antwortet, wird als Antwort akzeptiert, alle anderen Antworten werden ignoriert.
3984
+
3985
+ Um eine Nachricht an ein JavaScript-Skript zu senden, die dann von diesem Handler empfangen wird, verwendet man [messageTo](#messageto).
3986
+
3987
+ Um eine Nachricht von einem beliebigen anderen Adapter zu senden, verwendet man
3988
+
3989
+ \`\`\`js
3990
+ adapter.sendTo('javascript.0', 'toScript', {
3991
+ script: 'script.js.messagetest',
3992
+ message: 'messageName',
3993
+ data: {
3994
+ flag: true
3995
+ }
3996
+ });
3997
+ \`\`\`
3998
+
3999
+ Um eine Nachricht über die CLI zu senden, verwendet man
4000
+
4001
+ \`\`\`bash
4002
+ iob message javascript.0 toScript '{"script": "script.js.messagetest", "message": "messageName", "data": { "flag": true }}'
4003
+ \`\`\`
4004
+
4005
+ ### onMessageUnregister
4006
+ \`\`\`js
4007
+ const id = onMessage('messageName', (data, callback) => {
4008
+ log(data);
4009
+ callback({ result: Date.now() });
4010
+ });
4011
+
4012
+ // Abonnement eines bestimmten Handlers beenden
4013
+ onMessageUnregister(id);
4014
+ // oder Abonnement über den Namen beenden
4015
+ onMessageUnregister('messageName');
4016
+ \`\`\`
4017
+
4018
+ Beendet das Abonnement dieser Nachricht.
4019
+
4020
+ ### onLog
4021
+ \`\`\`js
4022
+ onLog('error', data => {
4023
+ sendTo('telegram.0', { user: 'UserName', text: data.message });
4024
+ log('Following was sent to telegram: ' + data.message);
4025
+ });
4026
+ \`\`\`
4027
+
4028
+ Abonniert Logs mit dem angegebenen Schweregrad.
4029
+
4030
+ *Wichtig:* Im Handler können keine Logs mit demselben Schweregrad ausgegeben werden, um Endlosschleifen zu vermeiden.
4031
+
4032
+ Beispielsweise erzeugt dies keine Logs:
4033
+ \`\`\`js
4034
+ onLog('error', data => {
4035
+ console.error('Error: ' + data.message);
4036
+ });
4037
+ \`\`\`
4038
+
4039
+ Um alle Logs zu empfangen, kann \`*\` verwendet werden. In diesem Fall wird die Log-Ausgabe im Handler vollständig deaktiviert.
4040
+
4041
+ \`\`\`js
4042
+ onLog('*', data => {
4043
+ console.error('Error: ' + data.message); // erzeugt keine Logs
4044
+ });
4045
+ \`\`\`
4046
+
4047
+ ### onLogUnregister
4048
+ \`\`\`js
4049
+ function logHandler(data) {
4050
+ console.error('Error: ' + data.message);
4051
+ }
4052
+ const id = onLog('warn', logHandler);
4053
+
4054
+ // Abonnement über die ID beenden
4055
+ onLogUnregister(id);
4056
+ // oder Abonnement über die Handler-Funktion beenden
4057
+ onLogUnregister(logHandler);
4058
+ // oder Abonnement aller Handler mit einem bestimmten Schweregrad beenden
4059
+ onLogUnregister('warn');
4060
+ \`\`\`
4061
+
4062
+ Beendet das Abonnement dieser Logs.
4063
+
4064
+ ### httpGet
4065
+
4066
+ *Erfordert Version >= 7.9.0*
4067
+
4068
+ \`\`\`js
4069
+ httpGet('http://jsonplaceholder.typicode.com/posts', (err, response) => {
4070
+ if (!err) {
4071
+ console.log(response.statusCode);
4072
+ console.log(response.data);
4073
+ } else {
4074
+ console.error(err);
4075
+ }
4076
+ });
4077
+ \`\`\`
4078
+
4079
+ Der zweite Parameter kann ein Objekt mit weiteren Optionen sein (optional). Alle Optionen sind optional. Unterstützte Flags:
4080
+
4081
+ - \`timeout\` (number) - Timeout in Millisekunden
4082
+ - \`responseType\` (string) - Unterstützte Werte sind \`text\` (Standard) oder \`arraybuffer\` für Binärdaten in der Antwort
4083
+ - \`basicAuth\` (object) - Zugangsdaten für die HTTP-Basic-Authentifizierung, z. B. \`{ user: 'admin', password: 'iobroker' }\`
4084
+ - \`bearerAuth\` (string) - Token für die Bearer-Authentifizierung
4085
+ - \`headers\` (object) - Zusätzliche benutzerdefinierte HTTP-Header, z. B. \`{ 'Accept-Language': 'en-GB,en;q=0.9' }\`
4086
+ - \`validateCertificate\` (boolean) - Erlaubt selbstsignierte Zertifikate, wenn \`false\`
4087
+
4088
+ \`\`\`js
4089
+ httpGet('http://jsonplaceholder.typicode.com/posts', { timeout: 1000 }, (err, response) => {
4090
+ if (!err) {
4091
+ console.log(response.statusCode);
4092
+ console.log(response.data);
4093
+ } else {
4094
+ console.error(err);
4095
+ }
4096
+ });
4097
+ \`\`\`
4098
+
4099
+ Datei in das ioBroker-Dateisystem herunterladen:
4100
+
4101
+ \`\`\`js
4102
+ httpGet('http://1.2.3.4/image.jpg', { responseType: 'arraybuffer' }, async (err, response) => {
4103
+ if (!err) {
4104
+ writeFile('0_userdata.0', 'test.jpg', response.data, (err) => {
4105
+ if (err) {
4106
+ console.error(err);
4107
+ }
4108
+ });
4109
+ } else {
4110
+ console.error(err);
4111
+ }
4112
+ });
4113
+ \`\`\`
4114
+
4115
+ Zertifikatsprüfung deaktivieren - *Erfordert Version >= 8.4.0*
4116
+
4117
+ \`\`\`js
4118
+ httpGet('http://jsonplaceholder.typicode.com/posts', { validateCertificate: false }, (err, response) => {
4119
+ if (!err) {
4120
+ console.log(response.statusCode);
4121
+ console.log(response.data);
4122
+ } else {
4123
+ console.error(err);
4124
+ }
4125
+ });
4126
+ \`\`\`
4127
+
4128
+ ### httpPost
4129
+
4130
+ *Erfordert Version >= 7.9.0*
4131
+
4132
+ \`\`\`js
4133
+ httpPost('http://jsonplaceholder.typicode.com/posts', { title: 'foo', body: 'bar', userId: 1 }, (error, response) => {
4134
+ if (!error) {
4135
+ console.log(response.statusCode);
4136
+ console.log(response.data);
4137
+ console.log(response.headers);
4138
+ } else {
4139
+ console.error(error);
4140
+ }
4141
+ });
4142
+ \`\`\`
4143
+
4144
+ Mit benutzerdefinierten Headern und Authentifizierung
4145
+
4146
+ \`\`\`js
4147
+ httpPost(
4148
+ 'http://jsonplaceholder.typicode.com/posts',
4149
+ {
4150
+ title: 'foo',
4151
+ body: 'bar',
4152
+ userId: 1
4153
+ },
4154
+ {
4155
+ timeout: 2000,
4156
+ basicAuth: {
4157
+ user: 'admin',
4158
+ password: 'dg2LdALNznHFNo'
4159
+ },
4160
+ headers: {
4161
+ 'Cookie': 'PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1'
4162
+ }
4163
+ },
4164
+ (error, response) => {
4165
+ if (!error) {
4166
+ console.log(response.statusCode);
4167
+ console.log(response.data);
4168
+ console.log(response.headers);
4169
+ } else {
4170
+ console.error(error);
4171
+ }
4172
+ }
4173
+ );
4174
+ \`\`\`
4175
+
4176
+ ### createTempFile
4177
+
4178
+ *Erfordert Version >= 8.3.0*
4179
+
4180
+ \`\`\`js
4181
+ httpGet('https://raw.githubusercontent.com/ioBroker/ioBroker.javascript/master/admin/javascript.svg', { responseType: 'arraybuffer' }, async (err, response) => {
4182
+ if (err) {
4183
+ console.error(err);
4184
+ } else {
4185
+ const tempFilePath = createTempFile('javascript.svg', response.data);
4186
+ console.log(\`Saved to \${tempFilePath}\`);
4187
+
4188
+ // Den neuen Pfad in anderen Skripten verwenden (z. B. sendTo)
4189
+ }
4190
+ });
4191
+ \`\`\`
4192
+
4193
+ \`\`\`js
4194
+ onFile('0_userdata.0', '*.jpg', true, async (id, fileName, size, data, mimeType) => {
4195
+ const tempFilePath = createTempFile(fileName, response.data);
4196
+
4197
+ // Den neuen Pfad in anderen Skripten verwenden (z. B. sendTo)
4198
+ });
4199
+ \`\`\`
4200
+
4201
+ \`\`\`js
4202
+ readFile('0_userdata.0', 'test.jpg', (err, data, mimeType) => {
4203
+ if (err) {
4204
+ console.error(err);
4205
+ } else {
4206
+ const tempFilePath = createTempFile('test.jpg', data);
4207
+
4208
+ // Den neuen Pfad in anderen Skripten verwenden (z. B. sendTo)
4209
+ sendTo('telegram.0', 'send', {
4210
+ text: tempFilePath,
4211
+ caption: 'Just a test image',
4212
+ user: 'yourUsername',
4213
+ });
4214
+ }
4215
+ });
4216
+ \`\`\`
4217
+
4218
+ ### registerNotification
4219
+
4220
+ *Erfordert Version >= 8.8.0*
4221
+
4222
+ \`\`\`js
4223
+ registerNotification('This is just an information'); // Benachrichtigung
4224
+ registerNotification('This is an important message!', true); // Alarm
4225
+ \`\`\`
4226
+
4227
+ ## Globale Skriptvariablen
4228
+ ### scriptName
4229
+ \`scriptName\` - Der Name des Skripts.
4230
+
4231
+ \`\`\`js
4232
+ log(\`Script \${scriptName} started!\`);
4233
+ \`\`\`
4234
+
4235
+ ### instance
4236
+ \`instance\` - Die JavaScript-Instanz, in der ein Skript ausgeführt wird (z. B. \`0\`).
4237
+
4238
+ \`\`\`js
4239
+ log(\`Script \${scriptName} started started by \${instance}\`);
4240
+ \`\`\`
4241
+
4242
+ ### defaultDataDir
4243
+ \`defaultDataDir\` - Absoluter Pfad zu iobroker-data.
4244
+
4245
+ \`\`\`js
4246
+ log(\`Data dir: \${defaultDataDir}\`);
4247
+ \`\`\`
4248
+
4249
+ ### verbose
4250
+ \`verbose\` - Ist der ausführliche Modus (Verbose) aktiviert?
4251
+
4252
+ \`\`\`js
4253
+ log(\`Verbose mode: \${verbose ? 'enabled' : 'disabled'}\`);
4254
+
4255
+ // Beispiel
4256
+ if (verbose) {
4257
+ log('...');
4258
+ }
4259
+ \`\`\`
4260
+
4261
+ ### SECRETS
4262
+ \`SECRETS\` - Die Zugangsdaten aus dem zentralen Zugangsdaten-Speicher von ioBroker.
4263
+
4264
+ Die Zugangsdaten werden in der Admin-Oberfläche unter **Allgemeine Einstellungen** -> **Zugangsdaten** verwaltet. Jeder Zugangsdaten-Eintrag
4265
+ hat eine ID (wie \`CameraPassword\`) und enthält entweder einen einzelnen Schlüssel **key** (z. B. einen API-Schlüssel oder ein Passwort) oder ein
4266
+ Paar aus **login**/**password**. Die geheimen Felder werden mit dem System-Secret verschlüsselt gespeichert und
4267
+ den Skripten bereits entschlüsselt übergeben:
4268
+
4269
+ \`\`\`js
4270
+ // Zugangsdaten-Eintrag vom Typ "key"
4271
+ httpGet(\`http://camera.local/snapshot?password=\${SECRETS.CameraPassword.key}\`, (err, result) => {
4272
+ // ...
4273
+ });
4274
+
4275
+ // Zugangsdaten-Eintrag vom Typ "login"
4276
+ log(\`Mail account: \${SECRETS.MyMailAccount.login} / \${SECRETS.MyMailAccount.password}\`);
4277
+
4278
+ // IDs von Zugangsdaten, die keine gültigen Variablennamen sind
4279
+ log(SECRETS['My camera'].key);
4280
+ \`\`\`
4281
+
4282
+ \`SECRETS\` ist schreibgeschützt und immer aktuell: Wenn ein Zugangsdaten-Eintrag in der Admin-Oberfläche hinzugefügt, geändert oder gelöscht wird,
4283
+ wird der neue Wert sofort verwendet - weder der Adapter noch das Skript müssen neu gestartet werden.
4284
+
4285
+ Wenn ein Zugangsdaten-Eintrag nicht existiert, wird \`undefined\` zurückgegeben:
4286
+
4287
+ \`\`\`js
4288
+ if (SECRETS.CameraPassword) {
4289
+ log('The camera password is defined');
4290
+ }
4291
+ \`\`\`
4292
+
4293
+ #### Welche Felder hat ein Zugangsdaten-Eintrag?
4294
+
4295
+ Jeder Zugangsdaten-Eintrag hat entweder einen einzelnen \`key\` oder ein Paar \`login\`/\`password\`. Es gibt drei Möglichkeiten, das herauszufinden:
4296
+
4297
+ - In den Instanzeinstellungen des JavaScript-Adapters listet der Abschnitt **Verfügbare Zugangsdaten** jeden
4298
+ Zugangsdaten-Eintrag mit seinen Feldern und dem kopierfertigen Ausdruck auf.
4299
+ - Im Editor bietet die Autovervollständigung nach \`SECRETS.\` die vorhandenen Zugangsdaten an und nach dem
4300
+ nächsten Punkt genau die Felder, die dieser Zugangsdaten-Eintrag hat.
4301
+ - In einem Skript:
4302
+
4303
+ \`\`\`js
4304
+ log(JSON.stringify(Object.keys(SECRETS.CameraPassword))); // ["key"]
4305
+ log(JSON.stringify(Object.keys(SECRETS.MyMailAccount))); // ["login","password"]
4306
+ \`\`\`
4307
+
4308
+ Blockly hat für denselben Zweck einen **Zugangsdaten**-Baustein - siehe die
4309
+ [Blockly-Dokumentation](../en/blockly.md#credential) (englisch).
4310
+
4311
+ Der Zugriff kann mit der Instanzoption **Skripten den Zugriff auf die Zugangsdaten erlauben** abgeschaltet werden.
4312
+ \`SECRETS\` ist dann leer, und es wird eine Warnung ins Log geschrieben.
4313
+
4314
+ **Hinweis:** Dies erfordert js-controller 7.2 oder neuer.
4315
+
4316
+ ## Option - "Nicht alle Zustände beim Start abonnieren"
4317
+ Es gibt zwei Modi, States zu abonnieren:
4318
+
4319
+ 1. Der Adapter abonniert beim Start alle States und empfängt alle Änderungen aller States (getState(id) ist einfach zu verwenden, benötigt aber mehr CPU und RAM):
4320
+
4321
+ \`\`\`js
4322
+ log(getState('someID').val);
4323
+ \`\`\`
4324
+
4325
+ 2. Der Adapter abonniert die angegebene ID jedes Mal, wenn \`on/subscribe\` aufgerufen wird. In diesem Modus empfängt der Adapter nur Aktualisierungen für die gewünschten States. Diese Option benötigt weniger RAM und ist effizienter, allerdings kann man nicht synchron über getState auf States zugreifen. **Man muss Callbacks oder Promises verwenden, um auf die States zuzugreifen**:
4326
+
4327
+ \`\`\`js
4328
+ getState('someID', (error, state) => {
4329
+ log(state.val);
4330
+ });
4331
+ \`\`\`
4332
+
4333
+ Grund: Der Adapter hat den Wert des States nicht im RAM und muss ihn aus der zentralen State-Datenbank anfordern.
4334
+
4335
+ ## Skriptaktivität
4336
+
4337
+ Skripte können über States aktiviert und deaktiviert werden. Für jedes Skript wird ein State mit dem Namen \`javascript.INSTANCE.scriptEnabled.SCRIPT_NAME\` angelegt.
4338
+ Skripte können aktiviert und deaktiviert werden, indem dieser State mit \`ack=false\` gesteuert wird.
4339
+ `;n();var J={en:K,de:q},Y=`https://github.com/ioBroker/ioBroker.javascript/blob/master/docs/`,ie=`Editor.docScroll`,X=`Editor.docContents`,ae=1500,oe=`ui-monospace, "Cascadia Code", Consolas, monospace`,Z={};function se(e){let t=J[e]?e:`en`;if(!Z[t]){let e=L(I(J[t]));Z[t]={lang:t,blocks:e,entries:z(e)}}return Z[t]}function Q(e){try{return window.localStorage.getItem(e)}catch{return null}}function $(e,t){try{window.localStorage.setItem(e,t)}catch{}}function ce({onClose:e,word:n}){let{lang:r,blocks:p,entries:C}=se(g.getLanguage()),w=`${ie}.${r}`,T=b(null),E=b(null),D=b(0),O=b(null),[k,A]=o(()=>Q(X)!==`false`),[j,M]=o(``),[N,P]=o(``),F=S(()=>ne(C,j),[C,j]),I=()=>{let e=T.current;if(!e)return;let t=e.scrollTop+24,n=``;for(let r of Array.from(e.querySelectorAll(`[data-level]`)))if(!(Number(r.dataset.level)>3)){if(r.offsetTop>t)break;n=r.id.substring(V.length)}e.scrollTop+e.clientHeight>=e.scrollHeight-4&&O.current&&Date.now()-O.current.at<ae&&(n=O.current.anchor),P(n)},L=(e,t=!0)=>{var n;let r=(n=T.current)==null?void 0:n.querySelector(`#${CSS.escape(V+e)}`);r&&(O.current={anchor:e,at:Date.now()},P(e),r.scrollIntoView({block:`start`,behavior:t?`smooth`:`auto`}))};d(()=>{let e=B(C,n),t=Number(Q(w)),r=0,i=requestAnimationFrame(()=>{r=requestAnimationFrame(()=>{e?L(e,!1):T.current&&Number.isFinite(t)&&t>0&&(T.current.scrollTop=t),I()})});return()=>{cancelAnimationFrame(i),cancelAnimationFrame(r),cancelAnimationFrame(D.current)}},[]),d(()=>{if(N){var e;(e=E.current)==null||(e=e.querySelector(`[data-anchor="${CSS.escape(N)}"]`))==null||e.scrollIntoView({block:`nearest`})}},[N,k]);let R=()=>{D.current||=requestAnimationFrame(()=>{var e;D.current=0,$(w,String(((e=T.current)==null?void 0:e.scrollTop)||0)),I()})},z=S(()=>m(G,{blocks:p,base:`${Y}${r}/`,onAnchor:e=>L(e)}),[p,r]);return y(v,{open:!0,fullWidth:!0,maxWidth:`lg`,onClose:e,slotProps:{paper:{sx:{height:`calc(100% - 64px)`}}},children:[y(f,{sx:{display:`flex`,alignItems:`center`,gap:1,pb:1},children:[m(t,{title:g.t(`Contents`),slotProps:{popper:{sx:{pointerEvents:`none`}}},children:m(h,{size:`small`,color:k?`primary`:`default`,onClick:()=>{$(X,String(!k)),A(!k)},children:m(te,{})})}),g.t(`Documentation`)]}),y(_,{dividers:!0,sx:{display:`flex`,p:0,minHeight:0},children:[k?y(i,{sx:{flex:`0 0 280px`,minWidth:0,display:`flex`,flexDirection:`column`,minHeight:0,borderRight:1,borderColor:`divider`},children:[m(i,{sx:{p:1},children:m(a,{autoFocus:!0,fullWidth:!0,size:`small`,variant:`outlined`,placeholder:g.t(`Filter`),value:j,onChange:e=>M(e.target.value),onKeyDown:e=>{e.key===`Enter`&&F.length?L(F[0].anchor):e.key===`Escape`&&j&&(e.stopPropagation(),M(``))},slotProps:{input:{endAdornment:j?m(u,{position:`end`,children:m(h,{size:`small`,onClick:()=>M(``),children:m(s,{fontSize:`small`})})}):null}}})}),y(i,{ref:E,sx:{flex:1,overflowY:`auto`,pb:1},children:[F.map(e=>y(i,{"data-anchor":e.anchor,title:e.text,onClick:()=>L(e.anchor),sx:{display:`flex`,alignItems:`baseline`,gap:1,pl:e.level<3?1.5:3,pr:1,py:.4,mt:+(e.level<3&&!j),cursor:`pointer`,fontSize:e.level<3?14:13,fontWeight:e.level<3?500:400,color:e.anchor===N?`primary.main`:`text.primary`,bgcolor:e.anchor===N?`action.selected`:void 0,"&:hover":{bgcolor:`action.hover`}},children:[m(i,{component:`span`,sx:{flexShrink:+!e.code,fontFamily:e.code?oe:void 0,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:e.code?`nowrap`:void 0,maxWidth:`100%`},children:e.name}),e.description?m(i,{component:`span`,sx:{minWidth:0,fontSize:12,color:`text.secondary`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:e.description}):null]},e.anchor)),F.length?null:m(c,{sx:{px:1.5,py:1,fontSize:13,color:`text.secondary`},children:g.t(`Nothing found`)})]})]}):null,m(i,{ref:T,onScroll:R,sx:{flex:1,minWidth:0,overflowY:`auto`,position:`relative`,px:3,py:2},children:z})]}),m(ee,{children:m(l,{variant:`contained`,color:`grey`,startIcon:m(x,{}),onClick:e,children:g.t(`Close`)})})]})}export{ce as default};