class PlaceOS::Driver

Overview

This is base class for all PlaceOS drivers. It implements a standardised interface by introspecting the driver code you write.

Defined in:

placeos-driver.cr:54
placeos-driver.cr:627
placeos-driver/constants.cr
placeos-driver/interface/camera.cr
placeos-driver/interface/chat_bot.cr
placeos-driver/interface/chat_functions.cr
placeos-driver/interface/desk_control.cr
placeos-driver/interface/door_security.cr
placeos-driver/interface/electrical_relay.cr
placeos-driver/interface/guest_building_access.cr
placeos-driver/interface/lighting.cr
placeos-driver/interface/locatable.cr
placeos-driver/interface/lockers.cr
placeos-driver/interface/mailer.cr
placeos-driver/interface/mailer_templates.cr
placeos-driver/interface/moveable.cr
placeos-driver/interface/muteable.cr
placeos-driver/interface/powerable.cr
placeos-driver/interface/sensor.cr
placeos-driver/interface/sms.cr
placeos-driver/interface/stoppable.cr
placeos-driver/interface/switchable.cr
placeos-driver/interface/zoomable.cr
placeos-driver/logger.cr
placeos-driver/logger_io.cr
placeos-driver/protocol/request.cr
placeos-driver/storage.cr
placeos-driver/subscriptions.cr
placeos-driver/transport/http.cr
placeos-driver/transport/ssh.cr
placeos-driver/utilities/binder.cr
placeos-driver/utilities/discovery.cr
placeos-driver/utilities/rescue_from.cr

Constant Summary

LOG_FORMAT = ENV["PLACE_LOG_FORMAT"]?.presence || "JSON"
LOG_FORMATTER = LOG_FORMAT == "JSON" ? ActionController.json_formatter : ActionController.default_formatter
RESERVED_METHODS = {"initialize" => true, "received" => true, "connected" => true, "disconnected" => true, "on_load" => true, "on_update" => true, "on_unload" => true, "websocket_headers" => true, "before_request" => true, "[]?" => true, "[]" => true, "[]=" => true, "send" => true, "__handle_rescue_from__" => true, "transport" => true, "logger" => true, "queue" => true, "setting" => true, "schedule" => true} of Nil => Nil
VERSION = {{ (`shards version \"/home/runner/work/driver/driver/src/placeos-driver\"`).chomp.stringify.downcase }}

Class Method Summary

Macro Summary

Instance Method Summary

Class Method Detail

def self.default_settings(hash) #

provide example settings for your driver that can be customised in backoffice


[View source]
def self.description(markdown) #

provide a description for your driver that will be diplayed in backoffice.

it supports rendering markdown, so you can provide links to manuals and photos etc


[View source]
def self.descriptive_name(name) #

This is the name of the device you are writing a driver for.

examples such as: Sony VISCA Camera, Samsung MDC Protocol


[View source]
def self.generic_name(name) #

This is the name other drivers and frontends will use to access the driver functionality

exmaples such as: Camera, Display


[View source]
def self.makebreak! #

when using a TCP protocol, we want to close the connection after every request / response


[View source]
def self.register_log_level_signal #

Change the log level at run-time. Toggle TRACE level logging using kill -s USR1 %PID


[View source]
def self.tcp_port(port) #

define a TCP port default if your driver connects over a raw TCP socket


[View source]
def self.udp_port(port) #

define a UDP port default if your driver connects over a raw UDP socket

also use this if you are using multicast for communications


[View source]
def self.uri_base(url) #

the default base URI for a service driver, all HTTP requests will have this domain and path appended

for example: https://api.google.com/


[View source]

Macro Detail

macro accessor(name, implementing = nil) #

Creates helper methods in logic drivers for accessing other drivers in a system

For example, if you want to access system[:Display_1] via display use accessor display : Display_1

for system.all(Camera) use accessor cameras : Array(Camera)

for system.all(Display, implementing: Interface::Powerable) use accessor displays : Array(Display), implementing: Interface::Powerable

for system.implementing(Interface::Powerable) use accessor powerable, implementing: Interface::Powerable


[View source]
macro bind(mod, status, handler = nil) #

a helper for any driver to bind to changes in its own status values logic drivers can additionally bind to status values on remote drivers

local bind: bind :power, :power_changed

remote bind: bind Display_1, :power, :power_changed

the new_value provided in the handler is a JSON string

you would define your handler as protected def power_changed(_subscription, new_value : String)


[View source]
macro rescue_from(error_class, method = nil, &block) #

provides a generic method for handling otherwise unhandled errors in your drivers functions i.e. rescue_from(DivisionByZeroError, :return_zero) or alternatively: rescue_from(DivisionByZeroError) { 0 }


[View source]
macro setting(klass, *types) #

reads the provided class type out of the settings provided at the provided key.

i.e. given a setting: values: [1, 2, 3]

you can extract index 2 number using: setting(Int64, :values, 2)


[View source]
macro setting?(klass, *types) #

reads the provided class type out of the settings provided at the provided key.

i.e. given a setting: keys: {"public": "123456"}

you can extract the public key using: setting?(String, :keys, :public)


[View source]
macro status(klass, key) #

reads a status key and deserialises the value into the class provided.

It raises if the class does not exist.


[View source]
macro status?(klass, key) #

reads a status key and deserialises the value into the class provided.

It returns nil if the key doesn't exist


[View source]

Instance Method Detail

def [](key) : JSON::Any #

returns the current value of a status value and raises if it does not exist


[View source]
def []=(key, value) #

Expose a status key to other drivers and frontends #} allowing them to bind to value updates


[View source]
def []?(key) : JSON::Any | Nil #

returns the current value of a status value and nil if it does not exist


[View source]

the modules database configuration


[View source]
def define_setting(name, value) #

if you would like to save an updated value to settings so it survives restarts


[View source]
def disconnect #

forces a disconnect of the network transport, which will promptly reconnect


[View source]
def invoked_by_user_id #

was the current function executed directly by a user?


[View source]
def logger : PlaceOS::Driver::Log #

[View source]
def module_id : String #

the id of the currently running module


[View source]
def monitor(channel, &callback : Subscriptions::ChannelSubscription, String -> Nil) : Subscriptions::ChannelSubscription #

monitor for messages being published on redis

subscription = monitor("my_service/channel") { |subscription, string_value| ... }

use subscriptions.unsubscribe(subscription) to unsubscribe


[View source]
def publish(channel, message) #

publishes a message to a channel on redis, available to any drivers monitoring for these events

#publish("my_service/channel", "payload contents")


[View source]

[View source]
def queue(**opts, &block : Task -> Nil) #

Queue a task that intends to use the transport layer

primarily useful where a device can only perform a single function at a time and ordering is important

i.e power-on, switch-input, set-volume

however you typically won't need to use this function directly. See #send

opts options include:

  • priority: an Int between 0 and 100 where 0 is highest priority and 100 is the lowest

  • timeout: a Time::Span indicating the maximum time the task should wait for a response

  • retries: how many attempts should be made to successfully complete a task

  • wait: Bool should we wait for a response (defaults to true)

  • name: String of the command, if there is already a command with the same name in the queue, it will be replaced with this.

  • delay: Time::Span how long to wait after executing this command before executing the next

  • clear_queue: Bool after executing task, clear all the remaining tasks in the queue


[View source]

[View source]
def send(message, **opts) #

queues a message to be sent to the transport layer.

see #queue for available options


[View source]
def send(message, **opts, &block : Bytes, PlaceOS::Driver::Task -> Nil) #

queues a message to be sent to the transport layer.

the provided block is used to process responses while this task is active


[View source]
def set_connected_state(online, status_only = true) #

used to provide feedback in backoffice about the state of a driver

online: true == Green, online: false == Red in backoffice

setting status_only: false will set the queue online state.

when offline, this means the queue will ignore all unnamed tasks to avoid memory leaks.


[View source]
def setting : PlaceOS::Driver::Settings #

[View source]
def signal_status(key) #

pushes a change notification for the key specified, even though it hasn't changed


[View source]
def subscribe(status, &callback : Subscriptions::DirectSubscription, String -> Nil) : Subscriptions::DirectSubscription #

Subscribe to a local status value

subscription = subscribe(:my_status) { |subscription, string_value| ... }

use subscriptions.unsubscribe(subscription) to unsubscribe


[View source]

provides access to the modules subscriptions tracker


[View source]
def system(id : String) : Proxy::System #

provides access to the details of a remote system, if you have the ID of the system.


[View source]
def system : Proxy::System #

provides access to the details of the system the logic driver is running in.

NOTE : this only works for logic drivers as other drivers can be in multiple systems.


[View source]
def transport : PlaceOS::Driver::Transport #

[View source]
def wake_device(mac_address, subnet = "255.255.255.255", port = 9) #

sends a wake-on-lan message the specified mac_address, specify a subnet for directed WOL


[View source]