Skip to content

custom device _system

Marco Sillano edited this page Apr 24, 2023 · 96 revisions

SW devices: _core, _trigger, _system

Since ver 2.2.0 'core' and 'trigger' are devices, with few DPs: this increases independence and test capabilities.

  • _name GET only, returns global.instance_name
  • _DBase PUSH and GET, true if all DBs connected
  • _heartbeat periodical timestamp, decoded as 'HH:MM;SS' (for easy test). PUSH and GET.
  • _info SET(any) fills a structure with the enabled flows and all connected devices. GET returns last value.

CORE tips and tricks

Simple cron
It is possible to use the core/_heartbeat as simple cron, adding one or more 'share' in global.alldevices. Template:

             "share": [ {
                            "test": [                   // every day at 6:00 +1..30 s
                                "(msg.info.value > '06:00:00' && msg.info.value < '06:00:31')"
                            ],
                            "action": [
                                 {			// here the required task + value
				 "device": ...,
				 "property": ...,
				 "value": ...
                                 } 
			    ]  
                      } ]
  • Test to limit the days of the week (0=sun): " ... && (new Date().getDay() in [0,1, 6])",
  • Test to limit mounth (0=jan): " ... && (new Date().getMonth() in [0,1, 11])",

I use some like this to start a 'stored procedure' in MySQL server, 'do_purge', that deletes records older than 10 days from the DB 'message table'.

TRIGGER extra properties:

SW custom device: _system

With version 2.0 the '_system' device has undergone refactoring and is now a device, in a separate flow. The goal of this SW-only custom device is to group some useful extra functionalities. Here is an overview of the _system features, for details see the standard documentation.

_laststart

This _system property (and the related start_DEAMON node) is SET only once, some seconds after a node-red restart. Used to do some delayed startup operations on custom devices or to check devices' status.

_proxy

_This system property implements the message exchange between tuyaDAEMON instances on lan. It uses the REST interface to connect a remote tuyaDAEMON server. A map of the URLs for the existing tuyaDAEMON instances is in CORE, in the config 'global' node, named 'remotemap'.

The user doesn't use directly this property, enough to extend the tuyaDAEMON standard messages so:

       {
          "remote": "ANDROID",
          "device": "switch module #1",
          "property": "switch",
          "value": "OFF"
       }

tuyastatus

The global.tuyastatus variable is the storage of all last values got from devices, structured in device.property.value, plus some pseudoDP (_t, _connected). The tuyastatus is automatically updated with any GET and SET. Some _system functions allow more control over it.

  • _tuyastatus(std_cmd) Alternative and indirect access to the global.tuyastatus structure, regardless of dp capabilities. We can get:

    • the list of devices of an instance (if std_cmd is empty {})
    • the SCHEMA for a device, if std_cmd.property is undefined
    • the last value of a property: GET, if std_cmd.value is undefined
    • an unconditioned SET (take care: potentially dangerous) if the std_cmd is full.
  • _doSCHEMA(device) Does a SCHEMA simulation for any device (real, virtual, fake) sending ALL GETs available (from alldevices definitions), and update tuyastatus. Returns a list of all DPs tested + the output for any GET.

  • _doUPDATE(NULL|real|virtual|fake) For all devices connected, does a 'doSCHEMA' command. That can fully update tuyastatus, e.g. after a startup. Returns a list of devices. This can be a good check for 'alldevices' definitions: it must run without 'warning messages.

  • _toGlobal(variable, value) To GET (if value === 'NULL') the global.variable, or SET a global.variable, e.g. in remote. Take care: SET is potentially dangerous. Returns {variable, value}.

_exec

Runs a system command and returns its output.

DB functions

Any used DB can be controlled via _system, for DB maintenance and DB data access:

  • _sqlDBlocal(SQL) allows to send a sql-string to a DB and get the answer
    • more _sqlDBxxxx properties can be user-defined for all DB used. See core.

Timer functions

The timer functions are built around the 'node-red-contrib-jsontimer'. This implementation allows to define a temporized start for any task (local or remote tuyaDAEMON standard command) using a standard command or a share. The timer itself is simple: it has only one function, to send a stored message at a specific time. The time can be defined in three ways: timeout (in ms), time (as 22:13:36.010), and datetime (unixtimestamp).

The timer doesn't offer any kind of repetition. But it can handle many timers, identified by an 'id'. The running timers are maintained with the 'Deploy' and 'Restart Flows' operations.

  • _timerON ((id,) timeout:xx|time:yy|datetime:zz, alarmPayload:{<std_cmd>|<share>}) sets a new timer.
    • alarmPayload can be a standard command (more simple) or a share (more powerful).
    • id is mandatory only if you use _timerOFF else id is auto.
  • _timerOFF(id) deletes an existing timer selected by id, mandatory.
  • _timerList() gets the list of all active timers.

A timer can be used also inside a standard share, to have a delay effect on a task triggered by an event, see _system._beep_loop implementation in global.alldevices.

Use of _timerON: this command sets a timer to send a TRIGGER5000 to Tuya-cloud, 100 seconds later.

    {
          "device": "_system",
          "property": "_timerON",
          "value": {
               "timeout": 100000,        // or time|datetime
               "alarmPayload": {         // the delayed command                
                      "device": "_system",
                      "property": "_doTrigger",
                      "value": 5000
               }
         }
   }

benchmark

Benchmarks can be done by repeatedly running a task in freerunning and counting the number of runs performed by tuyaDAEMON in a given time.

Some _system properties simplify the benchmarks:

  • _zeroTask: the fastest SW-only task, does nothing, and it is without any logging (no debug pad, no DB).
  • _zeroLog: like _zeroTask, but with logging (debug pad and DB) and SET returns the coded 'value'.
  • _benchmark: to SET/GET the task (default _zeroTask) and the benchmark duration (default 10000 ms).
  • _doBenchmark: SET: trigger to execute a benchmark.

To do a benchmark, choose the task (device and property), then:

  1. To retrigger in free-run the chosen task, add to global.alldevice.<device>.<property> the share:
                    "share": [{
                            "action": [{
                                    "device": "_system",
                                    "property": "_benchmark_step"
                                }]}]

note: the _benchmark_step does is retrigger action only inside a running benchmark, so this share can be permanent (see global.alldevices._system._zeroTask and global.alldevices._system._zeroLog), or you can delete it after the benchmark.

  1. Use the _benchmark property to SET the task:
                  "payload":{
                            "device": "_system",
                            "property": "_benchmark",
                            "value" {
                                    "device":"_system",              // the chosen task GET _system._zeroTask
                                    "property":"_zeroTask",
                                    "value": if_required,            // to do GET/SET test
                                    "timeout": 20000                 // optional, default 10000 ms
                                }}
  1. Use _doBenchmark (SET: trigger) to start the benchmark.
  • In my old test server (i7-4777, 3.40 GHz, Win 10) and tuyaDAEMON 2.0 I get 15300/20s for _zeroTask, 11700/20s for _zeroLog, and a good 480/20s for a GET from the tuya_bridge real device.
  • Using ver. tuyaDAEMON 2.2.0 (new server i9-11900, 2.50 GHz, win 11) zeroTask more than 2000 run/s and zeroLog more than 1400 run/s.
  • On ANDROID 11 (H96 Max-V11, CPU RK3319 1.5 GHz 4core) I get 448/10s (zeroTask) and 309/10s (zeroLog).

Using MQTTExplorer, the benchmark results are visible in a chart (In the example zeroTask, min:12148/5s, max:12595/5s):

Nevertheless, I impose the very conservative limit of 30/10s to MQTT messages for all real devices, to keep low the Tuya-cloud stress.

note on _zeroLog SET _zeroLog(value) returns the user value encoded (see alldevicesnote[8]) using the default or _zeroLog.type or _zeroLog.typefield rules: this allows to do encoding tests changing the _zeroLog definitions in alldevices.

about tests _Also _doUPDATE, with all trace enabled, is a good stress test: if you get any warning, fine-tune global.alldevices.

User interface

Some _system features are about the UI:

  • _toDebug(string|obj) The string|obj is printed on the debug pad by a node.debug() call. To share results and info between tuyaDAEMON servers.
  • _toWarn(string|obj) The string|obj is printed on the debug pad by a node.warn() call. To signals problems or share alarms between tuyaDAEMON servers.
  • _play(WAV|string) Play a sound (WAV) or does text-to-speech function (browser dependent: set language on interface)
  • _beep
    Plays the short beep stored in globals.beep64 as WAV code64.
  • _beep_loop(count, interval) Example of tuyaDAEMON-chain: repeats the 'beep' count times, with an interval in ms.

security and recovery

A group of properties is present to help the user to build robust and fault-tolerant applications. This is one strong motivation for the tuyaDAEMON project.

Random or malicious events can cause serious problems. Here are some accident scenarios:

  1. _DBase and DB_ALARM node control the MySQL connections (note: this is placed in the CORE flow).
  2. loss of remote connection: tuya-cloud is out. Also the user control from the remote. Only tuyaDAEMON works. _LANnet (and the related LAN_ALARM node) are triggered by this event.
  3. local WiFi down: all devices are offline. Only the cabled LAN and devices (a siren?) can work. _WiFinet, WiFi_ALARM node, and _WiFiunconnected (a list of unconnected devices, including 'disabled' devices) can handle this event.
  4. AC power off: blackout or external action? Only UPS devices are available. Good to provide for the WiFi router, one tuyaDEAMON server, the siren, and also the IP cams... _ACpower, AC_ALARM node and _ACunconnected are triggered by this event.

It is now common for apartment thieves to interrupt the AC power and to use a jammer to disrupt communications. If you build your own security alarm think carefully to choose a good solution.

helper functions

These are some low-level properties that offer access to CORE functions via messages. This way allows meta-Programmation and can be useful for applications that use REST or for remote control:

  1. _doTrigger(number) Offers an alternative way to send TRIGGERs to tuya-cloud. Useful used in 'share' structures (see timer example).
  2. _toLowIN(toDev, dev-msg) The dev-msg is sent directly to the tuya-smart-device node, selected by toDev (see 'core.low_level_IN' node). To test new device functions.
  3. _toFastIN(std_msg) The standard message is sent to the 'core.fast_cmds' node to be executed.
  4. _toStdCmd(std_msg) The standard message is sent to the 'core.std_cmd' node to be executed.
  5. _toShare(share) The share structure is sent to the 'core.share_IN' node to be processed.
  6. _toLogging(out_msg) The out_msg (see 'core.logging' node) is processed like any device event.

SYSTEM tips and tricks

share examples

Some shares are used to implement SYSTEM capabilities, You can see in global.alldevices the implementation of _beep_loop and the definitions of _zeroTask and _zeroLog used by _doBenchmarks (a mix of share and internal tasks: _benchmark_step and _benchmark_end).

user libraries

This _system v 2.0 is now a collection of heterogeneous and basic utilities. It can be extended to meet the user's application needs. Better, it can be split into many SW-only devices, by application fields, to become specialized user "task libraries".

Clone this wiki locally