Skip to content

Commit

Permalink
Added source documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
bchabrier committed Oct 29, 2021
1 parent 34ff836 commit becc60f
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,51 @@ Domoja collects information from and interacts with devices through sources. You

### Sources

A source provides information about, and allows controlling a certain set of devices.

To use a source, it is necessary to load its type in Domoja. Some source types are predefined in Domoja, some others can be add by extension modules:
```
$ yarn add domoja-<source-module>
```

Once loaded in Domoja, the module providing the source type needs to be imported in the configuration before a source of this type can be declared and then referenced by a device.

The example below shows how to create the source `myAstronomy` of type `astronomy` from the module `proxity`, to get the sunrise time:
```
$ yarn add domoja-proxity
```
```
imports:
- module: proxiti
source: astronomy
sources:
- myAstronomy: {
type: astronomy,
location: "06030"
}
devices:
- sunrise: { type: device, widget: text, tags: 'astronomyTag', source: myAstronomy, id: sunriseTime, name: "Lever du soleil" }
```

[//]: # (sourcesList START)

Source type | Module | Description
----------- | ------ | -----------
astronomy | [proxiti](https://www.npmjs.com/package/domoja-proxiti) | This source provides astronomy information from http://www.proxiti.info/horaires_soleil.php?o=06030<br>This includes sunset, sunrise, dawn, dusk, zenith times, and day duration, at a specific location.<br>Parameters:<ul><li> location: the code corresponding to your location. Use https://www.proxiti.info/index.php to find it.</li><br></ul>Example:<pre><code>sources:</code><br><code> - astronomy: {</code><br><code> type: astronomy,</code><br><code> location: "06030"</code><br><code> }</code><br><code></code><br><code>devices:</code><br><code> - sunset: { type: device, widget: text, tags: 'astronomy', source: astronomy, id: sunsetTime, name: "Coucher du soleil" }</code><br><code></code></pre>
command | core/sources/command | Source implemented with shell commands:<ul><li> parameters define the shell commands to execute when a device takes a given value</li> Example with parameters `ON` and `OFF` :<pre><code>- sources:</code><br><code> - robonect-command: {</code><br><code> type: command,</code><br><code> ON: "AUTH=\$(grep robonectBasicAuth config/secrets.yml \| sed -e 's!^ *[^:][^:]*: *!!' -e 's/[\\r\\n]//g'); curl 'http://192.168.0.16/xml?cmd=start' -s -u \$AUTH", </code><br><code> OFF: "AUTH=\$(grep robonectBasicAuth config/secrets.yml \| sed -e 's!^ *[^:][^:]*: *!!' -e 's/[\\r\\n]//g'); curl 'http://192.168.0.16/xml?cmd=stop' -s -u \$AUTH"</code><br><code> }</code><br><code></code></pre><li> the optional parameter `pushupdates` is a shell command executed once as a daemon at the creation of the source</li><ul><li> it allows to emit changes of device state values </li><li> it shoud produce stdout output in the form `{"id": "<device_id>", "attribute": "<attribute>", "value": "<value>"}`, e.g. `{"id": "temp", "attribute": "state", "value": "10 °C"}`</li><li> the daemon will be killed when the source is released, but to avoid zombie processes to be created, it is good to guard a loop by checking the parent process, for example:</li><pre><code>while [ \$(ps -o ppid= \$\$) != 1 ]; do <commands>; sleep 60; done</code><br><code></code></pre><li> available variables are:</li><ul><li> ID: id of the device using the source</li><li> SOURCE: the path of the source</li><li> DEBUG: debug mode of the source ('0'\|'1') </li><br></ul></ul></ul>Example: <pre><code>sources:</code><br><code>- disk-usage: {</code><br><code> type: command,</code><br><code> push-updates: "</code><br><code> while [ \$(ps -o ppid= \$\$) != 1 ]</code><br><code> do </code><br><code> df -k \| awk '{</code><br><code> mount=\$6</code><br><code> percent=\$5</code><br><code> str=\\"{ \\\\\\"id\\\\\\": \\\\\\"\\"mount\\"\\\\\\", \\\\\\"attribute\\\\\\": \\\\\\"state\\\\\\", \\\\\\"value\\\\\\": \\\\\\"\\"percent\\"\\\\\\"}\\"</code><br><code> if ('\$DEBUG') print str > \\"/dev/stderr\\" # debug</code><br><code> print str</code><br><code> }'</code><br><code> sleep 60</code><br><code> done</code><br><code> "</code><br><code>}</code><br><code></code></pre>
Freebox | [freebox](https://www.npmjs.com/package/domoja-freebox) |
IPX800 | [ipx800](https://www.npmjs.com/package/domoja-ipx800) |
Mqtt | [mqtt](https://www.npmjs.com/package/domoja-mqtt) |
Openzwave | [openzwave](https://www.npmjs.com/package/domoja-openzwave) |
Sample | [sample](https://www.npmjs.com/package/domoja-sample) |
tempo | [tempo](https://www.npmjs.com/package/domoja-tempo) |
VoiceByGoogle | [voice-google](https://www.npmjs.com/package/domoja-voice-google) |
Zibase | [zibase](https://www.npmjs.com/package/domoja-zibase) |

[//]: # (sourcesList END)

### Devices

Expand Down
45 changes: 45 additions & 0 deletions core/sources/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,51 @@ import { Source, ConfigLoader, GenericDevice, InitObject, Parameters } from '..'
import * as child_process from 'child_process';
import * as kill from 'tree-kill';

/**
* Source implemented with shell commands:
* - parameters define the shell commands to execute when a device takes a given value
* Example with parameters `ON` and `OFF` :
* ```
* - sources:
* - robonect-command: {
* type: command,
* ON: "AUTH=$(grep robonectBasicAuth config/secrets.yml | sed -e 's!^ *[^:][^:]*: *!!' -e 's/[\r\n]//g'); curl 'http://192.168.0.16/xml?cmd=start' -s -u $AUTH",
* OFF: "AUTH=$(grep robonectBasicAuth config/secrets.yml | sed -e 's!^ *[^:][^:]*: *!!' -e 's/[\r\n]//g'); curl 'http://192.168.0.16/xml?cmd=stop' -s -u $AUTH"
* }
* ```
* - the optional parameter `push-updates` is a shell command executed once as a daemon at the creation of the source
* - it allows to emit changes of device state values
* - it shoud produce stdout output in the form `{"id": "<device_id>", "attribute": "<attribute>", "value": "<value>"}`, e.g. `{"id": "temp", "attribute": "state", "value": "10 °C"}`
* - the daemon will be killed when the source is released, but to avoid zombie processes to be created, it is good to guard a loop by checking the parent process, for example:
* ```
* while [ $(ps -o ppid= $$) != 1 ]; do <commands>; sleep 60; done
* ```
* - available variables are:
* - ID: id of the device using the source
* - SOURCE: the path of the source
* - DEBUG: debug mode of the source ('0'|'1')
*
* Example:
* ```
* sources:
* - disk-usage: {
* type: command,
* push-updates: "
* while [ $(ps -o ppid= $$) != 1 ]
* do
* df -k | awk '{
* mount=$6
* percent=$5
* str=\"{ \\\"id\\\": \\\"\"mount\"\\\", \\\"attribute\\\": \\\"state\\\", \\\"value\\\": \\\"\"percent\"\\\"}\"
* if ('$DEBUG') print str > \"/dev/stderr\" # debug
* print str
* }'
* sleep 60
* done
* "
* }
* ```
*/
export class command extends Source {

stateUpdaterProcess: child_process.ChildProcessWithoutNullStreams;
Expand Down
4 changes: 4 additions & 0 deletions core/sources/demo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Source, ConfigLoader, GenericDevice, InitObject, Parameters } from '..';

/**
* Source used for the demo mode
*
*/
export class demo extends Source {
createInstance(configLoader: ConfigLoader, path: string, initObject: InitObject): Source {
return new demo(path);
Expand Down
20 changes: 20 additions & 0 deletions modules/proxiti/sources/astronomy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ const SOLEIL_AU_ZENITH = 5;
const DEBUT_DE_L_AUBE = 6;
const FIN_DU_CREPUSCULE = 7;

/**
* This source provides astronomy information from http://www.proxiti.info/horaires_soleil.php?o=06030
*
* This includes sunset, sunrise, dawn, dusk, zenith times, and day duration, at a specific location.
*
* Parameters:
* - location: the code corresponding to your location. Use https://www.proxiti.info/index.php to find it.
*
* Example:
* ```
* sources:
* - astronomy: {
* type: astronomy,
* location: "06030"
* }
*
* devices:
* - sunset: { type: device, widget: text, tags: 'astronomy', source: astronomy, id: sunsetTime, name: "Coucher du soleil" }
* ```
*/
export class astronomy extends Source {
job: any;
request: request.Request;
Expand Down
165 changes: 165 additions & 0 deletions tools/update_readme
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,176 @@ CLEANUP()
mv $tmpreadme.FULL $readme
}

# extract from $1 the comments preceeding the line specified with regex or string $2
# - the comment signs (/* etc) are removed
# - the lines are cleaned (removing trailing \r, backslashing \\s etc)
extract_doc()
{
local file="$1"
local linespec="$2"
awk '
BEGIN {
debug=0
incomment=0
comment=""
}
{
gsub(/\r$/,"")
}
/^\/\*\*/ {
if (debug==1) print "start comment" > "/dev/stderr"
incomment=1
next
}
incomment==1 && /^ \*\// {
if (debug==1) print "end comment" > "/dev/stderr"
incomment=0
line=NR
next
}
incomment==1 && /^ \* / {
gsub(/^ \* /,"")
if (debug==1) print "cleaning $0:", $0 > "/dev/stderr"
comment=comment==""?$0:comment"\n"$0
}
incomment==0 && NR==line+1 && '"$linespec"' {
print comment
exit
}
' "$file"
}

# filter that translates md description into md/html that can fit into a md table
# (mainly handles blockquotes(```) and lists(-))
md2htmlmd()
{
awk '
BEGIN {
debug=0
blockquote=0
indent=0
indents[indent]=0
string=""
}
/^ *$/ { # empty line
if (debug==1) print "empty line" > "/dev/stderr"
if (blockquote==1)
string=string"</code><br><code>"
else
string=string"<br>"
next
}
blockquote==0 && /```/ {
if (debug==1) print "start blockquote" > "/dev/stderr"
blockquote=1
blockquoteindent=index($0, "`")
string=string"<pre><code>"
next
}
blockquote==1 && /```/ {
if (debug==1) print "end blockquote" > "/dev/stderr"
blockquote=0
blockquoteindent=""
string=string"</code></pre>"
next
}
blockquote==0 && /^ *- / { # list
if (debug==1) print "list:", $0 > "/dev/stderr"
newindent=index($0, "-")
gsub(/-/,"")
if (debug==1) print "newindent="newindent, "previndent="indents[indent] > "/dev/stderr"
if (newindent > indents[indent]) {
if (debug==1) print "start list" > "/dev/stderr"
indent++
indents[indent]=newindent
string=string"<ul><li>"$0"</li>"
} else if (newindent < indents[indent]) {
while (newindent < indents[indent]) {
if (debug==1) print "end list" > "/dev/stderr"
indent--
string=string"</ul>"
}
comment=comment"<li>"$0"</li>"
} else if (newindent == indents[indent]) {
if (debug==1) print "inside list" > "/dev/stderr"
string=string"<li>"$0"</li>"
}
next
}
blockquote==1 { # simple line in blockquote
if (debug==1) print "inside blockquote:", $0 > "/dev/stderr"
#gsub(/ /, " `\\&#x200B;`") # zero width space to avoid multiple spaces to be taken as one only
# remove leading blanks of the indentation
for (i=1; i < blockquoteindent; i++)
if (substr($0, 1, 1) == " ") $0 = substr($0, 2)
string=string$0"</code><br><code>"
next
}
blockquote==0 { # simple line
newindent=0
while (substr($0, newindent + 1, 1) == " ") newindent++
if (newindent < indents[indent]) {
while (newindent < indents[indent]) {
if (debug==1) print "end list" > "/dev/stderr"
indent--
string=string"</ul>"
}
}
if (debug==1) print "simple line:", $0 > "/dev/stderr"
string=string$0
next
}
END {
gsub(/``/, "", string)
# gsub(/\\/,"\\\\\\", string)
# manual treatment of \\
newstring=""
for (i=1; i<=length(string); i++) {
c = substr(string, i, 1)
if (c == "\\") {
newstring=newstring"\\\\\\\\"
#i++
} else {
newstring=newstring""c
}
}
string=newstring
gsub(/[|]/,"\\|", string)
gsub(/\$/, "\\$", string) # $ enters math mode in vscode (not in github)
printf "%s", string
}
'
}

# replacement sections
if ! $sourced_only
then
INIT

START sourcesList
(
echo # needed or the sourcesList balise is visible on github
echo 'Source type | Module | Description'
echo '----------- | ------ | -----------'

(
# list local sources
grep -e "export *class *[a-Z0-9_]* *extends *Source" /dev/null core/sources/*.ts | awk -F'[ .:]' '
$5 == "DefaultSource" {next}
$5 == "demo" {next}
{ print $1"."$2, $5, $1}'

# list local domoja modules
grep -e "export *class *[a-Z0-9_]* *extends *Source" /dev/null modules/*/sources/*.ts | awk -F'[ /:]' '{ print $1"/"$2"/"$3"/"$4, $7, "["$2"](https://www.npmjs.com/package/domoja-"$2")"}'
) | sort -f -k 2 | while read file module source
do
description=$(extract_doc "$file" "/export +class +[a-zA-Z0-9_]+ +extends +Source/" | md2htmlmd)
echo $module "|" $source "|" "$description"
done
) > $tmpreadme
END

START modulesList
(
# get all local modules names
Expand Down

0 comments on commit becc60f

Please sign in to comment.