commit bf688bf7f803581dfe1d41ec190ecba52aad6444 Author: Arnie Date: Mon Nov 3 22:13:37 2025 +0100 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94fad8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/log/ +/intermediate/ +/node_modules/ +/output/ +/environment.json + +.direnv +.devenv +.vscode +.envrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..64f9127 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +@annex4:registry=https://npm.pkg.github.com +@c3c:registry=https://git.c3c.cz/api/packages/c3c/npm/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7010887 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Control4 KNX Lights + +Control4 driver managing the lights system + + +Notes: + + +Audio + - any manual action = override ON + +Lights + - any manual action = override ON + +Presence detected + - turn on lights + +Presence detected +10s + - turn on audio + +Presence detected +3 min + - Ventilation intake OFF + - Ventilation out 50% + + +Presence ended + - turn off lights if not overridden + - turn off audio if not overridden + +Presence ended + 2 minutes + - turn off lights + - if humidity > 80%, Ventilation intake normal, Ventilation out 100% + - watch humidity on interval, when humidity < 80%, ventilation normal + +Presence ended + 5 minutes + - turn off audio if playing +F diff --git a/components/actions.c4c b/components/actions.c4c new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/components/actions.c4c @@ -0,0 +1 @@ +[] diff --git a/components/commands.c4c b/components/commands.c4c new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/components/commands.c4c @@ -0,0 +1 @@ +[] diff --git a/components/connections.c4c b/components/connections.c4c new file mode 100644 index 0000000..bbcdb29 --- /dev/null +++ b/components/connections.c4c @@ -0,0 +1,16 @@ +[ + { + "id": 1, + "connectionname": "KNX Control", + "type": 1, + "consumer": true, + "classes": [ + { + "classname": "KNX_DEVICE", + "autobind": false + } + ], + "linelevel": true, + "facing": 6 + } +] diff --git a/components/events.c4c b/components/events.c4c new file mode 100644 index 0000000..2b71cb1 --- /dev/null +++ b/components/events.c4c @@ -0,0 +1,12 @@ +[ + { + "id": 100, + "description": "Start Media in Upper Floor Bathroom", + "name": "UpperFloorBathroomMediaStart" + }, + { + "id": 101, + "description": "End Media in Upper Floor Bathroom", + "name": "UpperFloorBathroomMediaEnd" + } +] diff --git a/components/navdisplayoptions.c4c b/components/navdisplayoptions.c4c new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/components/navdisplayoptions.c4c @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/components/properties.c4c b/components/properties.c4c new file mode 100644 index 0000000..1f11c23 --- /dev/null +++ b/components/properties.c4c @@ -0,0 +1,12 @@ +[ + { + "name": "Log Level", + "type": "DYNAMIC_LIST", + "readonly": false + }, + { + "name": "Log Mode", + "type": "DYNAMIC_LIST", + "readonly": false + } +] diff --git a/components/proxies.c4c b/components/proxies.c4c new file mode 100644 index 0000000..e60129f --- /dev/null +++ b/components/proxies.c4c @@ -0,0 +1,7 @@ +[ + { + "id": 5000, + "proxy": "c3c-knx-presence", + "name": "KNX Presence" + } +] diff --git a/components/ui.c4c b/components/ui.c4c new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/components/ui.c4c @@ -0,0 +1 @@ +[] diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7097d60 --- /dev/null +++ b/flake.lock @@ -0,0 +1,276 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": [ + "nix", + "devenv" + ], + "flake-compat": [ + "nix", + "devenv" + ], + "git-hooks": [ + "nix", + "devenv" + ], + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1742042642, + "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", + "owner": "cachix", + "repo": "cachix", + "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "latest", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", + "nix": "nix_2", + "nixpkgs": [ + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1744876578, + "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=", + "owner": "cachix", + "repo": "devenv", + "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nix", + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "nix", + "devenv" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "nix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742649964, + "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "nix", + "devenv", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "nix": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1744882253, + "narHash": "sha256-SHedYfKXqJ0BuB0XDaIbWS0umcqj9ScYTrrWYN8HmMo=", + "ref": "refs/heads/main", + "rev": "49a4dc963156c87f2c13d0ef3db0d8d244bdaa2d", + "revCount": 14, + "type": "git", + "url": "ssh://git@git.c3c.cz/C3C/nix" + }, + "original": { + "type": "git", + "url": "ssh://git@git.c3c.cz/C3C/nix" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "nix", + "devenv" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": [ + "nix", + "devenv" + ], + "nixpkgs-regression": [ + "nix", + "devenv" + ], + "pre-commit-hooks": [ + "nix", + "devenv" + ] + }, + "locked": { + "lastModified": 1741798497, + "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nix": "nix" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0267978 --- /dev/null +++ b/flake.nix @@ -0,0 +1,84 @@ +{ + inputs = { + nix.url = "git+ssh://git@git.c3c.cz/C3C/nix"; + }; + + outputs = + { self, nix }: + { + formatter = nix.formatter; + + packages = nix.lib.forAllSystems (pkgs: { + devenv-up = self.devShells.${pkgs.system}.default.config.procfileScript; + devenv-test = self.devShells.${pkgs.system}.default.config.test; + }); + + devShells = nix.lib.forAllSystems (pkgs: { + default = nix.lib.mkDevenvShell { + inherit pkgs; + + inputs = { + self = self; + nixpkgs = pkgs; + }; + + modules = [ + { + packages = [ + nix.lib.control4-env.${pkgs.system} + ]; + + scripts = { + menu = { + description = "Print this menu"; + exec = '' + echo "Commands:" + echo -n '${ + builtins.toJSON ( + builtins.mapAttrs (s: value: value.description) self.devShells.${pkgs.system}.default.config.scripts + ) + }' | \ + ${pkgs.jq}/bin/jq -r 'to_entries | map(" \(.key)\n" + " - \(if .value == "" then "no description provided" else .value end)") | "" + .[]' + ''; + }; + + generate = { + exec = '' + ${nix.lib.cd_root} + + rm -rf ./src/control4-utils + cp -rp node_modules/@c3c/control4-utils/src ./src/control4-utils + node_modules/@c3c/control4-utils/bin/driver-manager ./package.json + mdtohtml -page README.md > src/www/documentation.html + ''; + }; + + github-push = { + description = "Push current branch and all tags to github, lazy workaround for automated mirror. TODO: Automate"; + exec = '' + b=$(git rev-parse --abbrev-ref HEAD) + git push git@github.com:Sharsie/control4-knx-presence.git "$b":"$b" + git push git@github.com:Sharsie/control4-knx-presence.git --tags + ''; + }; + + lint = { + exec = '' + ${nix.lib.cd_root} + ''; + }; + + fix = { + exec = '' + ${nix.lib.cd_root} + nix fmt ./*.nix + stylua ./src + ''; + }; + }; + } + ]; + }; + }); + }; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8349026 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,64 @@ +{ + "name": "c3c-knx-presence", + "version": "0.0.223", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "c3c-knx-presence", + "version": "0.0.223", + "hasInstallScript": true, + "license": "GPL-3.0-only", + "devDependencies": { + "@c3c/control4-utils": "^0.5.2", + "@prettier/plugin-lua": "0.0.2", + "prettier": "^3" + } + }, + "node_modules/@c3c/control4-utils": { + "version": "0.5.2", + "resolved": "https://git.c3c.cz/api/packages/C3C/npm/%40c3c%2Fcontrol4-utils/-/0.5.2/control4-utils-0.5.2.tgz", + "integrity": "sha512-aKbyjB7SG8mZxYhuBnqngWA1RaFL+3TsH+CDtkOzb4VGZ9VfLk/3a4uvREuClSJMqC6EdrG1t9gz5DTwL2h1Uw==", + "dev": true, + "license": "GPL-3.0-only" + }, + "node_modules/@prettier/plugin-lua": { + "version": "0.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "luaparse": "0.2.1" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "prettier": ">=2.0.0" + } + }, + "node_modules/luaparse": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "bin": { + "luaparse": "bin/luaparse" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..81fd0a4 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "author": "C3C", + "control4": { + "capabilities": {}, + "control": "lua_gen", + "controlmethod": "KNX", + "created": "5/14/2023, 10:16:49 PM", + "icon": { + "image_source": "c4z", + "large": "icons/logo_32.png", + "small": "icons/logo_16.png" + }, + "manufacturer": "C3C", + "model": "Presence", + "name": "KNX Presence" + }, + "description": "", + "devDependencies": { + "@c3c/control4-utils": "^0.5.2", + "@prettier/plugin-lua": "0.0.2", + "prettier": "^3" + }, + "homepage": "https://git.c3c.cz/C3C/control4-knx-presence", + "keywords": [], + "license": "GPL-3.0-only", + "main": "src/driver.lua", + "name": "c3c-knx-presence", + "repository": { + "type": "git", + "url": "https://git.c3c.cz/C3C/control4-knx-presence.git" + }, + "scripts": { + "driver-manager": "node_modules/@c3c/control4-utils/bin/driver-manager ./package.json", + "install-control4-utils": "rm -rf ./src/control4-utils && cp -rp node_modules/@c3c/control4-utils/src ./src/control4-utils", + "postinstall": "npm run install-control4-utils && npm run driver-manager", + "test": "busted tests/test.lua" + }, + "version": "0.0.223" +} diff --git a/src/control4-utils/base.lua b/src/control4-utils/base.lua new file mode 100644 index 0000000..221e77f --- /dev/null +++ b/src/control4-utils/base.lua @@ -0,0 +1 @@ +C3C = {} diff --git a/src/control4-utils/helpers.lua b/src/control4-utils/helpers.lua new file mode 100644 index 0000000..23463be --- /dev/null +++ b/src/control4-utils/helpers.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "helpers.all") diff --git a/src/control4-utils/helpers/all.lua b/src/control4-utils/helpers/all.lua new file mode 100644 index 0000000..2f1397f --- /dev/null +++ b/src/control4-utils/helpers/all.lua @@ -0,0 +1,6 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "context.all") +require(dirRequire .. "logging.all") +require(dirRequire .. "strings.all") +require(dirRequire .. "timers.all") diff --git a/src/control4-utils/helpers/context/Context.lua b/src/control4-utils/helpers/context/Context.lua new file mode 100644 index 0000000..7e5adbe --- /dev/null +++ b/src/control4-utils/helpers/context/Context.lua @@ -0,0 +1,36 @@ +---@alias C3CContext table +do + if not C3C then + print("Control4Utils: ERROR LOADING src.helpers.context.Context, src.base must be required first") + return + end + + C3C.Context = { + ---@param parentCtx C3CContext + ---@param stack string? + ---@return C3CContext + Attach = function(parentCtx, stack) + local ctx = {} + for k, v in pairs(parentCtx) do + if type(k) == "string" then + local vt = type(v) + if vt == "string" or vt == "number" or vt == "boolean" then + ctx[k] = v + end + end + end + + if not ctx.start then + ctx.start = C4:GetTime() + end + + if not ctx.stack then + ctx.stack = "" + elseif stack and stack ~= "" and type(ctx.stack) == "string" then + ctx.stack = ctx.stack .. " -> " .. stack + end + + return ctx + end, + } +end diff --git a/src/control4-utils/helpers/context/all.lua b/src/control4-utils/helpers/context/all.lua new file mode 100644 index 0000000..c78213a --- /dev/null +++ b/src/control4-utils/helpers/context/all.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "Context") diff --git a/src/control4-utils/helpers/logging/Logger.lua b/src/control4-utils/helpers/logging/Logger.lua new file mode 100644 index 0000000..455eb59 --- /dev/null +++ b/src/control4-utils/helpers/logging/Logger.lua @@ -0,0 +1,245 @@ +---@alias C3CLogFN fun(s: string): nil + +do + if not C3C then + print("Control4Utils: ERROR LOADING src.helpers.logging.Logger, src/base.lua must be required first") + return + end + + local logLevelPropName = "Log Level" + local logModePropName = "Log Mode" + local logLevels = "" + local logModes = "" + local defaultLogLevel = "Error" + local defaultLogMode = "Off" + + local disableRemotelogging = false + + local availableLevels = { + ["Debug"] = "debug", + ["Error"] = "error", + ["Info"] = "info", + } + + for k, _ in pairs(availableLevels) do + logLevels = logLevels .. k .. "," + end + + logLevels = logLevels:sub(1, -2) + + local availableModes = { + ["Print"] = "print", + ["Log"] = "log", + ["Print and Log"] = "printlog", + ["Off"] = "off", + } + + for k, _ in pairs(availableModes) do + logModes = logModes .. k .. "," + end + + logModes = logModes:sub(1, -2) + + -- The following functions are helpers for disabling + -- and enabling logging based on mode and log level + local noop = function(s) end + + -- Log function is set using the logging mode, to either print + -- or error log into the director + local logFn = noop + + -- Print function is set when log leve changes, it internally uses the logFn set by logging mode + local printFn = function(s) + logFn(s) + end + + ---@type {debug: C3CLogFN, error: C3CLogFN, info: C3CLogFN} + local log = { + debug = noop, + error = noop, + info = noop, + } + + --- Sets the logging verbosity + ---@param level "Debug"|"Info"|"Error" + local function setLogLevel(level) + if availableLevels[level] == nil then + print(string.format("Invalid logging level requested: %s", level)) + return + end + + print("Setting log level:" .. level) + if level == "Debug" then + log.debug = printFn + log.info = printFn + log.error = printFn + elseif level == "Error" then + log.debug = noop + log.info = noop + log.error = printFn + elseif level == "Info" then + log.debug = noop + log.info = printFn + log.error = printFn + end + end + + --- Sets the target where logging will be sent + ---@param mode "Print"|"Log"|"Print and Log"|"Off" + local function setLoggingMode(mode) + if availableModes[mode] == nil then + print(string.format("Invalid logging mode requested: %s", mode)) + return + end + + print("Setting log mode:" .. mode) + if mode == "Off" then + logFn = noop + elseif mode == "Print" then + logFn = function(s) + print(s) + end + elseif mode == "Log" then + logFn = function(s) + C4:ErrorLog(s) + end + elseif mode == "Print and Log" then + logFn = function(s) + print(s) + C4:ErrorLog(s) + end + end + end + + ---Formats a table into a string + ---@param tParams table + ---@return string + local function formatParams(tParams) + tParams = tParams or {} + local out = {} + for k, v in pairs(tParams) do + if type(v) == "string" then + local vString = tostring(v) + if vString:len() > 1000 then + table.insert(out, k .. ': "' .. vString:sub(1, 1000) .. ' [...]"') + else + table.insert(out, k .. ': "' .. vString .. '"') + end + elseif type(v) == "table" then + table.insert(out, k .. ": [" .. formatParams(v) .. "]") + else + table.insert(out, k .. ": " .. tostring(v):sub(1, 100)) + end + end + return table.concat(out, ", ") + end + + if C3C.HookIntoOnDriverLateInit then + C3C.HookIntoOnDriverLateInit(function() + local logLevel = Properties[logLevelPropName] + local logMode = Properties[logModePropName] + + if not logLevel or logLevel == "" then + logLevel = defaultLogLevel + end + if not logMode or logMode == "" then + logMode = defaultLogMode + end + + C4:UpdatePropertyList(logLevelPropName, logLevels, logLevel) + C4:UpdatePropertyList(logModePropName, logModes, logMode) + + setLogLevel(logLevel) + setLoggingMode(logMode) + end) + else + print("ERROR: HookIntoOnDriverLateInit is not loaded") + end + + if C3C.HookIntoOnPropertyChanged then + C3C.HookIntoOnPropertyChanged(function(strProperty) + if strProperty == logLevelPropName then + setLogLevel(Properties[logLevelPropName]) + elseif strProperty == logModePropName then + setLoggingMode(Properties[logModePropName]) + end + end) + else + print("ERROR: HookIntoOnPropertyChanged is not loaded") + end + + C3C.Logger = { + DisableRemoteLogging = function() + disableRemotelogging = true + end, + + ---Sends the message to a debug channel + ---@param s string + ---@param ctx table|nil + Debug = function(s, ctx) + local disableRemote = disableRemotelogging + if ctx and ctx.disableRemoteLog == true then + disableRemote = true + ctx.disableRemoteLog = nil + end + + if ctx ~= nil then + log.debug("[DEBUG] > " .. s .. " | CTX: " .. formatParams(ctx)) + else + log.debug("[DEBUG] > " .. s) + end + + if not disableRemote and C3C.RemoteLogger then + local remoteCtx = ctx or {} + remoteCtx.source = "Logger" + C3C.RemoteLogger.Debug(s, remoteCtx) + end + end, + + ---Sends the message to an error channel + ---@param s string + ---@param ctx table|nil + Error = function(s, ctx) + local disableRemote = disableRemotelogging + if ctx and ctx.disableRemoteLog == true then + disableRemote = true + ctx.disableRemoteLog = nil + end + + if ctx ~= nil then + log.error("[ERROR] > " .. s .. " | CTX: " .. formatParams(ctx)) + else + log.error("[ERROR] > " .. s) + end + + if not disableRemote and C3C.RemoteLogger then + local remoteCtx = ctx or {} + remoteCtx.source = "Logger" + C3C.RemoteLogger.Error(s, remoteCtx) + end + end, + + ---Sends the message to an info channel + ---@param s string + ---@param ctx table|nil + Info = function(s, ctx) + local disableRemote = disableRemotelogging + if ctx and ctx.disableRemoteLog == true then + disableRemote = true + ctx.disableRemoteLog = nil + end + + if ctx ~= nil then + log.info("[ INFO] > " .. s .. " | CTX: " .. formatParams(ctx)) + else + log.info("[ INFO] > " .. s) + end + + if not disableRemote and C3C.RemoteLogger then + local remoteCtx = ctx or {} + remoteCtx.source = "Logger" + C3C.RemoteLogger.Info(s, remoteCtx) + end + end, + } +end diff --git a/src/control4-utils/helpers/logging/RemoteLogger.lua b/src/control4-utils/helpers/logging/RemoteLogger.lua new file mode 100644 index 0000000..9611d18 --- /dev/null +++ b/src/control4-utils/helpers/logging/RemoteLogger.lua @@ -0,0 +1,339 @@ +---@alias C3CLogLevel +---|"DEBUG" +---|"INFO" +---|"WARN" +---|"ERROR" + +---@alias C3CRemoteLoggerPayload table + +--- time_of_execution in milliseconds, use C4:GetTime() +---@alias C3CLoggerPayload { level: C3CLogLevel, message: string, service_identifier: string, time_of_execution: number, [string]: string|number|boolean } +---@alias C3CMetricValue string|number|boolean +---@alias C3CMetricPayload { metric: string, service_identifier: string, time_of_execution: number, [string]: C3CMetricValue } + +---@alias C3CRemoteLogFN fun(message: string, context: C3CRemoteLoggerPayload): boolean +---@alias C3CMetricFN fun(metric: string, context: C3CRemoteLoggerPayload): boolean + +C3CRemoteLoggerCastKeyPrefix = "_cast_" +C3CRemoteLoggerTagKeyPrefix = "_tag_" +C3CRemoteLoggerFieldKeyPrefix = "_field_" + +do + if not C3C then + print("Control4Utils: ERROR LOADING src.helpers.logging.RemoteLogger, src/base.lua must be required first") + return + end + + local LoggerConnectionBindingID = 100 + + -- service identifier + ---@type string|nil + local SI = nil + + ---@param payload C3CRemoteLoggerPayload + ---@return table + local encodeTypedPayload = function(payload) + ---@type table + local output = {} + local errorCtx = { + level = "ERROR", + message = "Service tried to encode data for remote logger", + } + local error = false + + for k, v in pairs(payload) do + if k:sub(1, C3CRemoteLoggerCastKeyPrefix:len()) == C3CRemoteLoggerCastKeyPrefix then + error = true + errorCtx["offending_key_" .. k] = k + end + + local vType = type(v) + if vType ~= "nil" and vType ~= "number" and vType ~= "string" and vType ~= "boolean" then + output[k] = "invalid field value type " .. vType + else + output[k] = tostring(v) + if vType ~= "string" then + output[C3CRemoteLoggerCastKeyPrefix .. k] = vType + end + end + end + + if error then + C3C.Logger.Error("Tried to remote log data with reserved context keys", errorCtx) + end + + return output + end + + ---@param tags table + ---@param fields table + ---@return table + local encodeMetrics = function(tags, fields) + ---@type table + local output = {} + + for k, v in pairs(tags) do + output[C3CRemoteLoggerTagKeyPrefix .. k] = v + end + + for k, v in pairs(fields) do + output[C3CRemoteLoggerFieldKeyPrefix .. k] = v + end + + return output + end + + ---@param level C3CLogLevel + ---@param message string + ---@param ctx C3CRemoteLoggerPayload + ---@return boolean + local sendLog = function(level, message, ctx) + if SI == nil then + -- Make sure we do not loop + C3C.Logger.Error( + "Tried to remote log without setting up service identifier, call RemoteLogger.Setup first", + { + + -- Make sure we do not loop + disableRemoteLog = true, + } + ) + return false + end + + ---@type C3CLoggerPayload + local payload = { + level = "ERROR", + message = "", + service_identifier = SI, + time_of_execution = C4:GetTime(), + } + + if ctx.level ~= nil or ctx.message ~= nil or ctx.service_identifier ~= nil or ctx.time_of_execution ~= nil then + C3C.Logger.Error("Tried to remote log data with reserved context keys", { + + -- Make sure we do not loop + disableRemoteLog = true, + message = message, + level = level, + ctx = ctx, + }) + + payload.level = "ERROR" + payload.message = "Service tried to remote log, but used reserved keyword in the context value" + + C4:SendToProxy(LoggerConnectionBindingID, "INSERT_LOG", payload, "NOTIFY") + return false + end + + payload.level = level + payload.message = message + + for k, v in pairs(ctx) do + payload[k] = v + end + + C4:SendToProxy(LoggerConnectionBindingID, "INSERT_LOG", encodeTypedPayload(payload), "NOTIFY") + + return true + end + + ---@param metric string + ---@param tags table + ---@param fields table + ---@return boolean + local sendMetric = function(metric, tags, fields) + if SI == nil then + C3C.Logger.Error( + "tried to add remote metric without setting up service identifier, call C3C.RemoteLogger.Setup first", + { + disableRemoteLog = true, + stack = "RemoteLogger.sendMetric", + } + ) + return false + end + + ---@type C3CMetricPayload + local payload = { + metric = metric, + service_identifier = SI, + time_of_execution = C4:GetTime(), + } + + if tags.service_identifier ~= nil or tags.time_of_execution ~= nil then + C3C.Logger.Error( + "Tried to add remote metric data with reserved tag keywords", + { metric = metric, tags = tags } + ) + + payload.level = "ERROR" + payload.message = "Service tried to add remote metric, but used reserved tag keywords" + + C4:SendToProxy(LoggerConnectionBindingID, "INSERT_LOG", payload, "NOTIFY") + return false + end + + payload.metric = metric + + for k, v in pairs(encodeMetrics(tags, fields)) do + payload[k] = v + end + + C4:SendToProxy(LoggerConnectionBindingID, "INSERT_METRIC", encodeTypedPayload(payload), "NOTIFY") + + return true + end + + C3C.RemoteLogger = { + ---@param payload table + ---@return C3CRemoteLoggerPayload + DecodeUntypedPayload = function(payload) + ---@type C3CRemoteLoggerPayload + local output = {} + + for k, v in pairs(payload) do + if k:sub(1, C3CRemoteLoggerCastKeyPrefix:len()) ~= C3CRemoteLoggerCastKeyPrefix then + local castValue = payload[C3CRemoteLoggerCastKeyPrefix .. k] + if castValue == "" or castValue == nil then + -- performance + output[k] = v + elseif castValue == "number" then + local numVal = tonumber(v) + if type(numVal) == "number" then + output[k] = numVal + else + output[k] = v + end + elseif castValue == "boolean" then + if v:lower() == "true" or v == "1" then + output[k] = true + else + output[k] = false + end + else + output[k] = v + end + end + end + + return output + end, + + ---@param payload table + ---@return {fields: table, tags: table} + DecodeMetrics = function(payload) + ---@type {fields: table, tags: table} + local output = { + fields = {}, + tags = {}, + } + + for k, v in pairs(payload) do + if k:sub(1, C3CRemoteLoggerTagKeyPrefix:len()) == C3CRemoteLoggerTagKeyPrefix then + output.tags[k:sub(C3CRemoteLoggerTagKeyPrefix:len() + 1)] = v + elseif k:sub(1, C3CRemoteLoggerFieldKeyPrefix:len()) == C3CRemoteLoggerFieldKeyPrefix then + output.fields[k:sub(C3CRemoteLoggerFieldKeyPrefix:len() + 1)] = v + end + end + + return output + end, + + ---@param service_identifier string + Setup = function(service_identifier) + SI = service_identifier + C4:AddDynamicBinding( + LoggerConnectionBindingID, + "CONTROL", + false, + "Logger", + "c3c-remote-logger", + false, + true + ) + end, + + ---@type C3CRemoteLogFN + Debug = function(message, ctx) + return sendLog("DEBUG", message, ctx) + end, + + ---@type C3CRemoteLogFN + Info = function(message, ctx) + return sendLog("INFO", message, ctx) + end, + + ---@type C3CRemoteLogFN + Warn = function(message, ctx) + return sendLog("WARN", message, ctx) + end, + + ---@type C3CRemoteLogFN + Error = function(message, ctx) + return sendLog("ERROR", message, ctx) + end, + + -- Example fields: humidity, temperature, co2 + ---@param sensorId string + ---@param location string + ---@param fields table + ---@param additionalTags nil|table + AirSensorMetric = function(sensorId, location, fields, additionalTags) + ---@type table + local tags = { + sensor_id = sensorId, + location = location, + } + + if additionalTags ~= nil then + for k, v in pairs(additionalTags) do + tags[k] = v + end + end + + return sendMetric("air_sensor", tags, fields) + end, + + -- Example kinds: water_level, door_open + -- Example fields: below_threshold, open + ---@param sensorId string + ---@param kind string + ---@param fields table + ---@param additionalTags nil|table + StatusMetric = function(sensorId, kind, fields, additionalTags) + ---@type table + local tags = { + sensor_id = sensorId, + kind = kind, + } + + if additionalTags ~= nil then + for k, v in pairs(additionalTags) do + tags[k] = v + end + end + + return sendMetric("status", tags, fields) + end, + + -- Example fields: airflow, boost, setpoint + ---@param sensorId string + ---@param fields table + ---@param additionalTags nil|table + VentilationMetric = function(sensorId, fields, additionalTags) + ---@type table + local tags = { + sensor_id = sensorId, + } + + if additionalTags ~= nil then + for k, v in pairs(additionalTags) do + tags[k] = v + end + end + + return sendMetric("ventilation", tags, fields) + end, + } +end diff --git a/src/control4-utils/helpers/logging/all.lua b/src/control4-utils/helpers/logging/all.lua new file mode 100644 index 0000000..c868113 --- /dev/null +++ b/src/control4-utils/helpers/logging/all.lua @@ -0,0 +1,4 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "Logger") +require(dirRequire .. "RemoteLogger") diff --git a/src/control4-utils/helpers/strings/Trim.lua b/src/control4-utils/helpers/strings/Trim.lua new file mode 100644 index 0000000..ed89205 --- /dev/null +++ b/src/control4-utils/helpers/strings/Trim.lua @@ -0,0 +1,7 @@ +--- Trim whitespace at start and end of string +---@param s string +---@return string, number +function Trim(s) -- Source: PiL2 20.4 + s = s or "" + return s:gsub("^%s*(.-)%s*$", "%1") +end diff --git a/src/control4-utils/helpers/strings/all.lua b/src/control4-utils/helpers/strings/all.lua new file mode 100644 index 0000000..b150553 --- /dev/null +++ b/src/control4-utils/helpers/strings/all.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "Trim") diff --git a/src/control4-utils/helpers/timers/OneShotTimer.lua b/src/control4-utils/helpers/timers/OneShotTimer.lua new file mode 100644 index 0000000..af7c842 --- /dev/null +++ b/src/control4-utils/helpers/timers/OneShotTimer.lua @@ -0,0 +1,43 @@ +do + if not C3C then + print("Control4Utils: ERROR LOADING src.helpers.timers.OneShotTimer, src/base.lua must be required first") + return + end + + ---@type table + local timers = {} + + C3C.OneShotTimer = { + ClearAll = function() + for _, timer in pairs(timers) do + timer:Cancel() + end + timers = {} + end, + + ---@param nDelay number Numeric value in milliseconds which is the desired timer delay. This value must be greater than 0. + ---@param fCallback fun(self: C4LuaTimer, skips: number) The function to be called when the timer fires. The function signature for non-repeating timers is: function(timer) + ---@param Name string? + Add = function(nDelay, fCallback, Name) + local timer = C4:SetTimer(nDelay, fCallback, false) + + -- Look for name if not nil, if found, remove existing timer callback... + if Name ~= nil then + for k, v in pairs(timers) do + if k == Name then + v:Cancel() + timers[k] = nil + end + end + + timers[Name] = timer + end + end, + } + + if C3C.HookIntoOnDriverLateInit then + C3C.HookIntoOnDriverLateInit(C3C.OneShotTimer.ClearAll) + else + print("hook HookIntoOnDriverLateInit must be loaded") + end +end diff --git a/src/control4-utils/helpers/timers/all.lua b/src/control4-utils/helpers/timers/all.lua new file mode 100644 index 0000000..e8c5c60 --- /dev/null +++ b/src/control4-utils/helpers/timers/all.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "OneShotTimer") diff --git a/src/control4-utils/hooks.lua b/src/control4-utils/hooks.lua new file mode 100644 index 0000000..424fb7c --- /dev/null +++ b/src/control4-utils/hooks.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "hooks.all") diff --git a/src/control4-utils/hooks/ExecuteCommand.lua b/src/control4-utils/hooks/ExecuteCommand.lua new file mode 100644 index 0000000..75ccfeb --- /dev/null +++ b/src/control4-utils/hooks/ExecuteCommand.lua @@ -0,0 +1,30 @@ +---@alias C3CExecuteCommandCallback fun(strCommand: string, tParams: table) +do + if not C3C then + print("Control4Utils: ERROR LOADING src.hooks.ExecuteCommand, src/base.lua must be required first") + return + end + + -- Stores global hook if already defined + local prevHook + + ---@type C3CExecuteCommandCallback[] + local hooks = {} + + ---@param callback C3CExecuteCommandCallback + C3C.HookIntoExecuteCommand = function(callback) + table.insert(hooks, callback) + end + + prevHook, ExecuteCommand = + ExecuteCommand or function() end, + ---@param strCommand string + ---@param tParams table + function(strCommand, tParams) + for _, callback in pairs(hooks) do + callback(strCommand, tParams) + end + + prevHook(strCommand, tParams) + end +end diff --git a/src/control4-utils/hooks/OnDriverLateInit.lua b/src/control4-utils/hooks/OnDriverLateInit.lua new file mode 100644 index 0000000..89d46c7 --- /dev/null +++ b/src/control4-utils/hooks/OnDriverLateInit.lua @@ -0,0 +1,28 @@ +---@alias C3COnDriverLateInitCallback fun(strDIT: string) +do + if not C3C then + print("Control4Utils: ERROR LOADING src.hooks.OnDriverLateInit, src/base.lua must be required first") + return + end + -- Stores global hook if already defined + local prevHook + + ---@type C3COnDriverLateInitCallback[] + local hooks = {} + + ---@param callback C3COnDriverLateInitCallback + C3C.HookIntoOnDriverLateInit = function(callback) + table.insert(hooks, callback) + end + + prevHook, OnDriverLateInit = + OnDriverLateInit or function() end, + ---@param strDIT string + function(strDIT) + for _, callback in pairs(hooks) do + callback(strDIT) + end + + prevHook(strDIT) + end +end diff --git a/src/control4-utils/hooks/OnPropertyChanged.lua b/src/control4-utils/hooks/OnPropertyChanged.lua new file mode 100644 index 0000000..0a9ae86 --- /dev/null +++ b/src/control4-utils/hooks/OnPropertyChanged.lua @@ -0,0 +1,29 @@ +---@alias C3COnPropertyChangedCallback fun(strProperty: string) +do + if not C3C then + print("Control4Utils: ERROR LOADING src.hooks.OnPropertyChanged, src/base.lua must be required first") + return + end + + -- Stores global hook if already defined + local prevHook + + ---@type C3COnPropertyChangedCallback[] + local hooks = {} + + ---@param callback C3COnPropertyChangedCallback + C3C.HookIntoOnPropertyChanged = function(callback) + table.insert(hooks, callback) + end + + prevHook, OnPropertyChanged = + OnPropertyChanged or function() end, + ---@param strProperty string + function(strProperty) + for _, callback in pairs(hooks) do + callback(strProperty) + end + + prevHook(strProperty) + end +end diff --git a/src/control4-utils/hooks/ReceivedFromProxy.lua b/src/control4-utils/hooks/ReceivedFromProxy.lua new file mode 100644 index 0000000..a454d25 --- /dev/null +++ b/src/control4-utils/hooks/ReceivedFromProxy.lua @@ -0,0 +1,31 @@ +---@alias C3CReceivedFromProxyCallback fun(idBinding: number, strCommand: string, tParams: table) +do + if not C3C then + print("Control4Utils: ERROR LOADING src.hooks.ReceivedFromProxy, src/base.lua must be required first") + return + end + + -- Stores global hook if already defined + local prevHook + + ---@type C3CReceivedFromProxyCallback[] + local hooks = {} + + ---@param callback C3CReceivedFromProxyCallback + C3C.HookIntoReceivedFromProxy = function(callback) + table.insert(hooks, callback) + end + + prevHook, ReceivedFromProxy = + ReceivedFromProxy or function() end, + ---@param idBinding number + ---@param strCommand string + ---@param tParams table + function(idBinding, strCommand, tParams) + for _, callback in pairs(hooks) do + callback(idBinding, strCommand, tParams) + end + + prevHook(idBinding, strCommand, tParams) + end +end diff --git a/src/control4-utils/hooks/all.lua b/src/control4-utils/hooks/all.lua new file mode 100644 index 0000000..cc73881 --- /dev/null +++ b/src/control4-utils/hooks/all.lua @@ -0,0 +1,6 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "ExecuteCommand") +require(dirRequire .. "OnDriverLateInit") +require(dirRequire .. "OnPropertyChanged") +require(dirRequire .. "ReceivedFromProxy") diff --git a/src/control4-utils/knx.lua b/src/control4-utils/knx.lua new file mode 100644 index 0000000..edcf98a --- /dev/null +++ b/src/control4-utils/knx.lua @@ -0,0 +1,3 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "knx.all") diff --git a/src/control4-utils/knx/DPT.lua b/src/control4-utils/knx/DPT.lua new file mode 100644 index 0000000..14acb34 --- /dev/null +++ b/src/control4-utils/knx/DPT.lua @@ -0,0 +1,25 @@ +--- @see https://snap-one.github.io/docs-driverworks-knx/#knx-datapoint-type-overview +--- @alias C3CKnxDPT +---|"DPT_1" +---|"DPT_2" +---|"DPT_3" +---|"DPT_4 +---|"DPT_5" +---|"DPT_6" +---|"DPT_7" +---|"DPT_8" +---|"DPT_9" +---|"DPT_10" +---|"DPT_11" +---|"DPT_12" +---|"DPT_13" +---|"DPT_14" +---|"DPT_15" +---|"DPT_16" +---|"DPT_17" +---|"DPT_18" +---|"DPT_19" +---|"DPT_5_001" +---|"DPT_232" +---|"DPT_242_600" +---|"DPT_251_600" diff --git a/src/control4-utils/knx/GroupAddress.lua b/src/control4-utils/knx/GroupAddress.lua new file mode 100644 index 0000000..08447d2 --- /dev/null +++ b/src/control4-utils/knx/GroupAddress.lua @@ -0,0 +1,235 @@ +---@alias C3CKnxGenericGroupAddressShape { DPT: C3CKnxDPT, GA: string, Name: T, Value: number | nil } + +do + if not C3C then + print("Control4Utils: ERROR LOADING src.knx.GroupAddress, src/base.lua must be required first") + return + end + + ---@param name C3CKnxGroupAddressName + ---@param ga string + ---@param dpt C3CKnxDPT + C3C.KnxGroupAddress = function(name, ga, dpt) + ---@class GroupAddress + ---@field DPT C3CKnxDPT + ---@field GA string + ---@field Name C3CKnxGroupAddressName + ---@field Value number|nil + local class = { + DPT = dpt, + GA = ga, + Name = name, + Value = nil, + } + + local responding = false + + ---@param v number + function class:Send(v) + class.Value = v + C3C.KnxProxy.Send(class.DPT, class.GA, class.Value) + end + + function class:RespondToRead() + responding = true + end + + function class:isResponding() + return responding + end + + return class + end + + ---@type {[C3CKnxGroupAddressName]: GroupAddress} + local namedRegistry = {} + ---@type {[string]: GroupAddress|nil} + local addressedRegistry = {} + + ---@type {[string]: GroupAddress} + local watchedRegistry = {} + + ---@type {[C3CKnxGroupAddressName]: nil|fun(current: GroupAddress, ctx: { newVal: number, prevVal: number?})[]} + local onChangedRegistry = {} + + ---@type {[C3CKnxGroupAddressName]: nil|fun(current: GroupAddress, ctx: { newVal: number, prevVal: number?})[]} + local onReceiveRegistry = {} + + local registered = false + + for _, v in pairs(C3CKnxCreateGroupAddresses()) do + addressedRegistry[v.GA] = v + namedRegistry[v.Name] = v + end + + local registerGAs = function() + C3C.KnxProxy.ClearGroupAddresses() + + C3C.OneShotTimer.Add(3000, function() + registered = true + for _, g in pairs(watchedRegistry) do + C3C.KnxProxy.AddGroupAddress(g) + end + end, "GroupAddressAddGroupItemsToKnx") + end + + C3C.KnxAddresses = { + ---Get GroupAddress by name + ---@param n C3CKnxGroupAddressName + ---@return GroupAddress + Get = function(n) + if namedRegistry[n] == nil then + C3C.Logger.Error( + "error accessing non existing group address name, this should never happen...never", + { name = n } + ) + end + return namedRegistry[n] + end, + + ---Get GroupAddress by address if defined + ---@param ga string + ---@return GroupAddress|nil + GetByGA = function(ga) + return addressedRegistry[ga] + end, + + ---@param n C3CKnxGroupAddressName + ---@param onValueReceive nil|fun(current: GroupAddress, state: { newVal: number, prevVal: number? }) + ---@param onValueChange nil|fun(current: GroupAddress, state: { newVal: number, prevVal: number? }) + Watch = function(n, onValueReceive, onValueChange) + if namedRegistry[n] == nil then + C3C.Logger.Error(W + "error accessing non existing group address name to Listen, this should never happen...never", + { name = n } + ) + end + + local addr = namedRegistry[n] + watchedRegistry[addr.GA] = addr + + if registered then + C3C.OneShotTimer.Add(1000, function() + registerGAs() + end, "knx-register-group-addresses") + end + + if onValueReceive ~= nil then + if onReceiveRegistry[addr.GA] == nil then + onReceiveRegistry[addr.GA] = {} + end + + table.insert(onReceiveRegistry[addr.GA], onValueReceive) + end + + if onValueChange ~= nil then + if onChangedRegistry[addr.GA] == nil then + onChangedRegistry[addr.GA] = {} + end + + table.insert(onChangedRegistry[addr.GA], onValueChange) + end + end, + + ---@param n C3CKnxGroupAddressName + UnWatch = function(n) + local addr = namedRegistry[n] + watchedRegistry[addr.GA] = nil + onReceiveRegistry[addr.GA] = nil + onChangedRegistry[addr.GA] = nil + + if registered then + C3C.OneShotTimer.Add(1000, function() + registerGAs() + end, "knx-register-group-addresses") + end + end, + } + + C3C.HookIntoOnDriverLateInit(function() + if not C3C.KnxProxy or not C3C.OneShotTimer then + C3C.Logger.Error( + "C3C.KnxProxy or C3C.OneShotTimer is not defined", + { fn = "control4-utils.knx.GroupADdress HookIntoOnDriverLateInit" } + ) + return + end + + registerGAs() + end) + + C3C.HookIntoExecuteCommand(function(strCommand, tParams) + if not Trim then + C3C.Logger.Error( + "Trim function is not defined", + { fn = "control4-utils.knx.GroupADdress HookIntoExecuteCommand" } + ) + return + end + + if strCommand == "KNX_READ_REQUEST" then + local ga = Trim(tParams["GROUP_ADDRESS"]) + if ga == "" then + C3C.Logger.Error("received invalid group address from KNX_READ_REQUEST", { + groupAddress = ga, + }) + return + end + + local addr = C3C.KnxAddresses.GetByGA(ga) + if addr ~= nil and addr.Value ~= nil and addr:isResponding() then + addr:Send(addr.Value) + end + end + + if strCommand ~= "DATA_FROM_KNX" then + return + end + + local groupAddress = Trim(tParams["GROUP_ADDRESS"]) + + if groupAddress == "" then + C3C.Logger.Error("received empty group address from DATA_FROM_KNX") + return + end + + local addr = C3C.KnxAddresses.GetByGA(groupAddress) + + if addr == nil then + C3C.Logger.Error("received unknown group address from DATA_FROM_KNX", { + groupAddress = groupAddress, + }) + return + end + + local value = nil + + if addr.DPT == "DPT_3" then + value = tonumber(tParams["DIRECTION"]) + else + value = tonumber(tParams["VALUE"]) + end + + if value == nil then + C3C.Logger.Error("received invalid data from DATA_FROM_KNX, value is nil", { + groupAddress = groupAddress, + }) + return + end + + local prevValue = addr.Value + addr.Value = value + + if onReceiveRegistry[addr.GA] then + for _, callback in pairs(onReceiveRegistry[addr.GA]) do + callback(addr, { newVal = value, prevVal = prevValue }) + end + end + + if onChangedRegistry[addr.GA] and prevValue ~= value then + for _, callback in pairs(onChangedRegistry[addr.GA]) do + callback(addr, { newVal = value, prevVal = prevValue }) + end + end + end) +end diff --git a/src/control4-utils/knx/Proxy.lua b/src/control4-utils/knx/Proxy.lua new file mode 100644 index 0000000..f3bd713 --- /dev/null +++ b/src/control4-utils/knx/Proxy.lua @@ -0,0 +1,68 @@ +do + if not C3C then + print("Control4Utils: ERROR LOADING src.knx.Proxy, src/base.lua must be required first") + return + end + + local binding = nil + + local send = function(strCommand, tParams) + if not binding then + C3C.Logger.Error( + "tried to send data to knx proxy without setting up binding, call C3C.KnxProxy.Setup first", + { + stack = "KnxProxy.send", + } + ) + return false + end + + local ctx = tParams + ctx.knxCommand = strCommand + + C3C.Logger.Debug("sending data to KNX", ctx) + + C4:SendToProxy(binding, strCommand, tParams) + end + + C3C.KnxProxy = { + ---@param bindingId number + Setup = function(bindingId) + binding = bindingId + end, + + ---@param ga GroupAddress + AddGroupAddress = function(ga) + send("ADD_GROUP_ITEM", { + GROUP_ADDRESS = ga.GA, + DEVICE_ID = C4:GetDeviceID(), + PROPERTY = ga.Name, + DATA_POINT_TYPE = ga.DPT, + }) + end, + + ClearGroupAddresses = function() + send("CLEAR_GROUP_ITEMS", { DEVICE_ID = C4:GetDeviceID() }) + end, + + ---@param dpt C3CKnxDPT + ---@param groupAddress string + ---@param value number + Send = function(dpt, groupAddress, value) + local tParams = {} + tParams.DATA_POINT_TYPE = dpt + tParams.GROUP_ADDRESS = groupAddress + tParams.VALUE = value + + send("SEND_TO_KNX", tParams) + end, + + ---@param groupAddress string + Read = function(groupAddress) + local tParams = {} + tParams.GROUP_ADDRESS = groupAddress + + send("REQUEST_STATUS", tParams) + end, + } +end diff --git a/src/control4-utils/knx/all.lua b/src/control4-utils/knx/all.lua new file mode 100644 index 0000000..b151b3f --- /dev/null +++ b/src/control4-utils/knx/all.lua @@ -0,0 +1,7 @@ +local dirRequire = (...):match("(.-)[^%.%/]+$") + +require(dirRequire .. "DPT") +require(dirRequire .. "gen.ADDRESSES") +require(dirRequire .. "gen.createGroupAddresses") +require(dirRequire .. "GroupAddress") +require(dirRequire .. "Proxy") diff --git a/src/control4-utils/knx/gen/ADDRESSES.lua b/src/control4-utils/knx/gen/ADDRESSES.lua new file mode 100644 index 0000000..330859d --- /dev/null +++ b/src/control4-utils/knx/gen/ADDRESSES.lua @@ -0,0 +1,927 @@ +---@alias C3CKnxGroupAddressName +---|C3CKnxGroupAddressNameControl +---|C3CKnxGroupAddressNameDimmer +---|C3CKnxGroupAddressNameHeat +---|C3CKnxGroupAddressNameMedia +---|C3CKnxGroupAddressNameOther +---|C3CKnxGroupAddressNameScene +---|C3CKnxGroupAddressNameSwitch +---|C3CKnxGroupAddressNameVent + +---@alias C3CKnxGroupAddressNameControl +---|"Control LF Hallway Presence" +---|"Control LF Hallway Presence Enable" +---|"Control LF Hallway Presence Enable Workaround" +---|"Control LF Hallway Presence Alarm" +---|"Control LF Hallway Presence Alarm State" +---|"Control LF Study LED Brightness Night" +---|"Control UF Bathroom Presence" +---|"Control UF Bathroom Presence Enable" +---|"Control UF Bathroom Presence Delayed" +---|"Control UF Bathroom Brightness" + +---@alias C3CKnxGroupAddressNameDimmer +--- Hallway Primary +---|"Dimmer LF Hallway" +--- Hallway Primary +---|"Dimmer LF Hallway FB" +--- Hallway Primary +---|"Dimmer LF Hallway Dimmer" +---|"Dimmer LF Hallway Color" +---|"Dimmer LF Hallway Color FB" +---|"Dimmer LF Hallway Color Dimmer" +--- Bathroom Primary +---|"Dimmer LF Bathroom Primary" +--- Bathroom Primary +---|"Dimmer LF Bathroom Primary FB" +--- Bathroom Primary +---|"Dimmer LF Bathroom Primary Dimmer" +---|"Dimmer LF Bathroom Primary Color" +---|"Dimmer LF Bathroom Primary Color FB" +---|"Dimmer LF Bathroom Primary Color Dimmer" +--- Bathroom Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient" +--- Bathroom Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient FB" +--- Bathroom Ambient +---|"Dimmer LF Bathroom Ambient Dimmer" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bathroom Ambient Saturation Dimming" +--- Bathroom Vanity +---|"Dimmer LF Bathroom Vanity" +--- Bathroom Vanity +---|"Dimmer LF Bathroom Vanity FB" +--- Bathroom Vanity +---|"Dimmer LF Bathroom Vanity Dimmer" +---|"Dimmer LF Bathroom Vanity Color" +---|"Dimmer LF Bathroom Vanity Color FB" +---|"Dimmer LF Bathroom Vanity Color Dimmer" +--- Laundry Primary +---|"Dimmer LF Laundry Primary" +--- Laundry Primary +---|"Dimmer LF Laundry Primary FB" +--- Laundry Primary +---|"Dimmer LF Laundry Primary Dimmer" +---|"Dimmer LF Laundry Color" +---|"Dimmer LF Laundry Color FB" +---|"Dimmer LF Laundry Color Dimmer" +--- Bedroom 1 Primary +---|"Dimmer LF Bedroom 1 Primary" +--- Bedroom 1 Primary +---|"Dimmer LF Bedroom 1 Primary FB" +--- Bedroom 1 Primary +---|"Dimmer LF Bedroom 1 Primary Dimmer" +---|"Dimmer LF Bedroom 1 Primary Color" +---|"Dimmer LF Bedroom 1 Primary Color FB" +---|"Dimmer LF Bedroom 1 Primary Color Dimmer" +--- Bedroom 1 Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient" +--- Bedroom 1 Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient FB" +--- Bedroom 1 Ambient +---|"Dimmer LF Bedroom 1 Ambient Dimmer" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 1 Ambient Saturation Dimming" +--- Study Primary +---|"Dimmer LF Study Primary" +--- Study Primary +---|"Dimmer LF Study Primary FB" +--- Study Primary +---|"Dimmer LF Study Primary Dimmer" +---|"Dimmer LF Study Color" +---|"Dimmer LF Study Color FB" +---|"Dimmer LF Study Color Dimmer" +--- Study Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient" +--- Study Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient FB" +--- Study Ambient +---|"Dimmer LF Study Ambient Dimmer" +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer LF Study Ambient Saturation Dimming" +--- Bedroom 2 Primary +---|"Dimmer LF Bedroom 2 Primary" +--- Bedroom 2 Primary +---|"Dimmer LF Bedroom 2 Primary FB" +--- Bedroom 2 Primary +---|"Dimmer LF Bedroom 2 Primary Dimmer" +---|"Dimmer LF Bedroom 2 Primary Color" +---|"Dimmer LF Bedroom 2 Primary Color FB" +---|"Dimmer LF Bedroom 2 Primary Color Dimmer" +--- Bedroom 2 Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient" +--- Bedroom 2 Ambient +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient FB" +--- Bedroom 2 Ambient +---|"Dimmer LF Bedroom 2 Ambient Dimmer" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer LF Bedroom 2 Ambient Saturation Dimming" +--- Utility room Primary +---|"Dimmer LF Utility" +--- Utility room Primary +---|"Dimmer LF Utility FB" +--- Utility room Primary +---|"Dimmer LF Utility Dimmer" +---|"Dimmer LF Utility Color" +---|"Dimmer LF Utility Color FB" +---|"Dimmer LF Utility Color Dimmer" +--- Hallway Entry +---|"Dimmer UF Hallway" +--- Hallway Entry +---|"Dimmer UF Hallway FB" +--- Hallway Entry +---|"Dimmer UF Hallway Dimming" +--- Hallway Entry +---|"Dimmer UF Hallway Color" +--- Hallway Entry +---|"Dimmer UF Hallway Color FB" +--- Hallway Entry +---|"Dimmer UF Hallway Color Dimming" +--- Bathroom Primary +---|"Dimmer UF Bathroom Primary" +--- Bathroom Primary +---|"Dimmer UF Bathroom Primary FB" +--- Bathroom Primary +---|"Dimmer UF Bathroom Primary Dimming" +---|"Dimmer UF Bathroom Primary Color" +---|"Dimmer UF Bathroom Primary Color FB" +---|"Dimmer UF Bathroom Primary Color Dimming" +--- Bathroom Ambient +---|"Dimmer UF Bathroom Ambient" +--- Bathroom Ambient +---|"Dimmer UF Bathroom Ambient FB" +--- Bathroom Ambient +---|"Dimmer UF Bathroom Ambient Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bathroom Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bathroom Ambient HSV FB" +---|"Dimmer UF Bathroom Ambient Hue Dimming" +---|"Dimmer UF Bathroom Ambient Saturation Dimming" +--- Bathroom Vanity +---|"Dimmer UF Bathroom Vanity" +--- Bathroom Vanity +---|"Dimmer UF Bathroom Vanity FB" +--- Bathroom Vanity +---|"Dimmer UF Bathroom Vanity Dimming" +---|"Dimmer UF Bathroom Vanity Color" +---|"Dimmer UF Bathroom Vanity Color FB" +---|"Dimmer UF Bathroom Vanity Color Dimming" +--- Bedroom Primary +---|"Dimmer UF Bedroom Primary" +--- Bedroom Primary +---|"Dimmer UF Bedroom Primary FB" +--- Bedroom Primary +---|"Dimmer UF Bedroom Primary Dimming" +---|"Dimmer UF Bedroom Primary Color" +---|"Dimmer UF Bedroom Primary Color FB" +---|"Dimmer UF Bedroom Primary Color Dimming" +--- Bedroom Ambient +---|"Dimmer UF Bedroom Ambient" +--- Bedroom Ambient +--- HAS UNKNOWN DPT +---|"Dimmer UF Bedroom Ambient FB" +--- Bedroom Ambient +---|"Dimmer UF Bedroom Ambient Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bedroom Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bedroom Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bedroom Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Bedroom Ambient Saturation Dimming" +--- Bedroom Closet +---|"Dimmer UF Closet" +--- Bedroom Closet +---|"Dimmer UF Closet FB" +--- Bedroom Closet +---|"Dimmer UF Closet Dimming" +---|"Dimmer UF Closet Color" +---|"Dimmer UF Closet Color FB" +---|"Dimmer UF Closet Color Dimming" +--- Living room Primary +---|"Dimmer UF Living room Primary" +--- Living room Primary +---|"Dimmer UF Living room Primary FB" +--- Living room Primary +---|"Dimmer UF Living room Primary Dimming" +--- Living room Primary +---|"Dimmer UF Living room Primary Color" +--- Living room Primary +---|"Dimmer UF Living room Primary Color FB" +--- Living room Primary +---|"Dimmer UF Living room Primary Color Dimming" +--- Living room Ambient +---|"Dimmer UF Living room Ambient" +--- Living room Ambient +---|"Dimmer UF Living room Ambient FB" +--- Living room Ambient +---|"Dimmer UF Living room Ambient Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Living room Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer UF Living room Ambient HSV FB" +--- Living room Ambient +---|"Dimmer UF Living room Ambient Hue Dimming" +--- Living room Ambient +---|"Dimmer UF Living room Ambient Saturation Dimming" +--- Office Primary +---|"Dimmer UF Office Primary" +--- Office Primary +---|"Dimmer UF Office Primary FB" +--- Office Primary +---|"Dimmer UF Office Primary Dimming" +---|"Dimmer UF Office Color" +---|"Dimmer UF Office Color FB" +---|"Dimmer UF Office Color Dimming" +--- Dining room Primary +---|"Dimmer UF Dining room" +--- Dining room Primary +---|"Dimmer UF Dining room FB" +--- Dining room Primary +---|"Dimmer UF Dining room Dimming" +---|"Dimmer UF Dining Room Color" +---|"Dimmer UF Dining Room Color FB" +---|"Dimmer UF Dining Room Color Dimming" +--- Kitchen Primary +---|"Dimmer UF Kitchen Primary" +--- Kitchen Primary +---|"Dimmer UF Kitchen Primary FB" +--- Kitchen Primary +---|"Dimmer UF Kitchen Primary Dimming" +---|"Dimmer UF Kitchen Primary Color" +---|"Dimmer UF Kitchen Primary Color FB" +---|"Dimmer UF Kitchen Primary Color Dimming" +--- Kitchen Ambient +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient" +--- Kitchen Ambient +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient FB" +--- Kitchen Ambient +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient HSV" +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient HSV FB" +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient Hue Dimming" +--- HAS UNKNOWN DPT +---|"Dimmer UF Kitchen Ambient Saturation Dimming" +--- Kitchen Counter +---|"Dimmer UF Kitchen Counter" +--- Kitchen Counter +---|"Dimmer UF Kitchen Counter FB" +--- Kitchen Counter +---|"Dimmer UF Kitchen Counter Dimming" +---|"Dimmer UF Kitchen Counter Color" +---|"Dimmer UF Kitchen Counter Color FB" +---|"Dimmer UF Kitchen Counter Color Dimming" +--- Kitchen Counter +---|"Dimmer UF Pantry" +--- Kitchen Counter +---|"Dimmer UF Pantry FB" +--- Kitchen Counter +---|"Dimmer UF Pantry Dimming" +---|"Dimmer UF Pantry Color" +---|"Dimmer UF Pantry Color FB" +---|"Dimmer UF Pantry Color Dimming" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase Tree Dimming" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase Tree Value" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase Tree Value Feedback" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail Swtich" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail Value" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail Value Feedback" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail bottom Dimming" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail bottom Value" +--- Staircase Primary +--- HAS UNKNOWN DPT +---|"Dimmer Staircase handrail bottom Value Feedback" +--- 20_Garden Terrace +--- HAS UNKNOWN DPT +---|"Dimmer EXT Terrace Dimming" +--- 20_Garden Terrace +--- HAS UNKNOWN DPT +---|"Dimmer EXT Terrace Value" +--- 20_Garden Terrace +--- HAS UNKNOWN DPT +---|"Dimmer EXT Terrace Value Feedback" +--- 20_Exterior Entrance +--- HAS UNKNOWN DPT +---|"Dimmer EXT Entrance Value Feedback" +--- 20_Exterior Entrance +--- HAS UNKNOWN DPT +---|"Dimmer EXT Entrance Dimming" +--- 20_Exterior Entrance +--- HAS UNKNOWN DPT +---|"Dimmer EXT Entrance Value" +--- Parking place Primary +--- HAS UNKNOWN DPT +---|"Dimmer Parking place Dimming" +--- Parking place Primary +--- HAS UNKNOWN DPT +---|"Dimmer Parking place Value" +--- Parking place Primary +--- HAS UNKNOWN DPT +---|"Dimmer Parking place Value Feedback" + +---@alias C3CKnxGroupAddressNameHeat +---|"Heat LF Hallway Temperature FB" +---|"Heat LF Bathroom Temperature FB" +---|"Heat LF Bathroom Ladder boost" +---|"Heat LF Bathroom Ladder boost FB" +--- HAS UNKNOWN DPT +---|"Heat LF Laundry Temperature FB" +---|"Heat LF Bedroom 1 Temperature FB" +---|"Heat LF Study Temperature FB" +---|"Heat LF Bedroom 2 Temperature FB" +---|"Heat LF Technical Room Temperature FB" +---|"Heat UF Hallway Temperature FB" +---|"Heat UF Bathroom Temperature FB" +---|"Heat UF Bathroom Ladder boost" +---|"Heat UF Bathroom Ladder boost FB" +---|"Heat UF Bedroom Temperature FB" +---|"Heat UF Living Room Temperature FB" +---|"Heat UF Kitchen Temperature FB" +---|"Heat Circulation Pump" +---|"Heat Circulation Pump FB" +---|"Heat Hot Water Circulation State" +---|"Heat Hot Water Circulation Emergency State" +---|"Heat Hot Water Circulation Control Value" +--- Heating = 1 +--- Cooling = 0 +---|"Heat Heating-Cooling" +--- Heating = 1 +--- Cooling = 0 +---|"Heat Heating-Cooling State" +--- HAS UNKNOWN DPT +---|"Heat Temperature Setpoint" +--- HAS UNKNOWN DPT +---|"Heat Temperature Setpoint State" +---|"Heat Heating Off lock" +---|"Heat UF+LF Heating Circulation Pump" +---|"Heat UF+LF Heating Circulation Pump State" + +---@alias C3CKnxGroupAddressNameMedia +---|"Media UF Bathroom Playing" +---|"Media UF Bathroom Off" +---|"Media UF Bathroom Volume Down" +---|"Media UF Bathroom Volume Up" +---|"Media UF Bathroom Previous" +---|"Media UF Bathroom Next" +---|"Media UF Bathroom Preset Cycle Forward" + +---@alias C3CKnxGroupAddressNameOther +---|"Entry" +---|"Exit" +---|"Present" +---|"UF Bathroom Exit" +---|"LF Bathroom Exit" +--- Day = 0 +--- Night = 1 +---|"Day/Night" +---|"Date" +---|"Time" +---|"Led Intensity" +---|"Retention Tank Water Level Low" + +---@alias C3CKnxGroupAddressNameScene +---|"Scene Lower Floor" +---|"Scene Lower Floor Hallway" +---|"Scene Lower Floor Bathroom" +---|"Scene Lower Floor Laundry" +---|"Scene Lower Floor Bedroom 1" +---|"Scene Lower Floor Study" +---|"Scene Lower Floor Bedroom 2" +---|"Scene Lower Floor Technical Room" +---|"Scene Upper Floor" +---|"Scene Upper Floor Hallway" +---|"Scene Upper Floor Bathroom" +---|"Scene Upper Floor Bedroom" +---|"Scene Upper Floor Closet" +---|"Scene Upper Floor Living Room" +---|"Scene Upper Floor Office" +---|"Scene Upper Floor Dining Room" +---|"Scene Upper Floor Kitchen" +---|"Scene Upper Floor Pantry" +---|"Scene Staircase" +---|"Scene Swimming Pool" +---|"Scene Ventilation Valves" +---|"Scene Ventilation Work" + +---@alias C3CKnxGroupAddressNameSwitch +--- Hallway Primary +---|"Switch LF Hallway" +--- Hallway Under staircase +---|"Switch LF Hallway CTRL" +--- Hallway Primary +---|"Switch LF Hallway Light 1" +--- Hallway Primary +---|"Switch LF Hallway Light 2" +--- Hallway Under staircase +---|"Switch LF Hallway Light Below staircase" +--- Hallway Primary +---|"Switch LF Hallway FB" +--- Hallway Under staircase +---|"Switch LF Hallway CTRL FB" +--- Hallway Primary +---|"Switch LF Hallway Light 1 FB" +--- Hallway Primary +---|"Switch LF Hallway Light 2 FB" +--- Hallway Under staircase +---|"Switch LF Hallway Light Below staircase FB" +--- Bathroom Primary +---|"Switch LF Bathroom Primary" +--- Bathroom Vanity +---|"Switch LF Bathroom Primary CTRL" +--- Bathroom Primary +---|"Switch LF Bathroom Primary Light 1" +--- Bathroom Primary +---|"Switch LF Bathroom Primary Light 2" +--- Bathroom Vanity +---|"Switch LF Bathroom Primary Light Vanity" +---|"Switch LF Bathroom Primary Ceiling" +--- Bathroom Primary +---|"Switch LF Bathroom Primary FB" +--- Bathroom Vanity +---|"Switch LF Bathroom Primary CTRL FB" +--- Bathroom Primary +---|"Switch LF Bathroom Primary Light 1 FB" +--- Bathroom Primary +---|"Switch LF Bathroom Primary Light 2 FB" +--- Bathroom Vanity +---|"Switch LF Bathroom Primary Light Vanity FB" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient CTRL" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Light 1" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Light 2" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Toilet" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient FB" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient CTRL FB" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Light 1 FB" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Light 2 FB" +--- Bathroom Ambient +---|"Switch LF Bathroom Ambient Toilet FB" +--- Laundry Primary +---|"Switch LF Laundry Primary" +--- Laundry Primary +---|"Switch LF Laundry Primary CTRL" +--- Laundry Primary +---|"Switch LF Laundry Primary Light 1" +--- Laundry Primary +---|"Switch LF Laundry Primary Light 2" +--- Laundry Primary +---|"Switch LF Laundry Primary FB" +--- Laundry Primary +---|"Switch LF Laundry Primary CTRL FB" +--- Laundry Primary +---|"Switch LF Laundry Primary Light 1 FB" +--- Laundry Primary +---|"Switch LF Laundry Primary Light 2 FB" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary" +--- Bedroom 1 Reading light +---|"Switch LF Bedroom 1 Primary CTRL" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary Light 1" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary Light 2" +--- Bedroom 1 Reading light +---|"Switch LF Bedroom 1 Primary Light Reading" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary FB" +---|"Switch LF Bedroom 1 Primary CTRL FB" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary Light 1 FB" +--- Bedroom 1 Primary +---|"Switch LF Bedroom 1 Primary Light 2 FB" +--- Bedroom 1 Reading light +---|"Switch LF Bedroom 1 Primary Light Reading FB" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient CTRL" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient Light 1" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient Light 2" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient FB" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient CTRL FB" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient Light 1 FB" +--- Bedroom 1 Ambient +---|"Switch LF Bedroom 1 Ambient Light 2 FB" +--- Study Primary +---|"Switch LF Study Primary" +--- Study Primary +---|"Switch LF Study Primary CTRL" +--- Study Primary +---|"Switch LF Study Primary Light 1" +--- Study Primary +---|"Switch LF Study Primary Light 2" +--- Study Primary +---|"Switch LF Study Primary FB" +--- Study Primary +---|"Switch LF Study Primary CTRL FB" +--- Study Primary +---|"Switch LF Study Primary Light 1 FB" +--- Study Primary +---|"Switch LF Study Primary Light 2 FB" +--- Study Ambient +---|"Switch LF Study Ambient" +--- Study Ambient +---|"Switch LF Study Ambient CTRL" +--- Study Ambient +---|"Switch LF Study Ambient Light 1" +--- Study Ambient +---|"Switch LF Study Ambient Light 2" +--- Study Ambient +---|"Switch LF Study Ambient FB" +--- Study Ambient +---|"Switch LF Study Ambient CTRL FB" +--- Study Ambient +---|"Switch LF Study Ambient Light 1 FB" +--- Study Ambient +---|"Switch LF Study Ambient Light 2 FB" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary" +--- Bedroom 2 Reading light +---|"Switch LF Bedroom 2 Primary CTRL" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary Light 1" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary Light 2" +--- Bedroom 2 Reading light +---|"Switch LF Bedroom 2 Primary Light Reading" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary FB" +--- Bedroom 2 Reading light +---|"Switch LF Bedroom 2 Primary CTRL FB" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary Light 1 FB" +--- Bedroom 2 Primary +---|"Switch LF Bedroom 2 Primary Light 2 FB" +--- Bedroom 2 Reading light +---|"Switch LF Bedroom 2 Primary Light Reading FB" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient CTRL" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient Light 1" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient Light 2" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient FB" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient CTRL FB" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient Light 1 FB" +--- Bedroom 2 Ambient +---|"Switch LF Bedroom 2 Ambient Light 2 FB" +---|"Switch LF Utility" +---|"Switch LF Utility CTRL" +---|"Switch LF Utility Light 1" +---|"Switch LF Utility Counter" +---|"Switch LF Utility FB" +---|"Switch LF Utility CTRL FB" +---|"Switch LF Utility Light 1 FB" +---|"Switch LF Utility Counter FB" +---|"Switch UF Hallway Entry" +---|"Switch UF Hallway Isle" +---|"Switch UF Hallway CTRL" +---|"Switch UF Hallway Entry 1" +---|"Switch UF Hallway Entry 2" +---|"Switch UF Hallway Isle 1" +---|"Switch UF Hallway Isle 2" +---|"Switch UF Hallway Entry FB" +---|"Switch UF Hallway Isle FB" +---|"Switch UF Hallway CTRL FB" +---|"Switch UF Hallway Entry 1 FB" +---|"Switch UF Hallway Entry 2 FB" +---|"Switch UF Hallway Isle 1 FB" +---|"Switch UF Hallway Isle 2 FB" +---|"Switch UF Bathroom Primary" +---|"Switch UF Bathroom Primary CTRL" +---|"Switch UF Bathroom Primary Light 1" +---|"Switch UF Bathroom Primary Light 2" +---|"Switch UF Bathroom Primary Light Vanity" +---|"Switch UF Bathroom Primary Ceiling" +---|"Switch UF Bathroom Primary FB" +---|"Switch UF Bathroom Primary CTRL FB" +---|"Switch UF Bathroom Primary Light 1 FB" +---|"Switch UF Bathroom Primary Light 2 FB" +---|"Switch UF Bathroom Primary Light Vanity FB" +---|"Switch UF Bathroom Vanity CTRL" +---|"Switch UF Bathroom Vanity CTRL FB" +---|"Switch UF Bathroom Ambient" +---|"Switch UF Bathroom Ambient CTRL" +---|"Switch UF Bathroom Ambient Light 1" +---|"Switch UF Bathroom Ambient Light 2" +---|"Switch UF Bathroom Ambient Toilet" +---|"Switch UF Bathroom Ambient FB" +---|"Switch UF Bathroom Ambient CTRL FB" +---|"Switch UF Bathroom Ambient Light 1 FB" +---|"Switch UF Bathroom Ambient Light 2 FB" +---|"Switch UF Bathroom Ambient Toilet FB" +---|"Switch UF Bedroom Primary" +---|"Switch UF Bedroom Primary CTRL" +---|"Switch UF Bedroom Primary Light 1" +---|"Switch UF Bedroom Primary Light 2" +---|"Switch UF Bedroom Primary Reading 1" +---|"Switch UF Bedroom Primary Reading 2" +---|"Switch UF Bedroom Primary FB" +---|"Switch UF Bedroom Primary CTRL FB" +---|"Switch UF Bedroom Primary Light 1 FB" +---|"Switch UF Bedroom Primary Light 2 FB" +---|"Switch UF Bedroom Primary Reading 1 FB" +---|"Switch UF Bedroom Primary Reading 2 FB" +---|"Switch UF Bedroom Ambient" +---|"Switch UF Bedroom Ambient CTRL" +---|"Switch UF Bedroom Ambient Light 1" +---|"Switch UF Bedroom Ambient Light 2" +---|"Switch UF Bedroom Ambient FB" +---|"Switch UF Bedroom Ambient CTRL FB" +---|"Switch UF Bedroom Ambient Light 1 FB" +---|"Switch UF Bedroom Ambient Light 2 FB" +---|"Switch UF Closet" +---|"Switch UF Closet CTRL" +---|"Switch UF Closet Light 1" +---|"Switch UF Closet Light 2" +---|"Switch UF Closet FB" +---|"Switch UF Closet CTRL FB" +---|"Switch UF Closet Light 1 FB" +---|"Switch UF Closet Light 2 FB" +---|"Switch UF Living room Primary" +---|"Switch UF Living room Primary CTRL" +---|"Switch UF Living room Primary Light 1" +---|"Switch UF Living room Primary Light 2" +---|"Switch UF Living room Primary Light 3" +---|"Switch UF Living room Primary FB" +---|"Switch UF Living room Primary CTRL FB" +---|"Switch UF Living room Primary Light 1 FB" +---|"Switch UF Living room Primary Light 2 FB" +---|"Switch UF Living room Primary Light 3 FB" +---|"Switch UF Living room Ambient" +---|"Switch UF Living room Ambient CTRL" +---|"Switch UF Living room Ambient Light 1" +---|"Switch UF Living room Ambient Light 2" +---|"Switch UF Living room Ambient FB" +---|"Switch UF Living room Ambient CTRL FB" +---|"Switch UF Living room Ambient Light 1 FB" +---|"Switch UF Living room Ambient Light 2 FB" +---|"Switch UF Office Primary" +---|"Switch UF Office Primary CTRL" +---|"Switch UF Office Primary Light 1" +---|"Switch UF Office Primary Light 2" +---|"Switch UF Office Primary FB" +---|"Switch UF Office Primary CTRL FB" +---|"Switch UF Office Primary Light 1 FB" +---|"Switch UF Office Primary Light 2 FB" +---|"Switch UF Dining room" +---|"Switch UF Dining room CTRL" +---|"Switch UF Dining room Light 1" +---|"Switch UF Dining room Light 2" +---|"Switch UF Dining room FB" +---|"Switch UF Dining room CTRL FB" +---|"Switch UF Dining room Light 1 FB" +---|"Switch UF Dining room Light 2 FB" +---|"Switch UF Kitchen Primary" +---|"Switch UF Kitchen Primary CTRL" +---|"Switch UF Kitchen Primary Light 1" +---|"Switch UF Kitchen Primary Light 2" +---|"Switch UF Kitchen Primary Light 3" +---|"Switch UF Kitchen Primary FB" +---|"Switch UF Kitchen Primary CTRL FB" +---|"Switch UF Kitchen Primary Light 1 FB" +---|"Switch UF Kitchen Primary Light 2 FB" +---|"Switch UF Kitchen Primary Light 3 FB" +---|"Switch UF Kitchen Ambient" +---|"Switch UF Kitchen Ambient CTRL" +---|"Switch UF Kitchen Ambient Light 1" +---|"Switch UF Kitchen Ambient Light 2" +---|"Switch UF Kitchen Ambient Light 3" +---|"Switch UF Kitchen Ambient FB" +---|"Switch UF Kitchen Ambient CTRL FB" +---|"Switch UF Kitchen Ambient Light 1 FB" +---|"Switch UF Kitchen Ambient Light 2 FB" +---|"Switch UF Kitchen Ambient Light 3 FB" +---|"Switch UF Kitchen Counter" +---|"Switch UF Kitchen Counter CTRL" +---|"Switch UF Kitchen Counter Light 1" +---|"Switch UF Kitchen Counter Light 2" +---|"Switch UF Kitchen Counter Light 3" +---|"Switch UF Kitchen Counter FB" +---|"Switch UF Kitchen Counter CTRL FB" +---|"Switch UF Kitchen Counter Light 1 FB" +---|"Switch UF Kitchen Counter Light 2 FB" +---|"Switch UF Kitchen Counter Light 3 FB" +---|"Switch UF Pantry" +---|"Switch UF Pantry CTRL" +---|"Switch UF Pantry Light 1" +---|"Switch UF Pantry Light 2" +---|"Switch UF Pantry FB" +---|"Switch UF Pantry CTRL FB" +---|"Switch UF Pantry Light 1 FB" +---|"Switch UF Pantry Light 2 FB" +---|"Switch Staircase" +---|"Switch Staircase CTRL" +--- Staircase Primary +---|"Switch Staircase Tree" +--- Staircase Primary +---|"Switch Staircase Handrail" +---|"Switch Staircase FB" +---|"Switch Staircase CTRL FB" +--- Staircase Primary +---|"Switch Staircase Tree FB" +--- Staircase Primary +---|"Switch Staircase Handrail FB" +--- 20_Garden Terrace +---|"Switch EXT Terrace" +--- 20_Garden Terrace +---|"Switch EXT Terrace FB" +--- 20_Exterior Entrance +---|"Switch EXT Entrance" +--- 20_Exterior Entrance +---|"Switch EXT Entrance FB" +--- Parking place Primary +---|"Switch Parking place" +--- Parking place Primary +---|"Switch Parking place FB" +---|"Switch Parking place AC Plug Right of Door" +---|"Switch Parking place AC Plug Right of Door State" +---|"Switch Underwater Light" +---|"Switch Underwater Light FB" +---|"Switch Filtration" +---|"Switch Filtration FB" + +---@alias C3CKnxGroupAddressNameVent +--- HAS UNKNOWN DPT +---|"Vent LF PLACEHOLDER Hallway" +---|"Vent LF Hallway Valve IN" +---|"Vent LF Hallway Valve IN FB" +---|"Vent LF Bathroom CO2 FB" +---|"Vent LF Bathroom Humidity FB" +---|"Vent LF Bathroom boost" +---|"Vent LF Bathroom boost FB" +---|"Vent LF Bathroom Valve IN" +---|"Vent LF Bathroom Valve IN FB" +---|"Vent LF Bathroom Valve OUT" +---|"Vent LF Bathroom Valve OUT FB" +--- HAS UNKNOWN DPT +---|"Vent LF PLACEHOLDER Laundry" +---|"Vent LF Bedroom 1 CO2 FB" +---|"Vent LF Bedroom 1 Humidity FB" +---|"Vent LF Bedroom 1 Valve IN" +---|"Vent LF Bedroom 1 Valve IN FB" +---|"Vent LF Study CO2 FB" +---|"Vent LF Study Humidity FB" +---|"Vent LF Study Valve IN" +---|"Vent LF Study Valve IN FB" +---|"Vent LF Study Valve IN Lock" +---|"Vent LF Bedroom 2 CO2 FB" +---|"Vent LF Bedroom 2 Humidity FB" +---|"Vent LF Bedroom 2 Valve IN" +---|"Vent LF Bedroom 2 Valve IN FB" +--- HAS UNKNOWN DPT +---|"Vent LF Technical Room CO2 FB" +---|"Vent LF Technical Room Humidity FB" +---|"Vent LF Box Valve OUT" +---|"Vent LF Box Valve OUT FB" +---|"Vent LF Box Valve OUT Lock" +--- HAS UNKNOWN DPT +---|"Vent UF PLACEHOLDER Hallway" +---|"Vent UF Bathroom CO2 FB" +---|"Vent UF Bathroom Humidity FB" +---|"Vent UF Bathroom boost" +---|"Vent UF Bathroom boost FB" +---|"Vent UF Bathroom Valve IN" +---|"Vent UF Bathroom Valve IN FB" +---|"Vent UF Bathroom Valve IN Lock" +--- HAS UNKNOWN DPT +---|"Vent UF PLACEHOLDER Bedroom" +---|"Vent UF Bedroom Valve IN" +---|"Vent UF Bedroom Valve IN FB" +---|"Vent UF Bedroom Valve IN Lock" +---|"Vent UF Bedroom CO2 FB" +---|"Vent UF Bedroom Humidity FB" +---|"Vent UF Living Room CO2 FB" +---|"Vent UF Living Room Humidity FB" +---|"Vent UF Living Room Valve IN" +---|"Vent UF Living Room Valve IN FB" +--- HAS UNKNOWN DPT +---|"Vent UF PLACEHOLDER Office" +---|"Vent UF Office Valve IN" +---|"Vent UF Office Valve IN FB" +---|"Vent UF Bathroom Valve OUT" +---|"Vent UF Bathroom Valve OUT FB" +---|"Vent UF Bathroom Valve OUT Lock" +--- HAS UNKNOWN DPT +---|"Vent UF PLACEHOLDER Dining Room" +--- HAS UNKNOWN DPT +---|"Vent UF PLACEHOLDER Kitchen" +---|"Vent UF Kitchen Valve OUT" +---|"Vent UF Kitchen Valve OUT FB" +---|"Vent UF Kitchen CO2 FB" +---|"Vent UF Kitchen Humidity FB" +---|"Vent UF Kitchen TVOC FB" +---|"Vent UF Kitchen boost" +---|"Vent UF Kitchen boost FB" +---|"Vent UF Closet+Hallway Valve OUT" +---|"Vent UF Closet+Hallway Valve OUT FB" +---|"Vent UF Closet+Hallway Valve OUT Lock" +---|"Vent Airflow" +---|"Vent Standby FB" +---|"Vent Temperature: Supply" +---|"Vent Temperature: Outdoor" +---|"Vent Temperature: Extract" +---|"Vent Temperature: Exhaust" +---|"Vent Humidity: Supply" +---|"Vent Humidity: Outdoor" +---|"Vent Humidity: Extract" +---|"Vent Humidity: Exhaust" +---|"Vent Filter: Remaining (h)" +---|"Vent Filter: Replace" +---|"Vent Heating-Cooling" +---|"Vent Heating-Cooling State" +---|"Vent Boost" +---|"Vent Boost FB" +---|"Vent Auto mode" +---|"Vent Auto mode FB" +---|"Vent Preset" +---|"Vent Preset FB" +---|"Vent Preset Away" +---|"Vent Preset Away FB" +---|"Vent Away function" +---|"Vent Away function FB" +---|"Vent Temperature profile mode" +---|"Vent Temperature profile mode FB" +---|"Vent Temperature profile" +---|"Vent Temperature profile FB" +---|"Vent Boost Time" +---|"Vent Boost Time FB" +---|"Vent External setpoint" +---|"Vent External setpoint FB" +---|"Vent Error" +---|"Vent Status" diff --git a/src/control4-utils/knx/gen/createGroupAddresses.lua b/src/control4-utils/knx/gen/createGroupAddresses.lua new file mode 100644 index 0000000..8acfe14 --- /dev/null +++ b/src/control4-utils/knx/gen/createGroupAddresses.lua @@ -0,0 +1,628 @@ + +---@return table +function C3CKnxCreateGroupAddresses() + return { + C3C.KnxGroupAddress("Entry", "0/0/1", "DPT_1"), + C3C.KnxGroupAddress("Exit", "0/0/2", "DPT_1"), + C3C.KnxGroupAddress("Present", "0/0/3", "DPT_1"), + C3C.KnxGroupAddress("UF Bathroom Exit", "0/0/50", "DPT_1"), + C3C.KnxGroupAddress("LF Bathroom Exit", "0/0/100", "DPT_1"), + C3C.KnxGroupAddress("Day/Night", "0/1/0", "DPT_1"), + C3C.KnxGroupAddress("Date", "0/1/1", "DPT_11"), + C3C.KnxGroupAddress("Time", "0/1/2", "DPT_10"), + C3C.KnxGroupAddress("Led Intensity", "0/1/100", "DPT_5"), + C3C.KnxGroupAddress("Retention Tank Water Level Low", "0/1/200", "DPT_1"), + C3C.KnxGroupAddress("Scene Lower Floor", "0/2/100", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Hallway", "0/2/101", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Bathroom", "0/2/102", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Laundry", "0/2/103", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Bedroom 1", "0/2/104", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Study", "0/2/105", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Bedroom 2", "0/2/106", "DPT_17"), + C3C.KnxGroupAddress("Scene Lower Floor Technical Room", "0/2/107", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor", "0/2/150", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Hallway", "0/2/151", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Bathroom", "0/2/152", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Bedroom", "0/2/153", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Closet", "0/2/154", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Living Room", "0/2/155", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Office", "0/2/156", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Dining Room", "0/2/157", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Kitchen", "0/2/158", "DPT_17"), + C3C.KnxGroupAddress("Scene Upper Floor Pantry", "0/2/159", "DPT_17"), + C3C.KnxGroupAddress("Scene Staircase", "0/2/200", "DPT_17"), + C3C.KnxGroupAddress("Scene Swimming Pool", "0/2/210", "DPT_17"), + C3C.KnxGroupAddress("Scene Ventilation Valves", "0/2/220", "DPT_17"), + C3C.KnxGroupAddress("Scene Ventilation Work", "0/2/221", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway", "1/0/0", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway CTRL", "1/0/1", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light 1", "1/0/2", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light 2", "1/0/3", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light Below staircase", "1/0/4", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway FB", "1/0/7", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway CTRL FB", "1/0/8", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light 1 FB", "1/0/9", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light 2 FB", "1/0/10", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Hallway Light Below staircase FB", "1/0/11", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary", "1/0/20", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary CTRL", "1/0/21", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light 1", "1/0/22", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light 2", "1/0/23", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light Vanity", "1/0/24", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Ceiling", "1/0/25", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary FB", "1/0/27", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary CTRL FB", "1/0/28", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light 1 FB", "1/0/29", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light 2 FB", "1/0/30", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Primary Light Vanity FB", "1/0/31", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient", "1/0/35", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient CTRL", "1/0/36", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Light 1", "1/0/37", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Light 2", "1/0/38", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Toilet", "1/0/39", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient FB", "1/0/42", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient CTRL FB", "1/0/43", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Light 1 FB", "1/0/44", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Light 2 FB", "1/0/45", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bathroom Ambient Toilet FB", "1/0/46", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary", "1/0/70", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary CTRL", "1/0/71", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary Light 1", "1/0/72", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary Light 2", "1/0/73", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary FB", "1/0/77", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary CTRL FB", "1/0/78", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary Light 1 FB", "1/0/79", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Laundry Primary Light 2 FB", "1/0/80", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary", "1/0/90", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary CTRL", "1/0/91", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light 1", "1/0/92", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light 2", "1/0/93", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light Reading", "1/0/94", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary FB", "1/0/97", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary CTRL FB", "1/0/98", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light 1 FB", "1/0/99", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light 2 FB", "1/0/100", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Primary Light Reading FB", "1/0/101", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient", "1/0/105", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient CTRL", "1/0/106", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient Light 1", "1/0/107", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient Light 2", "1/0/108", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient FB", "1/0/112", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient CTRL FB", "1/0/113", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient Light 1 FB", "1/0/114", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 1 Ambient Light 2 FB", "1/0/115", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary", "1/0/140", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary CTRL", "1/0/141", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary Light 1", "1/0/142", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary Light 2", "1/0/143", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary FB", "1/0/147", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary CTRL FB", "1/0/148", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary Light 1 FB", "1/0/149", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Primary Light 2 FB", "1/0/150", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient", "1/0/155", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient CTRL", "1/0/156", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient Light 1", "1/0/157", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient Light 2", "1/0/158", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient FB", "1/0/162", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient CTRL FB", "1/0/163", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient Light 1 FB", "1/0/164", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Study Ambient Light 2 FB", "1/0/165", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary", "1/0/190", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary CTRL", "1/0/191", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light 1", "1/0/192", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light 2", "1/0/193", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light Reading", "1/0/194", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary FB", "1/0/197", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary CTRL FB", "1/0/198", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light 1 FB", "1/0/199", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light 2 FB", "1/0/200", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Primary Light Reading FB", "1/0/201", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient", "1/0/205", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient CTRL", "1/0/206", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient Light 1", "1/0/207", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient Light 2", "1/0/208", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient FB", "1/0/212", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient CTRL FB", "1/0/213", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient Light 1 FB", "1/0/214", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Bedroom 2 Ambient Light 2 FB", "1/0/215", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility", "1/0/225", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility CTRL", "1/0/226", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility Light 1", "1/0/227", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility Counter", "1/0/228", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility FB", "1/0/232", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility CTRL FB", "1/0/233", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility Light 1 FB", "1/0/234", "DPT_1"), + C3C.KnxGroupAddress("Switch LF Utility Counter FB", "1/0/235", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry", "1/1/0", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle", "1/1/1", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway CTRL", "1/1/2", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry 1", "1/1/3", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry 2", "1/1/4", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle 1", "1/1/5", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle 2", "1/1/6", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry FB", "1/1/7", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle FB", "1/1/8", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway CTRL FB", "1/1/9", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry 1 FB", "1/1/10", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Entry 2 FB", "1/1/11", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle 1 FB", "1/1/12", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Hallway Isle 2 FB", "1/1/13", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary", "1/1/20", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary CTRL", "1/1/21", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light 1", "1/1/22", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light 2", "1/1/23", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light Vanity", "1/1/24", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Ceiling", "1/1/25", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary FB", "1/1/27", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary CTRL FB", "1/1/28", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light 1 FB", "1/1/29", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light 2 FB", "1/1/30", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Primary Light Vanity FB", "1/1/31", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Vanity CTRL", "1/1/33", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Vanity CTRL FB", "1/1/34", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient", "1/1/35", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient CTRL", "1/1/36", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Light 1", "1/1/37", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Light 2", "1/1/38", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Toilet", "1/1/39", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient FB", "1/1/42", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient CTRL FB", "1/1/43", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Light 1 FB", "1/1/44", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Light 2 FB", "1/1/45", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bathroom Ambient Toilet FB", "1/1/46", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary", "1/1/50", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary CTRL", "1/1/51", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Light 1", "1/1/52", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Light 2", "1/1/53", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Reading 1", "1/1/54", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Reading 2", "1/1/55", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary FB", "1/1/57", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary CTRL FB", "1/1/58", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Light 1 FB", "1/1/59", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Light 2 FB", "1/1/60", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Reading 1 FB", "1/1/61", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Primary Reading 2 FB", "1/1/62", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient", "1/1/65", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient CTRL", "1/1/66", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient Light 1", "1/1/67", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient Light 2", "1/1/68", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient FB", "1/1/72", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient CTRL FB", "1/1/73", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient Light 1 FB", "1/1/74", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Bedroom Ambient Light 2 FB", "1/1/75", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet", "1/1/80", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet CTRL", "1/1/81", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet Light 1", "1/1/82", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet Light 2", "1/1/83", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet FB", "1/1/87", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet CTRL FB", "1/1/88", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet Light 1 FB", "1/1/89", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Closet Light 2 FB", "1/1/90", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary", "1/1/95", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary CTRL", "1/1/96", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 1", "1/1/97", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 2", "1/1/98", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 3", "1/1/99", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary FB", "1/1/102", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary CTRL FB", "1/1/103", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 1 FB", "1/1/104", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 2 FB", "1/1/105", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Primary Light 3 FB", "1/1/106", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient", "1/1/110", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient CTRL", "1/1/111", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient Light 1", "1/1/112", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient Light 2", "1/1/113", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient FB", "1/1/117", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient CTRL FB", "1/1/118", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient Light 1 FB", "1/1/119", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Living room Ambient Light 2 FB", "1/1/120", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary", "1/1/125", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary CTRL", "1/1/126", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary Light 1", "1/1/127", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary Light 2", "1/1/128", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary FB", "1/1/132", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary CTRL FB", "1/1/133", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary Light 1 FB", "1/1/134", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Office Primary Light 2 FB", "1/1/135", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room", "1/1/140", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room CTRL", "1/1/141", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room Light 1", "1/1/142", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room Light 2", "1/1/143", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room FB", "1/1/147", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room CTRL FB", "1/1/148", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room Light 1 FB", "1/1/149", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Dining room Light 2 FB", "1/1/150", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary", "1/1/155", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary CTRL", "1/1/156", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 1", "1/1/157", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 2", "1/1/158", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 3", "1/1/159", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary FB", "1/1/162", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary CTRL FB", "1/1/163", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 1 FB", "1/1/164", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 2 FB", "1/1/165", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Primary Light 3 FB", "1/1/166", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient", "1/1/170", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient CTRL", "1/1/171", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 1", "1/1/172", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 2", "1/1/173", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 3", "1/1/174", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient FB", "1/1/177", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient CTRL FB", "1/1/178", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 1 FB", "1/1/179", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 2 FB", "1/1/180", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Ambient Light 3 FB", "1/1/181", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter", "1/1/185", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter CTRL", "1/1/186", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 1", "1/1/187", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 2", "1/1/188", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 3", "1/1/189", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter FB", "1/1/192", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter CTRL FB", "1/1/193", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 1 FB", "1/1/194", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 2 FB", "1/1/195", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Kitchen Counter Light 3 FB", "1/1/196", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry", "1/1/200", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry CTRL", "1/1/201", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry Light 1", "1/1/202", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry Light 2", "1/1/203", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry FB", "1/1/207", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry CTRL FB", "1/1/208", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry Light 1 FB", "1/1/209", "DPT_1"), + C3C.KnxGroupAddress("Switch UF Pantry Light 2 FB", "1/1/210", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase", "1/2/0", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase CTRL", "1/2/1", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase Tree", "1/2/2", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase Handrail", "1/2/3", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase FB", "1/2/7", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase CTRL FB", "1/2/8", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase Tree FB", "1/2/9", "DPT_1"), + C3C.KnxGroupAddress("Switch Staircase Handrail FB", "1/2/10", "DPT_1"), + C3C.KnxGroupAddress("Switch EXT Terrace", "1/3/0", "DPT_1"), + C3C.KnxGroupAddress("Switch EXT Terrace FB", "1/3/7", "DPT_1"), + C3C.KnxGroupAddress("Switch EXT Entrance", "1/3/20", "DPT_1"), + C3C.KnxGroupAddress("Switch EXT Entrance FB", "1/3/27", "DPT_1"), + C3C.KnxGroupAddress("Switch Parking place", "1/4/0", "DPT_1"), + C3C.KnxGroupAddress("Switch Parking place FB", "1/4/7", "DPT_1"), + C3C.KnxGroupAddress("Switch Parking place AC Plug Right of Door", "1/4/10", "DPT_1"), + C3C.KnxGroupAddress("Switch Parking place AC Plug Right of Door State", "1/4/17", "DPT_1"), + C3C.KnxGroupAddress("Switch Underwater Light", "1/5/0", "DPT_1"), + C3C.KnxGroupAddress("Switch Underwater Light FB", "1/5/1", "DPT_1"), + C3C.KnxGroupAddress("Switch Filtration", "1/5/10", "DPT_1"), + C3C.KnxGroupAddress("Switch Filtration FB", "1/5/11", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Hallway", "2/0/0", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Hallway FB", "2/0/1", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Hallway Dimmer", "2/0/2", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Hallway Color", "2/0/5", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Hallway Color FB", "2/0/6", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Hallway Color Dimmer", "2/0/7", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary", "2/0/20", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary FB", "2/0/21", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary Dimmer", "2/0/22", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary Color", "2/0/25", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary Color FB", "2/0/26", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Primary Color Dimmer", "2/0/27", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient", "2/0/35", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient FB", "2/0/36", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient Dimmer", "2/0/37", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient HSV", "2/0/40", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient HSV FB", "2/0/41", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient Hue Dimming", "2/0/42", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Ambient Saturation Dimming", "2/0/43", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity", "2/0/50", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity FB", "2/0/51", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity Dimmer", "2/0/52", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity Color", "2/0/53", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity Color FB", "2/0/54", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bathroom Vanity Color Dimmer", "2/0/55", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Laundry Primary", "2/0/70", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Laundry Primary FB", "2/0/71", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Laundry Primary Dimmer", "2/0/72", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Laundry Color", "2/0/75", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Laundry Color FB", "2/0/76", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Laundry Color Dimmer", "2/0/77", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary", "2/0/90", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary FB", "2/0/91", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary Dimmer", "2/0/92", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary Color", "2/0/95", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary Color FB", "2/0/96", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Primary Color Dimmer", "2/0/97", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient", "2/0/105", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient FB", "2/0/106", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient Dimmer", "2/0/107", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient HSV", "2/0/110", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient HSV FB", "2/0/111", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient Hue Dimming", "2/0/112", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 1 Ambient Saturation Dimming", "2/0/113", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Primary", "2/0/140", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Study Primary FB", "2/0/141", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Study Primary Dimmer", "2/0/142", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Study Color", "2/0/145", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Study Color FB", "2/0/146", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Study Color Dimmer", "2/0/147", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient", "2/0/155", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient FB", "2/0/156", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient Dimmer", "2/0/157", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient HSV", "2/0/160", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient HSV FB", "2/0/161", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient Hue Dimming", "2/0/162", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Study Ambient Saturation Dimming", "2/0/163", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary", "2/0/190", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary FB", "2/0/191", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary Dimmer", "2/0/192", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary Color", "2/0/195", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary Color FB", "2/0/196", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Primary Color Dimmer", "2/0/197", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient", "2/0/205", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient FB", "2/0/206", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient Dimmer", "2/0/207", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient HSV", "2/0/210", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient HSV FB", "2/0/211", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient Hue Dimming", "2/0/212", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Bedroom 2 Ambient Saturation Dimming", "2/0/213", "DPT_1"), + C3C.KnxGroupAddress("Dimmer LF Utility", "2/0/225", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Utility FB", "2/0/226", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Utility Dimmer", "2/0/227", "DPT_3"), + C3C.KnxGroupAddress("Dimmer LF Utility Color", "2/0/230", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Utility Color FB", "2/0/231", "DPT_5"), + C3C.KnxGroupAddress("Dimmer LF Utility Color Dimmer", "2/0/232", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Hallway", "2/1/0", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Hallway FB", "2/1/1", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Hallway Dimming", "2/1/2", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Hallway Color", "2/1/5", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Hallway Color FB", "2/1/6", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Hallway Color Dimming", "2/1/7", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary", "2/1/15", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary FB", "2/1/16", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary Dimming", "2/1/17", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary Color", "2/1/20", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary Color FB", "2/1/21", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Primary Color Dimming", "2/1/22", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient", "2/1/30", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient FB", "2/1/31", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient Dimming", "2/1/32", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient HSV", "2/1/35", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient HSV FB", "2/1/36", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient Hue Dimming", "2/1/37", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Ambient Saturation Dimming", "2/1/38", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity", "2/1/45", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity FB", "2/1/46", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity Dimming", "2/1/47", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity Color", "2/1/50", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity Color FB", "2/1/51", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bathroom Vanity Color Dimming", "2/1/52", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary", "2/1/65", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary FB", "2/1/66", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary Dimming", "2/1/67", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary Color", "2/1/70", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary Color FB", "2/1/71", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Primary Color Dimming", "2/1/72", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient", "2/1/80", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient FB", "2/1/81", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient Dimming", "2/1/82", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient HSV", "2/1/85", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient HSV FB", "2/1/86", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient Hue Dimming", "2/1/87", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Bedroom Ambient Saturation Dimming", "2/1/88", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Closet", "2/1/95", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Closet FB", "2/1/96", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Closet Dimming", "2/1/97", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Closet Color", "2/1/100", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Closet Color FB", "2/1/101", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Closet Color Dimming", "2/1/102", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary", "2/1/105", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary FB", "2/1/106", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary Dimming", "2/1/107", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary Color", "2/1/110", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary Color FB", "2/1/111", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Primary Color Dimming", "2/1/112", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient", "2/1/120", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient FB", "2/1/121", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient Dimming", "2/1/122", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient HSV", "2/1/125", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient HSV FB", "2/1/126", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient Hue Dimming", "2/1/127", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Living room Ambient Saturation Dimming", "2/1/128", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Office Primary", "2/1/135", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Office Primary FB", "2/1/136", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Office Primary Dimming", "2/1/137", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Office Color", "2/1/140", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Office Color FB", "2/1/141", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Office Color Dimming", "2/1/142", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Dining room", "2/1/150", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Dining room FB", "2/1/151", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Dining room Dimming", "2/1/152", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Dining Room Color", "2/1/155", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Dining Room Color FB", "2/1/156", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Dining Room Color Dimming", "2/1/157", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary", "2/1/165", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary FB", "2/1/166", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary Dimming", "2/1/167", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary Color", "2/1/170", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary Color FB", "2/1/171", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Primary Color Dimming", "2/1/172", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient", "2/1/180", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient FB", "2/1/181", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient Dimming", "2/1/182", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient HSV", "2/1/185", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient HSV FB", "2/1/186", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient Hue Dimming", "2/1/187", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Ambient Saturation Dimming", "2/1/188", "DPT_1"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter", "2/1/195", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter FB", "2/1/196", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter Dimming", "2/1/197", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter Color", "2/1/200", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter Color FB", "2/1/201", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Kitchen Counter Color Dimming", "2/1/202", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Pantry", "2/1/210", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Pantry FB", "2/1/211", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Pantry Dimming", "2/1/212", "DPT_3"), + C3C.KnxGroupAddress("Dimmer UF Pantry Color", "2/1/215", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Pantry Color FB", "2/1/216", "DPT_5"), + C3C.KnxGroupAddress("Dimmer UF Pantry Color Dimming", "2/1/217", "DPT_3"), + C3C.KnxGroupAddress("Dimmer Staircase Tree Dimming", "2/2/0", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase Tree Value", "2/2/1", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase Tree Value Feedback", "2/2/2", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail Swtich", "2/2/20", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail Value", "2/2/21", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail Value Feedback", "2/2/22", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail bottom Dimming", "2/2/35", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail bottom Value", "2/2/36", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Staircase handrail bottom Value Feedback", "2/2/37", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Terrace Dimming", "2/3/0", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Terrace Value", "2/3/1", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Terrace Value Feedback", "2/3/2", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Entrance Value Feedback", "2/3/20", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Entrance Dimming", "2/3/21", "DPT_1"), + C3C.KnxGroupAddress("Dimmer EXT Entrance Value", "2/3/22", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Parking place Dimming", "2/4/0", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Parking place Value", "2/4/1", "DPT_1"), + C3C.KnxGroupAddress("Dimmer Parking place Value Feedback", "2/4/2", "DPT_1"), + C3C.KnxGroupAddress("Heat LF Hallway Temperature FB", "3/0/0", "DPT_9"), + C3C.KnxGroupAddress("Heat LF Bathroom Temperature FB", "3/0/30", "DPT_9"), + C3C.KnxGroupAddress("Heat LF Bathroom Ladder boost", "3/0/40", "DPT_1"), + C3C.KnxGroupAddress("Heat LF Bathroom Ladder boost FB", "3/0/41", "DPT_1"), + C3C.KnxGroupAddress("Heat LF Laundry Temperature FB", "3/0/60", "DPT_1"), + C3C.KnxGroupAddress("Heat LF Bedroom 1 Temperature FB", "3/0/90", "DPT_9"), + C3C.KnxGroupAddress("Heat LF Study Temperature FB", "3/0/120", "DPT_9"), + C3C.KnxGroupAddress("Heat LF Bedroom 2 Temperature FB", "3/0/150", "DPT_9"), + C3C.KnxGroupAddress("Heat LF Technical Room Temperature FB", "3/0/180", "DPT_9"), + C3C.KnxGroupAddress("Heat UF Hallway Temperature FB", "3/1/0", "DPT_9"), + C3C.KnxGroupAddress("Heat UF Bathroom Temperature FB", "3/1/30", "DPT_9"), + C3C.KnxGroupAddress("Heat UF Bathroom Ladder boost", "3/1/40", "DPT_1"), + C3C.KnxGroupAddress("Heat UF Bathroom Ladder boost FB", "3/1/41", "DPT_1"), + C3C.KnxGroupAddress("Heat UF Bedroom Temperature FB", "3/1/60", "DPT_9"), + C3C.KnxGroupAddress("Heat UF Living Room Temperature FB", "3/1/90", "DPT_9"), + C3C.KnxGroupAddress("Heat UF Kitchen Temperature FB", "3/1/120", "DPT_9"), + C3C.KnxGroupAddress("Heat Circulation Pump", "3/5/0", "DPT_1"), + C3C.KnxGroupAddress("Heat Circulation Pump FB", "3/5/1", "DPT_1"), + C3C.KnxGroupAddress("Heat Hot Water Circulation State", "3/6/2", "DPT_1"), + C3C.KnxGroupAddress("Heat Hot Water Circulation Emergency State", "3/6/3", "DPT_1"), + C3C.KnxGroupAddress("Heat Hot Water Circulation Control Value", "3/6/4", "DPT_5"), + C3C.KnxGroupAddress("Heat Heating-Cooling", "3/7/0", "DPT_1"), + C3C.KnxGroupAddress("Heat Heating-Cooling State", "3/7/1", "DPT_1"), + C3C.KnxGroupAddress("Heat Temperature Setpoint", "3/7/2", "DPT_1"), + C3C.KnxGroupAddress("Heat Temperature Setpoint State", "3/7/3", "DPT_1"), + C3C.KnxGroupAddress("Heat Heating Off lock", "3/7/4", "DPT_1"), + C3C.KnxGroupAddress("Heat UF+LF Heating Circulation Pump", "3/7/20", "DPT_1"), + C3C.KnxGroupAddress("Heat UF+LF Heating Circulation Pump State", "3/7/21", "DPT_1"), + C3C.KnxGroupAddress("Vent LF PLACEHOLDER Hallway", "4/0/0", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Hallway Valve IN", "4/0/10", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Hallway Valve IN FB", "4/0/11", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bathroom CO2 FB", "4/0/30", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bathroom Humidity FB", "4/0/31", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bathroom boost", "4/0/35", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Bathroom boost FB", "4/0/36", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Bathroom Valve IN", "4/0/40", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bathroom Valve IN FB", "4/0/41", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bathroom Valve OUT", "4/0/42", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bathroom Valve OUT FB", "4/0/43", "DPT_5"), + C3C.KnxGroupAddress("Vent LF PLACEHOLDER Laundry", "4/0/60", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Bedroom 1 CO2 FB", "4/0/90", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bedroom 1 Humidity FB", "4/0/91", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bedroom 1 Valve IN", "4/0/100", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bedroom 1 Valve IN FB", "4/0/101", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Study CO2 FB", "4/0/120", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Study Humidity FB", "4/0/121", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Study Valve IN", "4/0/130", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Study Valve IN FB", "4/0/131", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Study Valve IN Lock", "4/0/132", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Bedroom 2 CO2 FB", "4/0/150", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bedroom 2 Humidity FB", "4/0/151", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Bedroom 2 Valve IN", "4/0/160", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Bedroom 2 Valve IN FB", "4/0/161", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Technical Room CO2 FB", "4/0/180", "DPT_1"), + C3C.KnxGroupAddress("Vent LF Technical Room Humidity FB", "4/0/181", "DPT_9"), + C3C.KnxGroupAddress("Vent LF Box Valve OUT", "4/0/240", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Box Valve OUT FB", "4/0/241", "DPT_5"), + C3C.KnxGroupAddress("Vent LF Box Valve OUT Lock", "4/0/242", "DPT_1"), + C3C.KnxGroupAddress("Vent UF PLACEHOLDER Hallway", "4/1/0", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Bathroom CO2 FB", "4/1/30", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Bathroom Humidity FB", "4/1/31", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Bathroom boost", "4/1/35", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Bathroom boost FB", "4/1/36", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve IN", "4/1/40", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve IN FB", "4/1/41", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve IN Lock", "4/1/42", "DPT_1"), + C3C.KnxGroupAddress("Vent UF PLACEHOLDER Bedroom", "4/1/60", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Bedroom Valve IN", "4/1/70", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bedroom Valve IN FB", "4/1/71", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bedroom Valve IN Lock", "4/1/72", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Bedroom CO2 FB", "4/1/80", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Bedroom Humidity FB", "4/1/81", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Living Room CO2 FB", "4/1/90", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Living Room Humidity FB", "4/1/91", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Living Room Valve IN", "4/1/100", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Living Room Valve IN FB", "4/1/101", "DPT_5"), + C3C.KnxGroupAddress("Vent UF PLACEHOLDER Office", "4/1/120", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Office Valve IN", "4/1/130", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Office Valve IN FB", "4/1/131", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve OUT", "4/1/142", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve OUT FB", "4/1/143", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Bathroom Valve OUT Lock", "4/1/144", "DPT_1"), + C3C.KnxGroupAddress("Vent UF PLACEHOLDER Dining Room", "4/1/150", "DPT_1"), + C3C.KnxGroupAddress("Vent UF PLACEHOLDER Kitchen", "4/1/180", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Kitchen Valve OUT", "4/1/190", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Kitchen Valve OUT FB", "4/1/191", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Kitchen CO2 FB", "4/1/200", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Kitchen Humidity FB", "4/1/201", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Kitchen TVOC FB", "4/1/202", "DPT_9"), + C3C.KnxGroupAddress("Vent UF Kitchen boost", "4/1/205", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Kitchen boost FB", "4/1/206", "DPT_1"), + C3C.KnxGroupAddress("Vent UF Closet+Hallway Valve OUT", "4/1/240", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Closet+Hallway Valve OUT FB", "4/1/241", "DPT_5"), + C3C.KnxGroupAddress("Vent UF Closet+Hallway Valve OUT Lock", "4/1/242", "DPT_1"), + C3C.KnxGroupAddress("Vent Airflow", "4/7/0", "DPT_13"), + C3C.KnxGroupAddress("Vent Standby FB", "4/7/1", "DPT_1"), + C3C.KnxGroupAddress("Vent Temperature: Supply", "4/7/5", "DPT_9"), + C3C.KnxGroupAddress("Vent Temperature: Outdoor", "4/7/6", "DPT_9"), + C3C.KnxGroupAddress("Vent Temperature: Extract", "4/7/7", "DPT_9"), + C3C.KnxGroupAddress("Vent Temperature: Exhaust", "4/7/8", "DPT_9"), + C3C.KnxGroupAddress("Vent Humidity: Supply", "4/7/10", "DPT_9"), + C3C.KnxGroupAddress("Vent Humidity: Outdoor", "4/7/11", "DPT_9"), + C3C.KnxGroupAddress("Vent Humidity: Extract", "4/7/12", "DPT_9"), + C3C.KnxGroupAddress("Vent Humidity: Exhaust", "4/7/13", "DPT_9"), + C3C.KnxGroupAddress("Vent Filter: Remaining (h)", "4/7/25", "DPT_7"), + C3C.KnxGroupAddress("Vent Filter: Replace", "4/7/26", "DPT_1"), + C3C.KnxGroupAddress("Vent Heating-Cooling", "4/7/30", "DPT_1"), + C3C.KnxGroupAddress("Vent Heating-Cooling State", "4/7/31", "DPT_1"), + C3C.KnxGroupAddress("Vent Boost", "4/7/100", "DPT_1"), + C3C.KnxGroupAddress("Vent Boost FB", "4/7/101", "DPT_1"), + C3C.KnxGroupAddress("Vent Auto mode", "4/7/102", "DPT_1"), + C3C.KnxGroupAddress("Vent Auto mode FB", "4/7/103", "DPT_1"), + C3C.KnxGroupAddress("Vent Preset", "4/7/104", "DPT_5"), + C3C.KnxGroupAddress("Vent Preset FB", "4/7/105", "DPT_5"), + C3C.KnxGroupAddress("Vent Preset Away", "4/7/106", "DPT_1"), + C3C.KnxGroupAddress("Vent Preset Away FB", "4/7/107", "DPT_1"), + C3C.KnxGroupAddress("Vent Away function", "4/7/108", "DPT_1"), + C3C.KnxGroupAddress("Vent Away function FB", "4/7/109", "DPT_1"), + C3C.KnxGroupAddress("Vent Temperature profile mode", "4/7/110", "DPT_5"), + C3C.KnxGroupAddress("Vent Temperature profile mode FB", "4/7/111", "DPT_5"), + C3C.KnxGroupAddress("Vent Temperature profile", "4/7/112", "DPT_5"), + C3C.KnxGroupAddress("Vent Temperature profile FB", "4/7/113", "DPT_5"), + C3C.KnxGroupAddress("Vent Boost Time", "4/7/114", "DPT_7"), + C3C.KnxGroupAddress("Vent Boost Time FB", "4/7/115", "DPT_7"), + C3C.KnxGroupAddress("Vent External setpoint", "4/7/116", "DPT_9"), + C3C.KnxGroupAddress("Vent External setpoint FB", "4/7/117", "DPT_9"), + C3C.KnxGroupAddress("Vent Error", "4/7/200", "DPT_1"), + C3C.KnxGroupAddress("Vent Status", "4/7/201", "DPT_5"), + C3C.KnxGroupAddress("Media UF Bathroom Playing", "5/1/100", "DPT_5"), + C3C.KnxGroupAddress("Media UF Bathroom Off", "5/1/101", "DPT_1"), + C3C.KnxGroupAddress("Media UF Bathroom Volume Down", "5/1/103", "DPT_1"), + C3C.KnxGroupAddress("Media UF Bathroom Volume Up", "5/1/104", "DPT_1"), + C3C.KnxGroupAddress("Media UF Bathroom Previous", "5/1/105", "DPT_1"), + C3C.KnxGroupAddress("Media UF Bathroom Next", "5/1/106", "DPT_1"), + C3C.KnxGroupAddress("Media UF Bathroom Preset Cycle Forward", "5/1/107", "DPT_1"), + C3C.KnxGroupAddress("Control LF Hallway Presence", "6/0/0", "DPT_1"), + C3C.KnxGroupAddress("Control LF Hallway Presence Enable", "6/0/1", "DPT_1"), + C3C.KnxGroupAddress("Control LF Hallway Presence Enable Workaround", "6/0/2", "DPT_3"), + C3C.KnxGroupAddress("Control LF Hallway Presence Alarm", "6/0/10", "DPT_1"), + C3C.KnxGroupAddress("Control LF Hallway Presence Alarm State", "6/0/11", "DPT_1"), + C3C.KnxGroupAddress("Control LF Study LED Brightness Night", "6/0/146", "DPT_5"), + C3C.KnxGroupAddress("Control UF Bathroom Presence", "6/1/30", "DPT_1"), + C3C.KnxGroupAddress("Control UF Bathroom Presence Enable", "6/1/31", "DPT_1"), + C3C.KnxGroupAddress("Control UF Bathroom Presence Delayed", "6/1/32", "DPT_1"), + C3C.KnxGroupAddress("Control UF Bathroom Brightness", "6/1/35", "DPT_9"), + } +end + \ No newline at end of file diff --git a/src/driver.lua b/src/driver.lua new file mode 100644 index 0000000..9d71743 --- /dev/null +++ b/src/driver.lua @@ -0,0 +1,52 @@ +local VERSION = "2025.10.0" + +local ID_KNX = 1 + +require("control4-utils.base") +require("control4-utils.hooks") +require("control4-utils.helpers") +require("control4-utils.knx") +C3C.KnxProxy.Setup(ID_KNX) +C3C.RemoteLogger.Setup("c4c-knx-presence") +C3C.Logger.DisableRemoteLogging() +-- C4:AllowExecute(false) +C3C.Lib = {} + +require("lib.createContext") +require("lib.state.index") +require("lib.presence.index") + +Driver = (function() + local class = { + initialized = false, + online = false, + } + + ---@param strCommand string + ---@param tParams table + function class:Execute(strCommand, tParams) + if strCommand == "ONLINE" then + class.online = true + elseif strCommand == "OFFLINE" then + class.online = false + end + end + + function class:SetInitialized() + C3C.RemoteLogger.Info("C3C KNX Presence driver initialized", { version = VERSION }) + class.initialized = true + end + + return class +end)() + +C3C.HookIntoOnDriverLateInit(function() + C3C.OneShotTimer.Add(5000, Driver.SetInitialized, "Initialized") +end) + +C3C.HookIntoExecuteCommand(function(strCommand, tParams) + tParams = tParams or {} + strCommand = strCommand or "" + + Driver:Execute(strCommand, tParams) +end) diff --git a/src/lib/createContext.lua b/src/lib/createContext.lua new file mode 100644 index 0000000..b2bc4bd --- /dev/null +++ b/src/lib/createContext.lua @@ -0,0 +1,14 @@ +--- @param ga GroupAddress +--- @return table +CreateGroupAddressContext = function(ga) + return { + trace = ga.Name .. "-" .. string.format("%d", C4:GetTime()) + } +end + +CreateRoomContext = function(location) + return { + location = location, + time = os.clock(), + } +end diff --git a/src/lib/presence/LF/Hallway.lua b/src/lib/presence/LF/Hallway.lua new file mode 100644 index 0000000..6e0928a --- /dev/null +++ b/src/lib/presence/LF/Hallway.lua @@ -0,0 +1,27 @@ +(function() + local createContext = function(name) + local ctx = CreateRoomContext("LF Hallway") + ctx.name = name + return ctx + end + + C3C.KnxAddresses.Watch("Control LF Hallway Presence", function(ga, state) + local ctx = createContext("GA: " .. ga.Name) + ctx.newVal = state.newVal + C3C.Logger.Debug("Watched Presence", ctx) + + if state.newVal == 1 then + time = os.date("*t") + if C3C.Lib.State.DayTime.IsDay() or (time.hour >= 7 and time.hour <= 22) then + C3C.Logger.Debug("Switching on primary light", ctx) + C3C.KnxAddresses.Get("Switch LF Hallway CTRL"):Send(1) + else + C3C.Logger.Debug("Switching on primary light with dimmed control during nighttime", ctx) + C3C.KnxAddresses.Get("Dimmer LF Hallway"):Send(2) + end + else + C3C.Logger.Debug("Presence ended", ctx) + C3C.KnxAddresses.Get("Switch LF Hallway CTRL"):Send(0) + end + end) +end)() diff --git a/src/lib/presence/LF/index.lua b/src/lib/presence/LF/index.lua new file mode 100644 index 0000000..f685a96 --- /dev/null +++ b/src/lib/presence/LF/index.lua @@ -0,0 +1 @@ +require("lib.presence.LF.Hallway") diff --git a/src/lib/presence/UF/Bathroom.lua b/src/lib/presence/UF/Bathroom.lua new file mode 100644 index 0000000..e0fee69 --- /dev/null +++ b/src/lib/presence/UF/Bathroom.lua @@ -0,0 +1,191 @@ +(function() + local disable = false + local present = false + + local mediaPlaying = false + local mediaOverridden = false + local lightOverridden = false + + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary CTRL FB") + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient CTRL FB") + local gaPrimaryLightCTRLFB = C3C.KnxAddresses.Get("Switch UF Bathroom Primary CTRL FB") + local gaAmbientLightCTRLFB = C3C.KnxAddresses.Get("Switch UF Bathroom Ambient CTRL FB") + + local createContext = function(name) + local ctx = CreateRoomContext("UF Bathroom") + ctx.mediaOverridden = mediaOverridden + ctx.lightOverridden = lightOverridden + ctx.name = name + return ctx + end + + local overrideMedia = function(ga) + local ctx = createContext("overrideMedia") + ctx.ga = ga.Name + C3C.Logger.Debug("Overriding media", ctx) + mediaOverridden = true + C3C.OneShotTimer.Add(120 * 1000, function() + ctx.mediaPlaying = mediaPlaying + ctx.present = present + C3C.Logger.Debug("Checking media override reset after 120 seconds", ctx) + if not mediaPlaying and not present then + C3C.Logger.Debug("Resetting media override after 120 seconds", ctx) + mediaOverridden = false + end + end, "checkMediaOverride") + end + + local overrideLight = function(ga) + local ctx = createContext("overrideLight") + ctx.ga = ga.Name + C3C.Logger.Debug("Overriding light", ctx) + lightOverridden = true + C3C.OneShotTimer.Add(120 * 1000, function() + ctx.present = present + ctx.gaPrimaryLightCTRLFB = gaPrimaryLightCTRLFB.Value + ctx.gaAmbientLightCTRLFB = gaAmbientLightCTRLFB.Value + C3C.Logger.Debug("Checking light override reset after 120 seconds", ctx) + if not present and gaPrimaryLightCTRLFB.Value ~= 1 and gaAmbientLightCTRLFB.Value ~= 1 then + C3C.Logger.Debug("Resetting light override after 120 seconds", ctx) + lightOverridden = false + end + end, "checkLightOverride") + end + + local unOverrideMedia = function() + local ctx = createContext("unOverrideMedia") + C3C.Logger.Debug("Resetting media override", ctx) + mediaOverridden = false + end + + local unOverrideLight = function() + local ctx = createContext("unOverrideLight") + C3C.Logger.Debug("Resetting light override", ctx) + lightOverridden = false + end + + C3C.KnxAddresses.Watch("Media UF Bathroom Playing", function(ga) + mediaPlaying = ga.Value == 1 + end) + + C3C.KnxAddresses.Watch("Media UF Bathroom Off", overrideMedia) + C3C.KnxAddresses.Watch("Media UF Bathroom Preset Cycle Forward", overrideMedia) + C3C.KnxAddresses.Watch("Media UF Bathroom Volume Up", overrideMedia) + C3C.KnxAddresses.Watch("Media UF Bathroom Volume Down", overrideMedia) + C3C.KnxAddresses.Watch("Media UF Bathroom Previous", overrideMedia) + C3C.KnxAddresses.Watch("Media UF Bathroom Next", overrideMedia) + + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary CTRL", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary Light 1", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary Light 2", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary Light Vanity", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Primary Ceiling", overrideLight) + C3C.KnxAddresses.Watch("Dimmer UF Bathroom Primary", overrideLight) + C3C.KnxAddresses.Watch("Dimmer UF Bathroom Primary Dimming", overrideLight) + + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient CTRL", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient Light 1", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient Light 2", overrideLight) + C3C.KnxAddresses.Watch("Switch UF Bathroom Ambient Toilet", overrideLight) + C3C.KnxAddresses.Watch("Dimmer UF Bathroom Ambient", overrideLight) + C3C.KnxAddresses.Watch("Dimmer UF Bathroom Ambient Dimming", overrideLight) + + C3C.KnxAddresses.Watch("UF Bathroom Exit", function() + unOverrideMedia() + unOverrideLight() + end) + + + local presenceStart = function() + present = true + + local ctx = createContext("presenceStart") + C3C.Logger.Debug("Triggered: Presence start", ctx) + + if disable then + C3C.Logger.Debug("Presence start disabled", ctx) + return + end + + time = os.date("*t") + if C3C.Lib.State.DayTime.IsDay() or (time.hour >= 7 and time.hour <= 19) then + C3C.Logger.Debug("Switching on primary light", ctx) + C3C.KnxAddresses.Get("Switch UF Bathroom Primary CTRL"):Send(1) + elseif time.hour > 19 and time.hour <= 20 then + C3C.Logger.Debug("Switching on primary light with dimmed control during nighttime", ctx) + C3C.KnxAddresses.Get("Dimmer UF Bathroom Primary"):Send(12) + end + + + C3C.Logger.Debug("Firing event: UpperFloorBathroomMediaStart", ctx) + C4:FireEvent("UpperFloorBathroomMediaStart") + end + + local presenceEnd = function() + present = false + + local ctx = createContext("presenceEnd") + C3C.Logger.Debug("Triggered: Presence end", ctx) + + if not lightOverridden then + if gaPrimaryLightCTRLFB.Value ~ 0 then + C3C.Logger.Debug("Switching off primary light", ctx) + C3C.KnxAddresses.Get("Switch UF Bathroom Primary CTRL"):Send(0) + end + + if gaAmbientLightCTRLFB.Value ~= 0 then + C3C.Logger.Debug("Switching off ambient light", ctx) + C3C.KnxAddresses.Get("Switch UF Bathroom Ambient CTRL"):Send(0) + end + end + + if not mediaOverridden then + C3C.Logger.Debug("Firing event: UpperFloorBathroomMediaEnd", ctx) + C4:FireEvent("UpperFloorBathroomMediaEnd") + end + + unOverrideMedia() + unOverrideLight() + end + + local presenceEndDelayed = function() + local ctx = createContext("presenceEndDelayed") + C3C.Logger.Debug("Triggered: Presence end delayed", ctx) + + C3C.Logger.Debug("Firing event: UpperFloorBathroomMediaEnd", ctx) + C4:FireEvent("UpperFloorBathroomMediaEnd") + + if gaPrimaryLightCTRLFB.Value ~= 0 then + C3C.Logger.Debug("Switching off primary light", ctx) + C3C.KnxAddresses.Get("Switch UF Bathroom Primary CTRL"):Send(0) + end + if gaAmbientLightCTRLFB.Value ~= 0 then + C3C.Logger.Debug("Switching off ambient light", ctx) + C3C.KnxAddresses.Get("Switch UF Bathroom Ambient CTRL"):Send(0) + end + end + + C3C.KnxAddresses.Watch("Control UF Bathroom Presence", function(ga, state) + local ctx = createContext("GA: " .. ga.Name) + ctx.newVal = state.newVal + C3C.Logger.Debug("Watched GA Bathroom Presence", ctx) + if state.newVal == 1 then + presenceStart() + else + presenceEnd() + end + end) + + C3C.KnxAddresses.Watch("Control UF Bathroom Presence Delayed", function(ga, state) + local ctx = createContext("GA: " .. ga.Name) + ctx.newVal = state.newVal + C3C.Logger.Debug("Watched GA Bathroom Presence Delayed", ctx) + if state.newVal == 1 then + + else + presenceEndDelayed() + end + end) +end)() diff --git a/src/lib/presence/UF/index.lua b/src/lib/presence/UF/index.lua new file mode 100644 index 0000000..1143e0d --- /dev/null +++ b/src/lib/presence/UF/index.lua @@ -0,0 +1 @@ +require("lib.presence.UF.Bathroom") diff --git a/src/lib/presence/index.lua b/src/lib/presence/index.lua new file mode 100644 index 0000000..fc12bae --- /dev/null +++ b/src/lib/presence/index.lua @@ -0,0 +1,2 @@ +require("lib.presence.LF.index") +require("lib.presence.UF.index") diff --git a/src/lib/state/daytime.lua b/src/lib/state/daytime.lua new file mode 100644 index 0000000..ec27a1a --- /dev/null +++ b/src/lib/state/daytime.lua @@ -0,0 +1,13 @@ +(function() + C3C.Lib.State.DayTime = {} + + local isDay = false + + C3C.KnxAddresses.Watch("Day/Night", function(ga) + isDay = ga.Value == 0 + end) + + C3C.Lib.State.DayTime.IsDay = function() + return isDay + end +end)() diff --git a/src/lib/state/index.lua b/src/lib/state/index.lua new file mode 100644 index 0000000..4bce271 --- /dev/null +++ b/src/lib/state/index.lua @@ -0,0 +1,2 @@ +C3C.Lib.State = {} +require("lib.state.daytime") diff --git a/src/www/documentation.html b/src/www/documentation.html new file mode 100644 index 0000000..9cfa50c --- /dev/null +++ b/src/www/documentation.html @@ -0,0 +1,15 @@ + + + + Control4 KNX Ventilation + + + + + +

Control4 KNX Presence

+ +

Control4 driver managing the presence system

+ + + diff --git a/src/www/icons/logo.png b/src/www/icons/logo.png new file mode 100644 index 0000000..8893db6 Binary files /dev/null and b/src/www/icons/logo.png differ diff --git a/src/www/icons/logo_16.png b/src/www/icons/logo_16.png new file mode 100644 index 0000000..574a024 Binary files /dev/null and b/src/www/icons/logo_16.png differ diff --git a/src/www/icons/logo_32.png b/src/www/icons/logo_32.png new file mode 100644 index 0000000..c08279e Binary files /dev/null and b/src/www/icons/logo_32.png differ diff --git a/tests/test.lua b/tests/test.lua new file mode 100644 index 0000000..ba939aa --- /dev/null +++ b/tests/test.lua @@ -0,0 +1,24 @@ +-- Include NPM dependencies +dofile('tests/generated.lua') + +-- Load test module +require('busted') + +-- Load driver +require('driver') + +local inspect = require 'inspect' + +describe("Driver", function() + it("loads", function() + local start1, result1 = pcall(_G["OnDriverLateInit"]) + local start2, result2 = pcall(_G["OnDriverInit"]) + + if (not start1) then + assert.are.equal(true, start2) + return + end + + assert.are.equal(true, start1) + end) +end)