<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Josu Igoa</title>
    <description>The latest articles on Forem by Josu Igoa (@josuigoa).</description>
    <link>https://forem.com/josuigoa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F933104%2Fc7299b11-fe23-4b3b-98c1-a172a779562a.jpeg</url>
      <title>Forem: Josu Igoa</title>
      <link>https://forem.com/josuigoa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/josuigoa"/>
    <language>en</language>
    <item>
      <title>Writing an action for Ideckia</title>
      <dc:creator>Josu Igoa</dc:creator>
      <pubDate>Wed, 08 Mar 2023 09:04:41 +0000</pubDate>
      <link>https://forem.com/josuigoa/writing-an-action-for-ideckia-1pki</link>
      <guid>https://forem.com/josuigoa/writing-an-action-for-ideckia-1pki</guid>
      <description>&lt;p&gt;If you don't know what Ideckia is, I wrote an &lt;a href="https://dev.to/josuigoa/open-source-streamdeck-alternative-1gf3"&gt;introductory post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To get some context, this is how the Ideckia server works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the &lt;code&gt;layout.json&lt;/code&gt; file where all the items are defined (with their attached actions).&lt;/li&gt;
&lt;li&gt;Initializes all the actions defined in that file and injects to action each instance the properties defined for each case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post we will create our very first Ideckia action, a simple parametrized action to update an item with a text, we will follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the JavaScript file with the Ideckia actions structure&lt;/li&gt;
&lt;li&gt;Configure the new action in the editor.&lt;/li&gt;
&lt;li&gt;Check it is working as expected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a proof of what easy (and fast) is to create an action for Ideckia: I've created the action itself, configured it in the editor, documented each step, took screenshots, write a basic post, some bugfixes in the server... and it took me &lt;strong&gt;only 10 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creation of the action file
&lt;/h3&gt;

&lt;p&gt;If we haven't Ideckia in our machine, we must &lt;a href="https://ideckia.github.io" rel="noopener noreferrer"&gt;download&lt;/a&gt; and uncompress it in a directory.&lt;/p&gt;

&lt;p&gt;Go to the &lt;code&gt;actions&lt;/code&gt; directory and create a new directory called &lt;code&gt;simpleaction&lt;/code&gt; (or the name you prefer, is your action!). Inside that directory create a file called &lt;code&gt;index.js&lt;/code&gt;, this name is mandatory, so you can't pick another one, sorry.&lt;/p&gt;

&lt;p&gt;Paste this code in the &lt;code&gt;index.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleAction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Method called to inject the properties and server access (dialogs, logs...)&lt;/span&gt;
    &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ideckia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Method called when the action is loaded&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Method called when the item is clicked in the client&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Clicked at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Extra! Extra!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`A simple hello from a simple action! My name is [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Method called when the item is long pressed in the client&lt;/span&gt;
    &lt;span class="nf"&gt;onLongPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Method called from the editor to create an UI to configure the action&lt;/span&gt;
    &lt;span class="nf"&gt;getActionDescriptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;simpleaction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Demo of a simple Ideckia action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;String&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;isShared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ideckia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A name shown in the window&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IdeckiaAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SimpleAction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;There are two methods that needs to be explained: &lt;code&gt;execute&lt;/code&gt; and &lt;code&gt;getActionDescriptor&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;execute&lt;/code&gt;: This method is called when the item is clicked in the client. The code we put in there will do this:

&lt;ul&gt;
&lt;li&gt;Update the text of the client item.&lt;/li&gt;
&lt;li&gt;Show up a information dialog box with the given text. The text is the property called &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;getActionDescriptor&lt;/code&gt;: This method is called by the editor. It returns the properties that can be configured by the editor. In the example above, we've added the property &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure the action
&lt;/h3&gt;

&lt;p&gt;Now we must start the application (if it isn't already). When it is running, go to the &lt;a href="http://localhost:8888/editor" rel="noopener noreferrer"&gt;editor&lt;/a&gt;. We will add the action to the panel to test it. At this point it's convenient to be familiar with the &lt;a href="https://github.com/ideckia/ideckia/wiki/Concepts" rel="noopener noreferrer"&gt;concepts used by Ideckia&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select an empty item&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vcky8fr3s9tlp4t6vc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vcky8fr3s9tlp4t6vc2.png" alt="Selecting the item" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a state to the item. It will ask you to select between 'Change directory' or 'Multi State'. We will selecte the second one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjg0js0zyw0s22ursepr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjg0js0zyw0s22ursepr.png" alt="Adding a state to the item" width="475" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Optional) Select the state element and edit it: text, text color, background color, icon, text size...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hri1jqnfwu2yey4lsqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hri1jqnfwu2yey4lsqy.png" alt="Editing the state" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add an action to the state&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tc4fknl5etc9skh759f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tc4fknl5etc9skh759f.png" alt="Adding an action" width="249" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select our action from the actions list&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi488g2e12rgyerp8le8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi488g2e12rgyerp8le8v.png" alt="Selecting our action" width="626" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the property of our action&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0e73sxkfkttq01e98i48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0e73sxkfkttq01e98i48.png" alt="Action property editing" width="330" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Update server layout&lt;/code&gt; button to save the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to test
&lt;/h3&gt;

&lt;p&gt;Be sure you have Ideckia running. Open an Ideckia client (&lt;a href="https://github.com/ideckia/mobile_client/releases/latest" rel="noopener noreferrer"&gt;mobile&lt;/a&gt;, &lt;a href="https://github.com/josuigoa/ideckia_client/releases/latest" rel="noopener noreferrer"&gt;desktop&lt;/a&gt;). If you don't have any of these, the &lt;a href="http://localhost:8888/editor" rel="noopener noreferrer"&gt;editor&lt;/a&gt; can be used as client checking the checkbox in the top right corner. Once it is selected, any click on an item will be considered a click in a client, triggering the action in the server.&lt;/p&gt;

&lt;p&gt;So, click the item in any (or every!) client connected to the server &lt;em&gt;et voilà!&lt;/em&gt; The item text will show the current timestamp and a info dialog box will pop up in your screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg60rmrswpumuywcljukd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg60rmrswpumuywcljukd.png" alt="Info dialog" width="406" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/josuigoa/simpleaction" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the entire code of this project&lt;/p&gt;

&lt;h3&gt;
  
  
  Change the code and play with it
&lt;/h3&gt;

&lt;p&gt;Ok, you've seen what is going on. Now it is time to tinker with the new action. What will happen if you...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a new property to change the title of the dialog box?&lt;/li&gt;
&lt;li&gt;Change the background color of the item when it is clicked?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember that the state of the item has this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ItemState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;
    &lt;span class="nx"&gt;textSize&lt;/span&gt;
    &lt;span class="nx"&gt;textColor&lt;/span&gt;
    &lt;span class="nx"&gt;textPosition&lt;/span&gt;
    &lt;span class="nx"&gt;icon&lt;/span&gt;
    &lt;span class="nx"&gt;bgColor&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have fun!&lt;/p&gt;

</description>
      <category>security</category>
      <category>resources</category>
    </item>
    <item>
      <title>Open source StreamDeck alternative</title>
      <dc:creator>Josu Igoa</dc:creator>
      <pubDate>Thu, 16 Feb 2023 11:26:31 +0000</pubDate>
      <link>https://forem.com/josuigoa/open-source-streamdeck-alternative-1gf3</link>
      <guid>https://forem.com/josuigoa/open-source-streamdeck-alternative-1gf3</guid>
      <description>&lt;p&gt;I've always been attracted by StreamDeck for developers productivity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mute/unmute microphone ...*&lt;/li&gt;
&lt;li&gt;Open all the needed applications when starting the day at once ...*&lt;/li&gt;
&lt;li&gt;Write log-in information (user/password) ...*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*... with a single click of a button&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;, you must buy a hardware of thousands of € or pay a smartphone app (monthly 2.99€) just for testing. Only works for windows &amp;amp; mac and writing plugins is not very straighforward (at least for me).&lt;/p&gt;

&lt;p&gt;It is very important for me to create my own plugins&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;I know there are some alternative solutions that works with smartphones such as &lt;a href="https://www.touch-portal.com/" rel="noopener noreferrer"&gt;touch-portal&lt;/a&gt; and &lt;a href="https://macrodeck.org/" rel="noopener noreferrer"&gt;macrodeck&lt;/a&gt; but they have some things that I don't like.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They are closed source (Maybe macrodeck is now open sourced? If it's so, too late, I'm on my way!).&lt;/li&gt;
&lt;li&gt;You have to install some dependencies (java and .net) to run them.&lt;/li&gt;
&lt;li&gt;There is no solution for linux machines&lt;/li&gt;
&lt;li&gt;They have a complex system to create plug-ins (install SDK, create a package...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, as any software developer, because the solutions out there weren't satisfing my needs 150%: I created my own solution!&lt;/p&gt;

&lt;h2&gt;
  
  
  My own solution (a.k.a. Ideckia)
&lt;/h2&gt;

&lt;p&gt;My main goal was to create a tool which was very easy to write plug-ins for it (and hack the existing ones). A tool which was able to work the same way in windows, macos and linux.&lt;/p&gt;

&lt;p&gt;The first I though was the name :-) I had the perfect name for it: &lt;strong&gt;ideckia&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hiztegiak.elhuyar.eus/eu_en/ireki" rel="noopener noreferrer"&gt;irekia&lt;/a&gt; means "open" in my mother language, Basque. And &lt;code&gt;deck&lt;/code&gt;... well, you get the reference, right?&lt;/p&gt;

&lt;p&gt;After that come the minor things, the plan for the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a server in the computer with duplex communication for the client (smartphone, desktop...).&lt;/li&gt;
&lt;li&gt;Save all the data in a plain json file. Human readable, no crypto-data&lt;/li&gt;
&lt;li&gt;Use NodeJS as it is cross-platform&lt;/li&gt;
&lt;li&gt;Make the actions (or plug-ins) in a single JavaScript file

&lt;ul&gt;
&lt;li&gt;The actions may match a structure which the server expects to call some functions: initializing, item-pressed...&lt;/li&gt;
&lt;li&gt;As the actions are Javascript files, you can use the vast library from npm&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Load the actions to run at startup from a directory. Making them interchangeable/hackable.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I will dig in the server, actions and client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server side
&lt;/h3&gt;

&lt;p&gt;These are the core concepts of the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout: Bunch of items&lt;/li&gt;
&lt;li&gt;Item: An element that has a single or multiple states. It is clickable in the client.&lt;/li&gt;
&lt;li&gt;State: Definition of the item status: text, textColor, bgColor, icon and an action list.&lt;/li&gt;
&lt;li&gt;Action: Action which will be fired (can be more than one in a state) in the host computer when the item is pressed in the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server is a simple app which reads a JSON file which defines the layout. The work of the server is to manage the current directory, send the needed information to show in the client. And when an item is clicked in the client, the servers runs the actions attached to that item or sets the directory to show. This server-client communication is done by Websockets.&lt;/p&gt;

&lt;p&gt;The server directory tree looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-- ideckia         -&amp;gt; the app executable
|-- app.props       -&amp;gt; properties for the server (actions directory path, layout file path, log level, listening port...)
|-- layout.json     -&amp;gt; the layout information
|-+ actions         -&amp;gt; directory where the actions are stored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The layout can be edited by hand, but the server provides an integrated editor to edit your layout. Open the browser and go to &lt;a href="http://localhost:8888/editor" rel="noopener noreferrer"&gt;http://localhost:8888/editor&lt;/a&gt; (the port is the one where the server is running). I know it is not the prettiest thing, but it does get the job done.&lt;/p&gt;

&lt;p&gt;Now we'll see how are the actions internals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;Actions sources are available in the &lt;code&gt;actions&lt;/code&gt; folder usually (configurable via &lt;code&gt;app.props&lt;/code&gt; file next to the executable). Every action is defined in it's own folder and an &lt;code&gt;index.js&lt;/code&gt; file in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-- ideckia
|-- app.props
|-- layout.json
|-- actions
|   |-- my_action
|       |-- index.js
|   |-- another_action
|       |-- index.js
|       |-- dependencies.js
|       |-+ node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;index.js&lt;/code&gt; file must have this functions defined to be called from the server when loaded and executed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setup(props, server):Void

&lt;ul&gt;
&lt;li&gt;Server will call to this method to inject the configuration properties of the action instance and some convenient functions from the server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;init(initialState:ItemState):Promise

&lt;ul&gt;
&lt;li&gt;The server will call to this once, to initialize what you need&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;execute(currentState:ItemState):Promise

&lt;ul&gt;
&lt;li&gt;The server will call when the associated item is clicked.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;onLongPress(currentState:ItemState):Promise

&lt;ul&gt;
&lt;li&gt;The server will call when the associated item is long pressed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;getActionDescriptor():ActionDescriptor

&lt;ul&gt;
&lt;li&gt;This method is used by the editor.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;There are some default actions that comes in the default ideckia package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_command" rel="noopener noreferrer"&gt;Command&lt;/a&gt;: Execute an application or shell file with given parameters &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_keyboard" rel="noopener noreferrer"&gt;Keyboard&lt;/a&gt;: Create hotkeys, write strings... A wrapper for &lt;a href="http://nutjs.dev/" rel="noopener noreferrer"&gt;NutJs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_counter" rel="noopener noreferrer"&gt;Counter&lt;/a&gt;: Count how many times the item is clicked. Can be a countdown too.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_random-color" rel="noopener noreferrer"&gt;Random color&lt;/a&gt;: Generate random color and show it in the item.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_stopwatch" rel="noopener noreferrer"&gt;Stopwatch&lt;/a&gt;: Executing this action, will start and pause a timer shown in the button itself.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_timezones" rel="noopener noreferrer"&gt;Timezones&lt;/a&gt;: Show the time in the configurated timezones.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_worklog" rel="noopener noreferrer"&gt;Worklog&lt;/a&gt;: Log you daily work in a plain json file.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_ftp-connect" rel="noopener noreferrer"&gt;FTP-Connect&lt;/a&gt;: Connect to FTP in a simple and fast way.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_ssh-connect" rel="noopener noreferrer"&gt;SSH-Connect&lt;/a&gt;: Connect to SSH in a simple and fast way.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_open-weather" rel="noopener noreferrer"&gt;Open weather&lt;/a&gt;: Show the weather in the configured towns.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_clementine-control" rel="noopener noreferrer"&gt;Clementine control&lt;/a&gt;: Control &lt;a href="https://www.clementine-player.org/" rel="noopener noreferrer"&gt;Clementine&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_mute-mic" rel="noopener noreferrer"&gt;Mute mic&lt;/a&gt;: Mute / unmute microphone.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_toot" rel="noopener noreferrer"&gt;Toot&lt;/a&gt;: Publish a toot in mastodon.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_wait" rel="noopener noreferrer"&gt;Wait&lt;/a&gt;: Waits given milliseconds until the next action.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_countdown" rel="noopener noreferrer"&gt;Countdown&lt;/a&gt;: Countdown timer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_log-in" rel="noopener noreferrer"&gt;Log-in&lt;/a&gt;: Writes username and password and presses 'Enter' (this action uses &lt;a href="https://github.com/ideckia/action_keyboard" rel="noopener noreferrer"&gt;Keyboard&lt;/a&gt;, it is mandatory).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_color-picker" rel="noopener noreferrer"&gt;Color-picker&lt;/a&gt;: Show in the item the color of the pixel where the mouse is.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_obs-control" rel="noopener noreferrer"&gt;Obs-control&lt;/a&gt;: Control OBS Studio (replacing former &lt;a href="https://github.com/ideckia/action_obs-websocket" rel="noopener noreferrer"&gt;obs-websocket action&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_keepassxc" rel="noopener noreferrer"&gt;KeePasXC&lt;/a&gt;: Get write the username and password from &lt;a href="https://keepassxc.org/" rel="noopener noreferrer"&gt;KeePassXC&lt;/a&gt;. This action uses &lt;a href="https://github.com/ideckia/action_log-in" rel="noopener noreferrer"&gt;Log-in&lt;/a&gt;, it is mandatory.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ideckia/action_emoji" rel="noopener noreferrer"&gt;Emoji&lt;/a&gt;: Shows a random emoji every time is clicked&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Client
&lt;/h3&gt;

&lt;p&gt;The server sends the panel information to any connected client. And those clients send to the server which item has been clicked. There is a &lt;a href="http://github.com/ideckia/mobile_client" rel="noopener noreferrer"&gt;mobile client&lt;/a&gt; written in Flutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I've achieved
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cross-platform application (windows, mac and linux)&lt;/li&gt;
&lt;li&gt;Portable (no installation needed)

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://github.com/vercel/pkg" rel="noopener noreferrer"&gt;PKG&lt;/a&gt; to create a single executable&lt;/li&gt;
&lt;li&gt;light executable &amp;lt; 40MB&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;modular and hackable

&lt;ul&gt;
&lt;li&gt;Any action can be changed&lt;/li&gt;
&lt;li&gt;The editor can be easily stylized&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;All the layout info in a plain json file&lt;/li&gt;

&lt;li&gt;Tons of NPM packages usable&lt;/li&gt;

&lt;li&gt;Easy to create and test new actions&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;More info &lt;a href="https://ideckia.github.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
