homescript

command module
v0.17.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2022 License: GPL-2.0 Imports: 6 Imported by: 0

README

The Homescript DSL

Go Build Typo Check

Homescript is a fast and custom DSL (domain-specific language) for the Smarthome server and the Homescript CLI.

It provides a scripting interface for Smarthome users in order to create customized routines and workflows.

Feature Documentation

Builtin variables

Weather
print(weather)

The current weather in a human-readable format. Possible weather types should be:

  • sunny
  • rainy
  • cloudy
  • windy

Like any other builtin, the functionality must be provided by the host-software.

Note: This means that variable values may change

Temperature
print(temperature)

The current temperature in the area of the Smarthome-server can be queried with the code above.

Note: the temperature's measurement unit is dependent on the host implementation

Time
print(currentYear)
print(currentMonth)
print(currentWeek)
print(currentDay)
print(currentHour)
print(currentMinute)
print(currentSecond)

Returns the values matching the local time of the Smarthome-server

User
print(user)

Prints the username of the user currently running the script

Builtin Functions

Switch
Changing power of a switch
switch("switchName", on)
switch("switchName", true)
switch("switchName", off)
switch("switchName", false)

Changes the power state of a given switch. A real implementation should check following things

  • The user's permissions for the switch
  • The validity of the switch
Querying power of a switch
print(switchOn("switchName"))

The code above should return the power state of the requested switch as a boolean value.

Notifications
notify("Notification Title", "An interesting description", 1)

Notify sends a push-notification to the current user.

Legal notification levels (last parameter) are:

  • 1 Info
  • 2 Warn
  • 3 Error
Users
Add User

Creates a new user with included metadata

addUser('username', 'password', 'forename', 'surname')
Delete User

Deletes a user and all their data

delUser('username')
Add Permission

Adds a permission to an arbitrary user

addPerm('username', 'permission')
Delete Permission

Removes a permission from an arbitrary user

delPerm('username', 'permission')
Logging
log("Log Title", "What happened?", 4)

Logs a message to the server's console and to the internal logging system.

Depending on the implementation, this should only be usable by admin users. Legal log levels (last parameter) are:

  • 0 Trace
  • 1 Debug
  • 2 Info
  • 3 Warn
  • 4 Error
  • 5 Fatal
HTTP
print(get('http://localhost:8082'))
print(http('http://localhost:8082', 'POST', '{"id": 2}', pair('Content-Type', 'application/json')))

As of v0.7.0-beta, Homescript supports the use of generic http functions. The get function only accepts an arbitrary string as a URL and returns the request response as a string.

The http function is generic: given a URL, a request-method, a body, and optional header pairs, a response will be returned as string. The returned response is the response body in its plaintext form. The optional header pairs have to be of the type pair, which can be created using the pair function. It is to be noted that I recommend using a permission which explicitly allows the use of networking inside HMS.

However, the function can fail due to several reasons:

  • Networking issues
  • Response codes which indicate failure (depends on the host implementation)
  • Abundant access permission (permission for the http function)
Exit
exit(42)

Exit stops execution of the running script with a provided exit code. Any non-0 exit code indicates a failure.

Panic
panic("message")

Panic stops execution of the running script with a provided error-message. It is meant to be used as a way to signal the failure of a script using a known reason, for example missing arguments.

Arguments

Call arguments can be used to control the behaviour of a Homescript dynamically. Before accessing the value of an expected argument, it is recommended to validate that this argument has been provided to the Homescript runtime.

Check Arg

For this, the checkArg function can be used. The checkArg function returns a boolean based on whether the argument has been found or not.

if checkArg('identifier') {
    # Do something, for example accessing the argument
}
Get Arg

After validating the existence of an arbitrary argument, it can be accessed using the getArg function. Just like the checkArg function, this requires the identifier of the desired argument. If the argument does not exist, this function will return an error. Due to this, it is recommended to use the checkArg function beforehand.

Warning: this function will always return a string because the argument type must be generic. If the function's return value is required as an integer, it can be parsed using num(getArg('number'))

if checkArg('identifier') {
    print(getArg('identifier'))
}
Provide Args to Homescript call

It is common to call other Homescripts from the current code. Sometimes you may also want to provide arguments to the Homescript to be executed. After the required first parameter homescript_id of the exec function, additional arguments can be used as call arguments for the target Homescript. When providing arguments to a Homescript call, make sure to avoid duplicate key entries in order to avoid errors. The call arguments have to be the pair type, which can be created using the pair function.

exec(
    'homescript_arg',
    pair('key', 'value'),
    pair('another_key', 'another_value'),
)
Type Conversion
Parsing a String to a Number

Sometimes, for example when processing arguments, it is required to parse a string value to a number. For this, the num function should be used. The function requires one argument of type string which will then be used to attempt the type conversion. If the function's input can not be parsed to a number, an error is returned.

print(num('1'))
print(num('-1'))
print(num('+1'))
print(num('0.1'))
# Will return an error
# print(num('NaN'))
Convert any to String

The ability to convert a value of any type to a textual representation or a string is just as useful as the other way around. For this, the str function should be used. The only time the str function can return an error is when used in conjunction with a pseudo-variable (e.g. weather)

print(str(1))
print(str(false))
print(str(switchOn('s2')))
Creating a long string using concatenation

A common problem is to form a formatted string using smaller parts to assemble the final string. In order to solve this problem, Homescript implements the concat function. The function accepts an arbitrary amount of strings and returns a final string, which contains all substrings after another.

print(concat(
	'Hello ',
	"World",
	"!"
))
# "Hello " + "World" + "!" => "Hello World!"
Creating a Pair from Strings

Some functions, such as exec or http require special arguments of the type pair. A pair acts like a map which is limited to one key and a matching value, thus providing a semantic and syntactical indication of grouped value pairs. It can be created using the pair function. Note: the pair type is only able to store strings, both as the key and value.

→ If you need to store other value types, use the according type-conversion functions discussed earlier in this section.

pair('unique_key', 'a string value')
print(pair('k', 'v'))
# <pair(k:v)>
Check if Number is Even

If you want to only run a script when the current week number is even, you can do so by using the even function.

print(even(2))
# true
print(even(1))
# false
print(event(currentWeek))
# true or false

Full example script

A full example program can be found in the demo.hms file

Usage Documentation

Implementing a Custom Executor

Due to Homescript's nature of being extensible, every function call (and some variable getters) need to be implemented by the host software. To get started, an executor has to be implemented. The executor acts like an interface between Homescript and the host software, providing all features of the language.

Structure

The executor's signature has to match following declaration. For a better understanding, visit the executor source code.

package interpreter

type LogLevel uint8

const (
	LevelTrace LogLevel = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
)

type Executor interface {
	CheckArg(identifier string) bool
	GetArg(identifier string) (string, error)

	Sleep(float64)
	Print(args ...string)
	SwitchOn(name string) (bool, error)
	Switch(name string, on bool) error
	Notify(title string, description string, level LogLevel) error
	Log(title string, description string, level LogLevel) error
	Exec(homescriptId string, args map[string]string) (string, error)
	AddUser(username string, password string, forename string, surname string) error
	DelUser(username string) error
	AddPerm(username string, permission string) error
	DelPerm(username string, permission string) error
	Get(url string) (string, error)
	Http(url string, method string, body string, headers map[string]string) (string, error)

	// Builtin variables
	GetUser() string
	GetWeather() (string, error)
	GetTemperature() (int, error)
	GetDate() (int, int, int, int, int, int)
}
Example implementation
type DummyExecutor struct{}

func (self DummyExecutor) Sleep(seconds float64) {
    time.Sleep(time.Millisecond * time.Duration(1000*seconds))
}

func (self DummyExecutor) Print(args ...string) {
    output := ""
    for _, arg := range args {
        output += arg
    }
    fmt.Println(output)
}

// More functions need to be implemented

Using the Run Function

The Run function is used in order to start the execution of a script.

  • The first argument describes the executor's definition (refer to previous)
Example

The sample was taken from ./main.go.

sigTerm := make(chan int)
code, errors := homescript.Run(
    homescript.DummyExecutor{},
    "<demo>",
    "print('hello world!')"
    &sigTerm,
)
for _, err := range errors {
    // Handle the errors this way
    fmt.Println(err.Error())
}
fmt.Printf("Exit code: %d\n", code)

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL