Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1016 lines
29 KiB

Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
Update rpnlib to 0.23.x (#2274) ## [0.23.0] 2020-07-26 ### Added - `p` operator to print the top of the stack via debug function - `&var` syntax to create variable reference in expression - `=` operator for variable assignment in expression - `exists` operator to check for variable existance (only for references) - `deref` operator to convert variable reference into a value (only for references) - Allow to use either float or double as floating type, parse numbers in expressions as specified type - Add boolean type, parse `true` and `false` in expressions - Add null type, parse `null` in expressions - Add string type, parse double-quoted `"string"` in expressions - Add integer and unsigned integer type, used in operators - Allow to configure underlying types from rpnlib\_config.h and -D... flags - Return `rpn_error` from operators, split error types into categories - Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`. ### Changed - Stack structure no longer holds raw `float`, but internal `rpn_value` type - rpn\_... setter and getter methods use `rpn_value` type - Operator functions return `rpn_error` type, allowing to return both value and operator errors - Variables in expressions are no longer required to exist when using `&var` Expression will automatically create the variable, set it to `null` and push it's reference on the stack - It is possible to create 'reference' stack values - Improve precision of `e` and `pi` ### Fixed - Proper value for `e` constant - Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback` with the current `rpn_context` members `error` and `debug_callback` respectively
4 years ago
  1. /*
  2. RPN RULES MODULE
  3. Use RPNLib library (https://github.com/xoseperez/rpnlib)
  4. Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
  5. */
  6. #include "rpnrules.h"
  7. #if RPN_RULES_SUPPORT
  8. #include <rpnlib.h>
  9. #include "light.h"
  10. #include "mqtt.h"
  11. #include "ntp.h"
  12. #include "ntp_timelib.h"
  13. #include "relay.h"
  14. #include "rfbridge.h"
  15. #include "rpc.h"
  16. #include "rtcmem.h"
  17. #include "sensor.h"
  18. #include "terminal.h"
  19. #include "wifi.h"
  20. #include "ws.h"
  21. #include <list>
  22. #include <type_traits>
  23. #include <vector>
  24. // -----------------------------------------------------------------------------
  25. // Custom commands
  26. // -----------------------------------------------------------------------------
  27. rpn_context _rpn_ctxt;
  28. bool _rpn_run = false;
  29. unsigned long _rpn_delay = RPN_DELAY;
  30. unsigned long _rpn_last = 0;
  31. struct RpnRunner {
  32. enum class Policy {
  33. OneShot,
  34. Periodic
  35. };
  36. RpnRunner(Policy policy_, uint32_t period_) :
  37. policy(policy_),
  38. period(period_),
  39. last(millis())
  40. {}
  41. Policy policy { Policy::Periodic };
  42. uint32_t period { 0ul };
  43. uint32_t last { 0ul };
  44. bool expired { false };
  45. };
  46. std::vector<RpnRunner> _rpn_runners;
  47. rpn_operator_error _rpnRunnerHandler(rpn_context & ctxt, RpnRunner::Policy policy, uint32_t time) {
  48. for (auto& runner : _rpn_runners) {
  49. if ((policy == runner.policy) && (time == runner.period)) {
  50. return runner.expired
  51. ? rpn_operator_error::Ok
  52. : rpn_operator_error::CannotContinue;
  53. }
  54. }
  55. _rpn_runners.emplace_back(policy, time);
  56. return rpn_operator_error::CannotContinue;
  57. }
  58. // -----------------------------------------------------------------------------
  59. bool _rpnWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  60. return (strncmp(key, "rpn", 3) == 0);
  61. }
  62. void _rpnWebSocketOnConnected(JsonObject& root) {
  63. root["rpnSticky"] = getSetting("rpnSticky", 1 == RPN_STICKY);
  64. root["rpnDelay"] = getSetting("rpnDelay", RPN_DELAY);
  65. JsonArray& rules = root.createNestedArray("rpnRules");
  66. unsigned char i = 0;
  67. String rule = getSetting({"rpnRule", i});
  68. while (rule.length()) {
  69. rules.add(rule);
  70. rule = getSetting({"rpnRule", ++i});
  71. }
  72. #if MQTT_SUPPORT
  73. i=0;
  74. JsonArray& topics = root.createNestedArray("rpnTopics");
  75. JsonArray& names = root.createNestedArray("rpnNames");
  76. String rpn_topic = getSetting({"rpnTopic", i});
  77. while (rpn_topic.length() > 0) {
  78. String rpn_name = getSetting({"rpnName", i});
  79. topics.add(rpn_topic);
  80. names.add(rpn_name);
  81. rpn_topic = getSetting({"rpnTopic", ++i});
  82. }
  83. #endif
  84. }
  85. #if MQTT_SUPPORT
  86. void _rpnMQTTSubscribe() {
  87. unsigned char i = 0;
  88. String rpn_topic = getSetting({"rpnTopic", i});
  89. while (rpn_topic.length()) {
  90. mqttSubscribeRaw(rpn_topic.c_str());
  91. rpn_topic = getSetting({"rpnTopic", ++i});
  92. }
  93. }
  94. void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  95. if (type == MQTT_CONNECT_EVENT) {
  96. _rpnMQTTSubscribe();
  97. }
  98. if (type == MQTT_MESSAGE_EVENT) {
  99. unsigned char i = 0;
  100. String rpn_topic = getSetting({"rpnTopic", i});
  101. while (rpn_topic.length()) {
  102. if (rpn_topic.equals(topic)) {
  103. String rpn_name = getSetting({"rpnName", i});
  104. if (rpn_name.length()) {
  105. rpn_value value { atof(payload) };
  106. rpn_variable_set(_rpn_ctxt, rpn_name, value);
  107. _rpn_run = true;
  108. break;
  109. }
  110. }
  111. rpn_topic = getSetting({"rpnTopic", ++i});
  112. }
  113. }
  114. }
  115. #endif // MQTT_SUPPORT
  116. void _rpnConfigure() {
  117. #if MQTT_SUPPORT
  118. if (mqttConnected()) {
  119. _rpnMQTTSubscribe();
  120. }
  121. #endif
  122. _rpn_delay = getSetting("rpnDelay", RPN_DELAY);
  123. }
  124. void _rpnRelayStatus(size_t id, bool status) {
  125. char name[32] = {0};
  126. snprintf(name, sizeof(name), "relay%u", id);
  127. rpn_variable_set(_rpn_ctxt, name, rpn_value(std::forward<T>(value)));
  128. _rpn_run = true;
  129. }
  130. void _rpnLightStatus() {
  131. auto channels = lightChannels();
  132. char name[32] = {0};
  133. for (decltype(channels) channel = 0; channel < channels; ++channel) {
  134. snprintf(name, sizeof(name), "channel%u", channel);
  135. rpn_variable_set(_rpn_ctxt, name, rpn_value(lightChannel(channel)));
  136. }
  137. _rpn_run = true;
  138. }
  139. #if NTP_SUPPORT
  140. namespace {
  141. constexpr bool time_t_is_32bit { sizeof(time_t) == 4 };
  142. constexpr bool time_t_is_64bit { sizeof(time_t) == 8 };
  143. static_assert(time_t_is_32bit || time_t_is_64bit, "");
  144. template <typename T>
  145. using split_t = std::integral_constant<bool, sizeof(T) == 8>;
  146. using RpnNtpFunc = rpn_int(*)(time_t);
  147. rpn_error _rpnNtpPopTimestampPair(rpn_context& ctxt, RpnNtpFunc func) {
  148. rpn_value rhs = rpn_stack_pop(ctxt);
  149. rpn_value lhs = rpn_stack_pop(ctxt);
  150. auto timestamp = (static_cast<long long>(lhs.toInt()) << 32ll)
  151. | (static_cast<long long>(rhs.toInt()));
  152. rpn_value value(func(timestamp));
  153. rpn_stack_push(ctxt, value);
  154. return 0;
  155. }
  156. rpn_error _rpnNtpPopTimestampSingle(rpn_context& ctxt, RpnNtpFunc func) {
  157. rpn_value input = rpn_stack_pop(ctxt);
  158. rpn_value result(func(input.toInt()));
  159. rpn_stack_push(ctxt, result);
  160. return 0;
  161. }
  162. void _rpnNtpPushTimestampPair(rpn_context& ctxt, time_t timestamp) {
  163. rpn_value lhs(static_cast<rpn_int>((static_cast<long long>(timestamp) >> 32ll) & 0xffffffffll));
  164. rpn_value rhs(static_cast<rpn_int>(static_cast<long long>(timestamp) & 0xffffffffll));
  165. rpn_stack_push(ctxt, lhs);
  166. rpn_stack_push(ctxt, rhs);
  167. }
  168. void _rpnNtpPushTimestampSingle(rpn_context& ctxt, time_t timestamp) {
  169. rpn_value result(static_cast<rpn_int>(timestamp));
  170. rpn_stack_push(ctxt, result);
  171. }
  172. inline rpn_error _rpnNtpPopTimestamp(const std::true_type&, rpn_context& ctxt, RpnNtpFunc func) {
  173. return _rpnNtpPopTimestampPair(ctxt, func);
  174. }
  175. inline rpn_error _rpnNtpPopTimestamp(const std::false_type&, rpn_context& ctxt, RpnNtpFunc func) {
  176. return _rpnNtpPopTimestampSingle(ctxt, func);
  177. }
  178. rpn_error _rpnNtpPopTimestamp(rpn_context& ctxt, RpnNtpFunc func) {
  179. return _rpnNtpPopTimestamp(split_t<time_t>{}, ctxt, func);
  180. }
  181. inline void _rpnNtpPushTimestamp(const std::true_type&, rpn_context& ctxt, time_t timestamp) {
  182. _rpnNtpPushTimestampPair(ctxt, timestamp);
  183. }
  184. inline void _rpnNtpPushTimestamp(const std::false_type&, rpn_context& ctxt, time_t timestamp) {
  185. _rpnNtpPushTimestampSingle(ctxt, timestamp);
  186. }
  187. void _rpnNtpPushTimestamp(rpn_context& ctxt, time_t timestamp) {
  188. _rpnNtpPushTimestamp(split_t<time_t>{}, ctxt, timestamp);
  189. }
  190. rpn_error _rpnNtpNow(rpn_context & ctxt) {
  191. if (ntpSynced()) {
  192. _rpnNtpPushTimestamp(ctxt, now());
  193. return 0;
  194. }
  195. return rpn_operator_error::CannotContinue;
  196. }
  197. rpn_error _rpnNtpFunc(rpn_context & ctxt, RpnNtpFunc func) {
  198. return _rpnNtpPopTimestamp(ctxt, func);
  199. }
  200. bool _rpn_ntp_tick_minute { false };
  201. bool _rpn_ntp_tick_hour { false };
  202. rpn_error _rpnNtpTickMinute(rpn_context& ctxt) {
  203. if (_rpn_ntp_tick_minute) {
  204. _rpn_ntp_tick_minute = false;
  205. return 0;
  206. }
  207. return rpn_operator_error::CannotContinue;
  208. }
  209. rpn_error _rpnNtpTickHour(rpn_context& ctxt) {
  210. if (_rpn_ntp_tick_hour) {
  211. _rpn_ntp_tick_hour = false;
  212. return 0;
  213. }
  214. return rpn_operator_error::CannotContinue;
  215. }
  216. } // namespace
  217. #endif // NTP_SUPPORT
  218. String _rpnValueToString(const rpn_value& value) {
  219. String out;
  220. if (value.isString()) {
  221. out = value.toString();
  222. } else if (value.isFloat()) {
  223. out = String(value.toFloat(), 10);
  224. } else if (value.isInt()) {
  225. out = String(value.toInt(), 10);
  226. } else if (value.isUint()) {
  227. out = String(value.toUint(), 10);
  228. } else if (value.isBoolean()) {
  229. out = String(value.toBoolean() ? "true" : "false");
  230. } else if (value.isNull()) {
  231. out = F("(null)");
  232. }
  233. return out;
  234. }
  235. char _rpnStackTypeTag(rpn_stack_value::Type type) {
  236. switch (type) {
  237. case rpn_stack_value::Type::None:
  238. return 'N';
  239. case rpn_stack_value::Type::Variable:
  240. return '$';
  241. case rpn_stack_value::Type::Array:
  242. return 'A';
  243. case rpn_stack_value::Type::Value:
  244. default:
  245. return ' ';
  246. }
  247. }
  248. #if RELAY_SUPPORT
  249. rpn_error _rpnRelayStatus(rpn_context & ctxt, bool force) {
  250. rpn_value id;
  251. rpn_value status;
  252. rpn_stack_pop(ctxt, id);
  253. rpn_stack_pop(ctxt, status);
  254. rpn_uint value = status.toUint();
  255. if (value == 2) {
  256. relayToggle(id.toUint());
  257. } else if (relayStatusTarget(id.toUint()) != (value == 1)) {
  258. relayStatus(id.toUint(), value == 1);
  259. }
  260. return 0;
  261. }
  262. #endif // RELAY_SUPPORT
  263. #if RFB_SUPPORT
  264. struct rpn_rfbridge_code {
  265. unsigned char protocol;
  266. String raw;
  267. size_t count;
  268. decltype(millis()) last;
  269. };
  270. // TODO: in theory, we could do with forward_list. however, this would require a more complicated removal process,
  271. // as we would no longer know the previous element and would need to track 2 elements at a time
  272. static std::list<rpn_rfbridge_code> _rfb_codes;
  273. static uint32_t _rfb_code_repeat_window;
  274. static uint32_t _rfb_code_stale_delay;
  275. static uint32_t _rfb_code_match_window;
  276. struct rpn_rfbridge_match {
  277. unsigned char protocol;
  278. String raw;
  279. };
  280. rpn_error _rpnRfbSequence(rpn_context& ctxt) {
  281. auto raw_second = rpn_stack_pop(ctxt);
  282. auto proto_second = rpn_stack_pop(ctxt);
  283. auto raw_first = rpn_stack_pop(ctxt);
  284. auto proto_first = rpn_stack_pop(ctxt);
  285. // find 2 codes in the same order and save pointers
  286. rpn_rfbridge_match match[2] {
  287. {static_cast<unsigned char>(proto_first.toUint()), raw_first.toString()},
  288. {static_cast<unsigned char>(proto_second.toUint()), raw_second.toString()}
  289. };
  290. rpn_rfbridge_code* refs[2] {nullptr, nullptr};
  291. for (auto& recent : _rfb_codes) {
  292. if ((refs[0] != nullptr) && (refs[1] != nullptr)) {
  293. break;
  294. }
  295. for (int index = 0; index < 2; ++index) {
  296. if ((refs[index] == nullptr)
  297. && (match[index].protocol == recent.protocol)
  298. && (match[index].raw == recent.raw)) {
  299. refs[index] = &recent;
  300. }
  301. }
  302. }
  303. if ((refs[0] == nullptr) || (refs[1] == nullptr)) {
  304. return rpn_operator_error::CannotContinue;
  305. }
  306. // purge codes to avoid matching again on the next rules run
  307. if ((millis() - refs[0]->last) > (millis() - refs[1]->last)) {
  308. _rfb_codes.remove_if([&refs](rpn_rfbridge_code& code) {
  309. return (refs[0] == &code) || (refs[1] == &code);
  310. });
  311. return rpn_operator_error::Ok;
  312. }
  313. return rpn_operator_error::CannotContinue;
  314. }
  315. decltype(_rfb_codes)::iterator _rpnRfbFindCode(unsigned char protocol, const String& match) {
  316. return std::find_if(_rfb_codes.begin(), _rfb_codes.end(), [protocol, &match](const rpn_rfbridge_code& code) {
  317. return (code.protocol == protocol) && (code.raw == match);
  318. });
  319. }
  320. rpn_error _rpnRfbSend(rpn_context& ctxt) {
  321. auto code = rpn_stack_pop(ctxt);
  322. if (!code.isString()) {
  323. return rpn_operator_error::InvalidArgument;
  324. }
  325. rfbSend(code.toString());
  326. return rpn_operator_error::Ok;
  327. }
  328. rpn_error _rpnRfbPop(rpn_context& ctxt) {
  329. auto code = rpn_stack_pop(ctxt);
  330. auto proto = rpn_stack_pop(ctxt);
  331. auto result = _rpnRfbFindCode(proto.toUint(), code.toString());
  332. if (result == _rfb_codes.end()) {
  333. return rpn_operator_error::CannotContinue;
  334. }
  335. _rfb_codes.erase(result);
  336. return rpn_operator_error::Ok;
  337. }
  338. rpn_error _rpnRfbInfo(rpn_context& ctxt) {
  339. auto code = rpn_stack_pop(ctxt);
  340. auto proto = rpn_stack_pop(ctxt);
  341. auto result = _rpnRfbFindCode(proto.toUint(), code.toString());
  342. if (result == _rfb_codes.end()) {
  343. return rpn_operator_error::CannotContinue;
  344. }
  345. rpn_stack_push(ctxt, rpn_value(
  346. static_cast<rpn_uint>((*result).count)));
  347. rpn_stack_push(ctxt, rpn_value(
  348. static_cast<rpn_uint>((*result).last)));
  349. return rpn_operator_error::Ok;
  350. }
  351. rpn_error _rpnRfbWaitMatch(rpn_context& ctxt) {
  352. auto code = rpn_stack_pop(ctxt);
  353. auto proto = rpn_stack_pop(ctxt);
  354. auto count = rpn_stack_pop(ctxt);
  355. auto time = rpn_stack_pop(ctxt);
  356. auto result = _rpnRfbFindCode(proto.toUint(), code.toString());
  357. if (result == _rfb_codes.end()) {
  358. return rpn_operator_error::CannotContinue;
  359. }
  360. if ((*result).count < count.toUint()) {
  361. return rpn_operator_error::CannotContinue;
  362. }
  363. // purge code to avoid matching again on the next rules run
  364. if (rpn_operator_error::Ok == _rpnRunnerHandler(ctxt, RpnRunner::Policy::OneShot, time.toUint())) {
  365. _rfb_codes.erase(result);
  366. return rpn_operator_error::Ok;
  367. }
  368. return rpn_operator_error::CannotContinue;
  369. }
  370. rpn_error _rpnRfbMatcher(rpn_context& ctxt) {
  371. auto code = rpn_stack_pop(ctxt);
  372. auto proto = rpn_stack_pop(ctxt);
  373. auto count = rpn_stack_pop(ctxt);
  374. auto result = _rpnRfbFindCode(proto.toUint(), code.toString());
  375. if (result == _rfb_codes.end()) {
  376. return rpn_operator_error::CannotContinue;
  377. }
  378. // only process recent codes, ignore when rule is processing outside of this small window
  379. if (millis() - (*result).last >= _rfb_code_match_window) {
  380. return rpn_operator_error::CannotContinue;
  381. }
  382. // purge code to avoid matching again on the next rules run
  383. if ((*result).count == count.toUint()) {
  384. _rfb_codes.erase(result);
  385. return rpn_operator_error::Ok;
  386. }
  387. return rpn_operator_error::CannotContinue;
  388. }
  389. void _rpnBrokerRfbridgeCallback(unsigned char protocol, const char* raw_code) {
  390. // remove really old codes that we have not seen in a while to avoid memory exhaustion
  391. auto ts = millis();
  392. auto old = std::remove_if(_rfb_codes.begin(), _rfb_codes.end(), [ts](rpn_rfbridge_code& code) {
  393. return (ts - code.last) >= _rfb_code_stale_delay;
  394. });
  395. if (old != _rfb_codes.end()) {
  396. _rfb_codes.erase(old, _rfb_codes.end());
  397. }
  398. auto result = _rpnRfbFindCode(protocol, raw_code);
  399. if (result != _rfb_codes.end()) {
  400. // we also need to reset the counter at a certain point to allow next batch of repeats to go through
  401. if (millis() - (*result).last >= _rfb_code_repeat_window) {
  402. (*result).count = 0;
  403. }
  404. (*result).last = millis();
  405. (*result).count += 1u;
  406. } else {
  407. _rfb_codes.push_back({protocol, raw_code, 1u, millis()});
  408. }
  409. _rpn_run = true;
  410. }
  411. void _rpnRfbSetup() {
  412. // - Repeat window is an arbitrary time, just about 3-4 more times it takes for
  413. // a code to be sent again when holding a generic remote button
  414. // Code counter is reset to 0 when outside of the window.
  415. // - Stale delay allows broker callback to remove really old codes.
  416. // (TODO: can this happen in loop() cb instead?)
  417. _rfb_code_repeat_window = getSetting("rfbRepeatWindow", 2000ul);
  418. _rfb_code_match_window = getSetting("rfbMatchWindow", 2000ul);
  419. _rfb_code_stale_delay = getSetting("rfbStaleDelay", 10000ul);
  420. #if TERMINAL_SUPPORT
  421. terminalRegisterCommand(F("RFB.CODES"), [](const terminal::CommandContext& ctx) {
  422. for (auto& code : _rfb_codes) {
  423. char buffer[128] = {0};
  424. snprintf_P(buffer, sizeof(buffer),
  425. PSTR("proto=%u raw=\"%s\" count=%u last=%u"),
  426. code.protocol,
  427. code.raw.c_str(),
  428. code.count,
  429. code.last
  430. );
  431. ctx.output.println(buffer);
  432. }
  433. });
  434. #endif
  435. // Main bulk of the processing goes on in here
  436. RfbridgeBroker::Register(_rpnBrokerRfbridgeCallback);
  437. }
  438. #endif // RFB_SUPPORT
  439. void _rpnShowStack(Print& print) {
  440. print.println(F("Stack:"));
  441. auto index = rpn_stack_size(_rpn_ctxt);
  442. if (!index) {
  443. print.println(F(" (empty)"));
  444. return;
  445. }
  446. rpn_stack_foreach(_rpn_ctxt, [&index, &print](rpn_stack_value::Type type, const rpn_value& value) {
  447. print.printf("%c %02u: %s\n",
  448. _rpnStackTypeTag(type), index--,
  449. _rpnValueToString(value).c_str()
  450. );
  451. });
  452. }
  453. void _rpnInit() {
  454. // Init context
  455. rpn_init(_rpn_ctxt);
  456. #if NTP_SUPPORT
  457. {
  458. constexpr size_t time_t_argc { split_t<time_t>{} ? 2 : 1 };
  459. rpn_operator_set(_rpn_ctxt, "tick_1h", 0, _rpnNtpTickHour);
  460. rpn_operator_set(_rpn_ctxt, "tick_1m", 0, _rpnNtpTickMinute);
  461. rpn_operator_set(_rpn_ctxt, "utc", 0, _rpnNtpNow);
  462. rpn_operator_set(_rpn_ctxt, "now", 0, _rpnNtpNow);
  463. rpn_operator_set(_rpn_ctxt, "utc_month", time_t_argc, [](rpn_context & ctxt) {
  464. return _rpnNtpFunc(ctxt, utc_month);
  465. });
  466. rpn_operator_set(_rpn_ctxt, "month", time_t_argc, [](rpn_context & ctxt) {
  467. return _rpnNtpFunc(ctxt, month);
  468. });
  469. rpn_operator_set(_rpn_ctxt, "utc_day", time_t_argc, [](rpn_context & ctxt) {
  470. return _rpnNtpFunc(ctxt, utc_day);
  471. });
  472. rpn_operator_set(_rpn_ctxt, "day", time_t_argc, [](rpn_context & ctxt) {
  473. return _rpnNtpFunc(ctxt, day);
  474. });
  475. rpn_operator_set(_rpn_ctxt, "utc_dow", time_t_argc, [](rpn_context & ctxt) {
  476. return _rpnNtpFunc(ctxt, utc_weekday);
  477. });
  478. rpn_operator_set(_rpn_ctxt, "dow", time_t_argc, [](rpn_context & ctxt) {
  479. return _rpnNtpFunc(ctxt, weekday);
  480. });
  481. rpn_operator_set(_rpn_ctxt, "utc_hour", time_t_argc, [](rpn_context & ctxt) {
  482. return _rpnNtpFunc(ctxt, utc_hour);
  483. });
  484. rpn_operator_set(_rpn_ctxt, "hour", time_t_argc, [](rpn_context & ctxt) {
  485. return _rpnNtpFunc(ctxt, hour);
  486. });
  487. rpn_operator_set(_rpn_ctxt, "utc_minute", time_t_argc, [](rpn_context & ctxt) {
  488. return _rpnNtpFunc(ctxt, utc_minute);
  489. });
  490. rpn_operator_set(_rpn_ctxt, "minute", time_t_argc, [](rpn_context & ctxt) {
  491. return _rpnNtpFunc(ctxt, minute);
  492. });
  493. }
  494. #endif
  495. // Accept relay number and numeric API status value (0, 1 and 2)
  496. #if RELAY_SUPPORT
  497. // apply status and reset timers when called
  498. rpn_operator_set(_rpn_ctxt, "relay_reset", 2, [](rpn_context & ctxt) {
  499. return _rpnRelayStatus(ctxt, true);
  500. });
  501. // only update status when target status differs, keep running timers
  502. rpn_operator_set(_rpn_ctxt, "relay", 2, [](rpn_context & ctxt) {
  503. return _rpnRelayStatus(ctxt, false);
  504. });
  505. #endif // RELAY_SUPPORT == 1
  506. // Channel operators
  507. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  508. rpn_operator_set(_rpn_ctxt, "update", 0, [](rpn_context & ctxt) -> rpn_error {
  509. lightUpdate();
  510. return 0;
  511. });
  512. rpn_operator_set(_rpn_ctxt, "black", 0, [](rpn_context & ctxt) -> rpn_error {
  513. lightColor(0ul);
  514. return 0;
  515. });
  516. rpn_operator_set(_rpn_ctxt, "channel", 2, [](rpn_context & ctxt) -> rpn_error {
  517. rpn_value value;
  518. rpn_value id;
  519. rpn_stack_pop(ctxt, id);
  520. rpn_stack_pop(ctxt, value);
  521. lightChannel(id.toUint(), id.toInt());
  522. return 0;
  523. });
  524. #endif
  525. #if RFB_SUPPORT
  526. rpn_operator_set(_rpn_ctxt, "rfb_send", 1, _rpnRfbSend);
  527. rpn_operator_set(_rpn_ctxt, "rfb_pop", 2, _rpnRfbPop);
  528. rpn_operator_set(_rpn_ctxt, "rfb_info", 2, _rpnRfbInfo);
  529. rpn_operator_set(_rpn_ctxt, "rfb_sequence", 4, _rpnRfbSequence);
  530. rpn_operator_set(_rpn_ctxt, "rfb_match", 3, _rpnRfbMatcher);
  531. rpn_operator_set(_rpn_ctxt, "rfb_match_wait", 4, _rpnRfbWaitMatch);
  532. #endif
  533. #if MQTT_SUPPORT
  534. rpn_operator_set(_rpn_ctxt, "mqtt_send", 2, [](rpn_context & ctxt) -> rpn_error {
  535. rpn_value message;
  536. rpn_stack_pop(ctxt, message);
  537. rpn_value topic;
  538. rpn_stack_pop(ctxt, topic);
  539. return mqttSendRaw(topic.toString().c_str(), message.toString().c_str())
  540. ? rpn_operator_error::Ok
  541. : rpn_operator_error::CannotContinue;
  542. });
  543. #endif
  544. // Some debugging. Dump stack contents
  545. #if TERMINAL_SUPPORT
  546. rpn_operator_set(_rpn_ctxt, "showstack", 0, [](rpn_context & ctxt) -> rpn_error {
  547. _rpnShowStack(terminalDefaultStream());
  548. return 0;
  549. });
  550. #endif
  551. // And, simple string logging
  552. #if DEBUG_SUPPORT
  553. rpn_operator_set(_rpn_ctxt, "dbgmsg", 1, [](rpn_context & ctxt) -> rpn_error {
  554. rpn_value message;
  555. rpn_stack_pop(ctxt, message);
  556. DEBUG_MSG_P(PSTR("[RPN] %s\n"), message.toString().c_str());
  557. return 0;
  558. });
  559. #endif
  560. rpn_operator_set(_rpn_ctxt, "mem?", 0, [](rpn_context & ctxt) -> rpn_error {
  561. rpn_stack_push(ctxt, rpn_value(rtcmemStatus()));
  562. return 0;
  563. });
  564. rpn_operator_set(_rpn_ctxt, "mem_write", 2, [](rpn_context & ctxt) -> rpn_error {
  565. auto addr = rpn_stack_pop(ctxt).toUint();
  566. auto value = rpn_stack_pop(ctxt).toUint();
  567. if (addr < RTCMEM_BLOCKS) {
  568. auto* rtcmem = reinterpret_cast<volatile uint32_t*>(RTCMEM_ADDR);
  569. *(rtcmem + addr) = value;
  570. return 0;
  571. }
  572. return rpn_operator_error::InvalidArgument;
  573. });
  574. rpn_operator_set(_rpn_ctxt, "mem_read", 1, [](rpn_context & ctxt) -> rpn_error {
  575. auto addr = rpn_stack_pop(ctxt).toUint();
  576. if (addr < RTCMEM_BLOCKS) {
  577. auto* rtcmem = reinterpret_cast<volatile uint32_t*>(RTCMEM_ADDR);
  578. rpn_uint result = *(rtcmem + addr);
  579. rpn_stack_push(ctxt, rpn_value(result));
  580. return 0;
  581. }
  582. return rpn_operator_error::InvalidArgument;
  583. });
  584. rpn_operator_set(_rpn_ctxt, "sleep", 2, [](rpn_context & ctxt) -> rpn_error {
  585. static bool once { false };
  586. if (once) return rpn_operator_error::CannotContinue;
  587. auto value = rpn_stack_pop(ctxt).checkedToUint();
  588. if (!value.ok()) {
  589. return value.error();
  590. }
  591. uint64_t duration = value.value();
  592. if (!duration) {
  593. return rpn_operator_error::CannotContinue;
  594. }
  595. auto mode = rpn_stack_pop(ctxt).toUint();
  596. once = true;
  597. schedule_function([duration, mode]() {
  598. wifiTurnOff();
  599. ESP.deepSleep(duration * 1000000ull, static_cast<RFMode>(mode));
  600. });
  601. return 0;
  602. });
  603. rpn_operator_set(_rpn_ctxt, "stations", 0, [](rpn_context & ctxt) -> rpn_error {
  604. if (!(WiFi.getMode() & WIFI_AP)) return rpn_operator_error::CannotContinue;
  605. rpn_stack_push(ctxt, rpn_value(static_cast<rpn_uint>(WiFi.softAPgetStationNum())));
  606. return 0;
  607. });
  608. rpn_operator_set(_rpn_ctxt, "disconnect", 0, [](rpn_context & ctxt) -> rpn_error {
  609. wifiDisconnect();
  610. yield();
  611. return 0;
  612. });
  613. rpn_operator_set(_rpn_ctxt, "rssi", 0, [](rpn_context & ctxt) -> rpn_error {
  614. if (!wifiConnected()) return rpn_operator_error::CannotContinue;
  615. rpn_stack_push(ctxt, rpn_value(static_cast<rpn_int>(WiFi.RSSI())));
  616. return 0;
  617. });
  618. rpn_operator_set(_rpn_ctxt, "delay", 1, [](rpn_context & ctxt) -> rpn_error {
  619. auto ms = rpn_stack_pop(ctxt);
  620. delay(ms.toUint());
  621. return 0;
  622. });
  623. rpn_operator_set(_rpn_ctxt, "yield", 0, [](rpn_context & ctxt) -> rpn_error {
  624. yield();
  625. return 0;
  626. });
  627. rpn_operator_set(_rpn_ctxt, "reset", 0, [](rpn_context & ctxt) -> rpn_error {
  628. static bool once = ([]() {
  629. deferredReset(100, CustomResetReason::Rule);
  630. return true;
  631. })();
  632. return once
  633. ? rpn_operator_error::CannotContinue
  634. : rpn_operator_error::Ok;
  635. });
  636. rpn_operator_set(_rpn_ctxt, "millis", 0, [](rpn_context & ctxt) -> rpn_error {
  637. rpn_stack_push(ctxt, rpn_value(static_cast<uint32_t>(millis())));
  638. return 0;
  639. });
  640. rpn_operator_set(_rpn_ctxt, "oneshot_ms", 1, [](rpn_context & ctxt) -> rpn_error {
  641. auto every = rpn_stack_pop(ctxt);
  642. return _rpnRunnerHandler(ctxt, RpnRunner::Policy::OneShot, every.toUint());
  643. });
  644. rpn_operator_set(_rpn_ctxt, "every_ms", 1, [](rpn_context & ctxt) -> rpn_error {
  645. auto every = rpn_stack_pop(ctxt);
  646. return _rpnRunnerHandler(ctxt, RpnRunner::Policy::Periodic, every.toUint());
  647. });
  648. // XXX: workaround for the vector 2x growth on push. will need to fix this in the rpnlib
  649. _rpn_ctxt.operators.shrink_to_fit();
  650. DEBUG_MSG_P(PSTR("[RPN] Registered %u operators\n"), _rpn_ctxt.operators.size());
  651. }
  652. #if TERMINAL_SUPPORT
  653. void _rpnInitCommands() {
  654. terminalRegisterCommand(F("RPN.RUNNERS"), [](const terminal::CommandContext& ctx) {
  655. if (!_rpn_runners.size()) {
  656. terminalError(ctx, F("No active runners"));
  657. return;
  658. }
  659. for (auto& runner : _rpn_runners) {
  660. char buffer[128] = {0};
  661. snprintf_P(buffer, sizeof(buffer), PSTR("%p %s %u ms, last %u ms"),
  662. &runner, (RpnRunner::Policy::Periodic == runner.policy) ? "every" : "one-shot",
  663. runner.period, runner.last
  664. );
  665. ctx.output.println(buffer);
  666. }
  667. terminalOK(ctx);
  668. });
  669. terminalRegisterCommand(F("RPN.VARS"), [](const terminal::CommandContext& ctx) {
  670. rpn_variables_foreach(_rpn_ctxt, [&ctx](const String& name, const rpn_value& value) {
  671. char buffer[256] = {0};
  672. snprintf_P(buffer, sizeof(buffer), PSTR(" %s: %s"), name.c_str(), _rpnValueToString(value).c_str());
  673. ctx.output.println(buffer);
  674. });
  675. terminalOK(ctx);
  676. });
  677. terminalRegisterCommand(F("RPN.OPS"), [](const terminal::CommandContext& ctx) {
  678. rpn_operators_foreach(_rpn_ctxt, [&ctx](const String& name, size_t argc, rpn_operator::callback_type) {
  679. char buffer[128] = {0};
  680. snprintf_P(buffer, sizeof(buffer), PSTR(" %s (%d)"), name.c_str(), argc);
  681. ctx.output.println(buffer);
  682. });
  683. terminalOK(ctx);
  684. });
  685. terminalRegisterCommand(F("RPN.TEST"), [](const terminal::CommandContext& ctx) {
  686. if (ctx.argc != 2) {
  687. terminalError(F("Wrong arguments"));
  688. return;
  689. }
  690. ctx.output.print(F("Running RPN expression: "));
  691. ctx.output.println(ctx.argv[1].c_str());
  692. if (!rpn_process(_rpn_ctxt, ctx.argv[1].c_str())) {
  693. rpn_stack_clear(_rpn_ctxt);
  694. char buffer[64] = {0};
  695. snprintf_P(buffer, sizeof(buffer), PSTR("position=%u category=%d code=%d"),
  696. _rpn_ctxt.error.position, static_cast<int>(_rpn_ctxt.error.category), _rpn_ctxt.error.code);
  697. terminalError(ctx, buffer);
  698. return;
  699. }
  700. _rpnShowStack(ctx.output);
  701. rpn_stack_clear(_rpn_ctxt);
  702. terminalOK(ctx);
  703. });
  704. }
  705. #endif
  706. // enables us to use rules without any events firing
  707. // notice: requires rpnRun to trigger at least once so that we can install runners
  708. void _rpnRunnersCheck() {
  709. auto ts = millis();
  710. for (auto& runner : _rpn_runners) {
  711. if (ts - runner.last >= runner.period) {
  712. runner.expired = true;
  713. runner.last = ts;
  714. _rpn_run = true;
  715. }
  716. }
  717. }
  718. void _rpnRunnersReset() {
  719. auto old = std::remove_if(_rpn_runners.begin(), _rpn_runners.end(), [](RpnRunner& runner) {
  720. return (RpnRunner::Policy::OneShot == runner.policy) && runner.expired;
  721. });
  722. if (old != _rpn_runners.end()) {
  723. _rpn_runners.erase(old, _rpn_runners.end());
  724. }
  725. for (auto& runner : _rpn_runners) {
  726. runner.expired = false;
  727. }
  728. }
  729. void _rpnRun() {
  730. if (!_rpn_run) {
  731. return;
  732. }
  733. if (millis() - _rpn_last <= _rpn_delay) {
  734. return;
  735. }
  736. _rpn_last = millis();
  737. _rpn_run = false;
  738. String rule;
  739. unsigned char i = 0;
  740. while ((rule = getSetting({"rpnRule", i++})).length()) {
  741. rpn_process(_rpn_ctxt, rule.c_str());
  742. rpn_stack_clear(_rpn_ctxt);
  743. }
  744. if (!getSetting("rpnSticky", 1 == RPN_STICKY)) {
  745. rpn_variables_clear(_rpn_ctxt);
  746. }
  747. }
  748. void _rpnLoop() {
  749. _rpnRunnersCheck();
  750. _rpnRun();
  751. _rpnRunnersReset();
  752. }
  753. void rpnSetup() {
  754. // Init context
  755. _rpnInit();
  756. // Load & cache settings
  757. _rpnConfigure();
  758. // Terminal commands
  759. #if TERMINAL_SUPPORT
  760. _rpnInitCommands();
  761. #endif
  762. // Websockets
  763. #if WEB_SUPPORT
  764. wsRegister()
  765. .onVisible([](JsonObject& root) { root["rpnVisible"] = 1; })
  766. .onConnected(_rpnWebSocketOnConnected)
  767. .onKeyCheck(_rpnWebSocketOnKeyCheck);
  768. #endif
  769. // MQTT
  770. #if MQTT_SUPPORT
  771. mqttRegister(_rpnMQTTCallback);
  772. #endif
  773. #if NTP_SUPPORT
  774. ntpOnTick([](NtpTick tick) {
  775. switch (tick) {
  776. case NtpTick::EveryMinute:
  777. _rpn_ntp_tick_minute = true;
  778. break;
  779. case NtpTick::EveryHour:
  780. _rpn_ntp_tick_hour = true;
  781. break;
  782. }
  783. _rpn_run = true;
  784. });
  785. #endif
  786. #if RELAY_SUPPORT
  787. relaySetStatusChange(_rpnRelayStatus);
  788. #endif
  789. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  790. lightSetReportListener(_rpnLightStatus);
  791. #endif
  792. #if RFB_SUPPORT
  793. _rpnRfbSetup();
  794. #endif
  795. #if SENSOR_SUPPORT
  796. SensorReadBroker::Register(_rpnBrokerCallback);
  797. #endif
  798. espurnaRegisterReload(_rpnConfigure);
  799. espurnaRegisterLoop(_rpnLoop);
  800. _rpn_last = millis();
  801. _rpn_run = true;
  802. }
  803. #endif // RPN_RULES_SUPPORT