diff --git a/README.md b/README.md index 3d6ddd8..c05bdc6 100644 --- a/README.md +++ b/README.md @@ -1 +1,119 @@ -IyBoYS1wYXJlbnRhbC1jb250cm9scwoKQSBIb21lIEFzc2lzdGFudCBkYXNoYm9hcmQgZm9yIHBlci11c2VyLCBwZXItZGV2aWNlIGludGVybmV0IGJsb2NraW5nIHZpYSBPUE5zZW5zZSBmaXJld2FsbCBhbGlhcyBtYW5hZ2VtZW50LiBBc3NpZ24gZGV2aWNlcyB0byB1c2VycyBieSBNQUMgYWRkcmVzcywgYmxvY2svdW5ibG9jayBpbmRpdmlkdWFsbHkgb3IgcGVyLXVzZXIsIGFuZCBvcHRpb25hbGx5IGVuZm9yY2Ugc2NoZWR1bGVkIGFjY2VzcyB3aW5kb3dzLgoKLS0tCgojIyBIb3cgaXQgd29ya3MKCjEuIE9QTnNlbnNlIG1haW50YWlucyBhIGZpcmV3YWxsIGFsaWFzIGNhbGxlZCBgcGFyZW50YWxfYmxvY2tlZGAKMi4gQSBMQU4gcnVsZSBibG9ja3MgYWxsIHRyYWZmaWMgZnJvbSBJUHMgaW4gdGhhdCBhbGlhcwozLiBUaGUgSEEgZGFzaGJvYXJkIGxvb2tzIHVwIGVhY2ggZGV2aWNlJ3MgY3VycmVudCBJUCBmcm9tIGBkZXZpY2VfdHJhY2tlcmAgZW50aXRpZXMgKHByb3ZpZGVkIGJ5IHRoZSBPUE5zZW5zZSBpbnRlZ3JhdGlvbiksIHRoZW4gYWRkcy9yZW1vdmVzIElQcyBmcm9tIHRoZSBhbGlhcyB2aWEgT1BOc2Vuc2UgQVBJCgpCZWNhdXNlIGRldmljZXMgYXJlIGlkZW50aWZpZWQgYnkgKipNQUMgYWRkcmVzcyoqLCBibG9ja3MgcmUtYXBwbHkgYXV0b21hdGljYWxseSB3aGVuIGEgZGV2aWNlIHJlY29ubmVjdHMgd2l0aCBhIG5ldyBJUCAoaGFuZGxlZCBieSBhIHN0YXRlIGNoYW5nZSBsaXN0ZW5lciBpbiB0aGUgZGFzaGJvYXJkKS4KCi0tLQoKIyMgU3RlcCAxIOKAlCBPUE5zZW5zZSBzZXR1cAoKIyMjIDEuMSBDcmVhdGUgdGhlIGZpcmV3YWxsIGFsaWFzCgpGaXJld2FsbCDihpIgQWxpYXNlcyDihpIg77yLCgp8IEZpZWxkIHwgVmFsdWUgfAp8LS0tfC0tLXwKfCBOYW1lIHwgYHBhcmVudGFsX2Jsb2NrZWRgIHwKfCBUeXBlIHwgYEhvc3QocylgIHwKfCBEZXNjcmlwdGlvbiB8IGBQYXJlbnRhbCBDb250cm9scyDigJQgYmxvY2tlZCBkZXZpY2VzYCB8CnwgQ29udGVudCB8ICoobGVhdmUgZW1wdHkg4oCUIG1hbmFnZWQgdmlhIEFQSSkqIHwKClNhdmUsIHRoZW4gKipBcHBseSoqLgoKIyMjIDEuMiBDcmVhdGUgdGhlIGJsb2NraW5nIHJ1bGUKCkZpcmV3YWxsIOKGkiBSdWxlcyDihpIgTEFOIOKGkiDvvIsKCnwgRmllbGQgfCBWYWx1ZSB8CnwtLS18LS0tfAp8IEFjdGlvbiB8ICoqQmxvY2sqKiB8CnwgSW50ZXJmYWNlIHwgTEFOIHwKfCBEaXJlY3Rpb24gfCBpbiB8CnwgUHJvdG9jb2wgfCBhbnkgfAp8IFNvdXJjZSB8IFNpbmdsZSBob3N0IG9yIGFsaWFzIOKGkiBgcGFyZW50YWxfYmxvY2tlZGAgfAp8IERlc3RpbmF0aW9uIHwgYW55IHwKfCBEZXNjcmlwdGlvbiB8IGBQYXJlbnRhbCBDb250cm9scyDigJQgYmxvY2sgaW50ZXJuZXRgIHwKClBvc2l0aW9uIHRoaXMgcnVsZSAqKmFib3ZlKiogeW91ciBkZWZhdWx0IGFsbG93LWFsbCBMQU4gcnVsZS4gU2F2ZSBhbmQgQXBwbHkuCgojIyMgMS4zIENyZWF0ZSBhbiBBUEkga2V5CgpTeXN0ZW0g4oaSIEFjY2VzcyDihpIgVXNlcnMg4oaSICh5b3VyIGFkbWluIHVzZXIgb3IgYSBkZWRpY2F0ZWQgdXNlcikg4oaSIEFQSSBLZXlzIOKGkiDvvIsKClBlcm1pc3Npb25zIG5lZWRlZDogKipGaXJld2FsbDogQWxpYXMqKiAocmVhZCArIHdyaXRlKQoKTm90ZSB0aGUgKipLZXkqKiBhbmQgKipTZWNyZXQqKiDigJQgeW91J2xsIG5lZWQgdGhlbSBmb3Igc2VjcmV0cy55YW1sLgoKLS0tCgojIyBTdGVwIDIg4oCUIEhvbWUgQXNzaXN0YW50IHNldHVwCgojIyMgMi4xIEFkZCBzZWNyZXRzIHRvIGAvY29uZmlnL3NlY3JldHMueWFtbGAKClJlcGxhY2UgdmFsdWVzIHdpdGggeW91ciBhY3R1YWwgT1BOc2Vuc2UgZGV0YWlsczoKCmBgYHlhbWwKb3Buc2Vuc2VfYWxpYXNfYWRkX3VybDogImh0dHBzOi8vWU9VUl9PUE5TRU5TRV9JUC9hcGkvZmlyZXdhbGwvYWxpYXNfdXRpbC9hZGQvcGFyZW50YWxfYmxvY2tlZCIKb3Buc2Vuc2VfYWxpYXNfZGVsX3VybDogImh0dHBzOi8vWU9VUl9PUE5TRU5TRV9JUC9hcGkvZmlyZXdhbGwvYWxpYXNfdXRpbC9kZWxldGUvcGFyZW50YWxfYmxvY2tlZCIKb3Buc2Vuc2VfYXBwbHlfdXJsOiAiaHR0cHM6Ly9ZT1VSX09QTlNFTlNFX0lQL2FwaS9maXJld2FsbC9hbGlhcy9yZWNvbmZpZ3VyZSIKCiMgR2VuZXJhdGUgd2l0aDogZWNobyAtbiAiQVBJS0VZOkFQSVNFQ1JFVCIgfCBiYXNlNjQKb3Buc2Vuc2VfYmFzaWNfYXV0aDogIkJhc2ljIEJBU0U2NF9PRl9LRVlfQ09MT05fU0VDUkVUIgpgYGAKClRvIGdlbmVyYXRlIHRoZSBiYXNlNjQgc3RyaW5nIG9uIGFueSBMaW51eC9tYWNPUyBtYWNoaW5lOgpgYGBiYXNoCmVjaG8gLW4gInlvdXJfYXBpX2tleTp5b3VyX2FwaV9zZWNyZXQiIHwgYmFzZTY0CmBgYAoKIyMjIDIuMiBDb3B5IGZpbGVzIGludG8gcGxhY2UKCmBgYGJhc2gKIyBGcm9tIHlvdXIgY29uZmlnIGRpcmVjdG9yeQpjcCBwYWNrYWdlcy9wYXJlbnRhbF9jb250cm9scy55YW1sIC9jb25maWcvcGFja2FnZXMvCmNwIHd3dy9wYXJlbnRhbF9jb250cm9scy5odG1sIC9jb25maWcvd3d3LwpgYGAKCj4gSWYgeW91IGRvbid0IHVzZSBwYWNrYWdlcyB5ZXQsIGFkZCB0aGlzIHRvIGAvY29uZmlnL2NvbmZpZ3VyYXRpb24ueWFtbGA6Cj4gYGBgeWFtbAo+IGhvbWVhc3Npc3RhbnQ6Cj4gICBwYWNrYWdlczogIWluY2x1ZGVfZGlyX25hbWVkIHBhY2thZ2VzCj4gYGBgCgojIyMgMi4zIEFkZCB0aGUgc2lkZWJhciBwYW5lbAoKQWRkIHRvIGAvY29uZmlnL2NvbmZpZ3VyYXRpb24ueWFtbGA6CgpgYGB5YW1sCnBhbmVsX2lmcmFtZToKICBwYXJlbnRhbF9jb250cm9sczoKICAgIHRpdGxlOiAiUGFyZW50YWwgQ29udHJvbHMiCiAgICBpY29uOiBtZGk6c2hpZWxkLWFjY291bnQKICAgIHVybDogL2xvY2FsL3BhcmVudGFsX2NvbnRyb2xzLmh0bWwKICAgIHJlcXVpcmVfYWRtaW46IHRydWUKYGBgCgojIyMgMi40IFJlc3RhcnQgSG9tZSBBc3Npc3RhbnQKCkNoZWNrIGZvciBlcnJvcnMgaW4gU2V0dGluZ3Mg4oaSIFN5c3RlbSDihpIgTG9ncy4gVGhlIGBpbnB1dF90ZXh0LnBhcmVudGFsX2NvbnRyb2xfY29uZmlnYCBlbnRpdHkgc2hvdWxkIG5vdyBleGlzdC4KCi0tLQoKIyMgU3RlcCAzIOKAlCBEYXNoYm9hcmQgZmlyc3QgcnVuCgoxLiBPcGVuIHRoZSAqKlBhcmVudGFsIENvbnRyb2xzKiogc2lkZWJhciBwYW5lbAoyLiBPbiBmaXJzdCBsb2FkLCB5b3UnbGwgc2VlIGEgc2V0dXAgc2NyZWVuLiBFbnRlcjoKICAgLSAqKkhBIFVSTCoqOiB5b3VyIEhBIFVSTCAoZS5nLiBgaHR0cHM6Ly9oYS5oaWRlYXdheWdhbWluZy5jb20uYXVgKQogICAtICoqTG9uZy1MaXZlZCBUb2tlbioqOiBmcm9tIHlvdXIgSEEgUHJvZmlsZSDihpIgU2VjdXJpdHkg4oaSIExvbmctTGl2ZWQgQWNjZXNzIFRva2VucyDihpIgQ3JlYXRlIFRva2VuCjMuIENsaWNrICoqQ29ubmVjdCoqCgpZb3VyIGNyZWRlbnRpYWxzIGFyZSBzYXZlZCB0byBicm93c2VyIGBsb2NhbFN0b3JhZ2VgIOKAlCB5b3Ugd29uJ3QgbmVlZCB0byBlbnRlciB0aGVtIGFnYWluIG9uIHRoZSBzYW1lIGRldmljZS4KCi0tLQoKIyMgVXNhZ2UKCiMjIyBBZGRpbmcgdXNlcnMKCkNsaWNrICoq77yLIEFkZCBVc2VyKiosIGdpdmUgdGhlbSBhIG5hbWUgYW5kIHBpY2sgYSBjb2xvdXIuCgojIyMgQWRkaW5nIGRldmljZXMKCk9uIGEgdXNlciBjYXJkLCBjbGljayAqKu+8iyBBZGQgRGV2aWNlKiouIEVpdGhlcjoKLSBQaWNrIGZyb20gdGhlICoqZGlzY292ZXJlZCBkZXZpY2VzKiogbGlzdCAoZGV2aWNlcyBzZWVuIGJ5IHRoZSBPUE5zZW5zZSBpbnRlZ3JhdGlvbikKLSBPciBlbnRlciBhIE1BQyBhZGRyZXNzIG1hbnVhbGx5Cgo+ICoqVGlwKio6IFNldCBESENQIHN0YXRpYyBsZWFzZXMgaW4gT1BOc2Vuc2UgZm9yIGtpZHMnIGRldmljZXMgc28gdGhlaXIgSVBzIHN0YXkgY29uc2lzdGVudCwgdGhvdWdoIHRoZSBkYXNoYm9hcmQgaGFuZGxlcyBJUCBjaGFuZ2VzIGF1dG9tYXRpY2FsbHkuCgojIyMgQmxvY2tpbmcKCi0gKipVc2VyIHRvZ2dsZSoqICh0b3AgcmlnaHQgb2YgY2FyZCk6IGJsb2Nrcy91bmJsb2NrcyBhbGwgZGV2aWNlcyBmb3IgdGhhdCB1c2VyIHNpbXVsdGFuZW91c2x5Ci0gKipEZXZpY2UgdG9nZ2xlKiogKG9uIGVhY2ggZGV2aWNlIHJvdyk6IGluZGl2aWR1YWwgZGV2aWNlIGNvbnRyb2wKCklmIGEgZGV2aWNlIGlzIG9mZmxpbmUgd2hlbiB5b3UgYmxvY2sgaXQsIHRoZSBkYXNoYm9hcmQgbWFya3MgaXQgYmxvY2tlZCBhbmQgd2lsbCBhcHBseSB0aGUgZmlyZXdhbGwgcnVsZSBhdXRvbWF0aWNhbGx5IHdoZW4gdGhlIGRldmljZSByZWNvbm5lY3RzLgoKIyMjIFNjaGVkdWxpbmcKCkV4cGFuZCB0aGUgKirwn5WQIFNjaGVkdWxlKiogc2VjdGlvbiBvbiBhbnkgdXNlciBjYXJkOgoKMS4gRW5hYmxlIHRoZSBzY2hlZHVsZSB0b2dnbGUKMi4gU2V0ICoqYmxvY2sqKiBhbmQgKip1bmJsb2NrKiogdGltZXMgZm9yIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcwozLiBUaW1lcyBhcmUgZW5mb3JjZWQgZXZlcnkgbWludXRlIHdoaWxlIHRoZSBkYXNoYm9hcmQgcGFnZSBpcyBvcGVuCgpUaGUgdGltZSByYW5nZSBpcyB0aGUgKipibG9ja2VkKiogd2luZG93IOKAlCBlLmcuIGAyMTowMCDihpIgMDc6MDBgIG1lYW5zIGJsb2NrZWQgZnJvbSA5cG0gdW50aWwgN2FtIChvdmVybmlnaHQgcmFuZ2VzIHdvcmsgY29ycmVjdGx5KS4KCiMjIyMgSEEtbmF0aXZlIHNjaGVkdWxlIGVuZm9yY2VtZW50IChvcHRpb25hbCkKCkZvciBzY2hlZHVsZXMgdG8gYmUgZW5mb3JjZWQgZXZlbiB3aGVuIHRoZSBkYXNoYm9hcmQgaXNuJ3Qgb3BlbiwgdW5jb21tZW50IHRoZSBgYXV0b21hdGlvbjpgIGJsb2NrIGF0IHRoZSBib3R0b20gb2YgYHBhY2thZ2VzL3BhcmVudGFsX2NvbnRyb2xzLnlhbWxgIGFuZCByZXN0YXJ0IEhBLiBOb3RlOiB0aGUgWUFNTCBhdXRvbWF0aW9uIHJlcXVpcmVzIHRoZSBgZnJvbV9qc29uYCBKaW5qYTIgZmlsdGVyIGFuZCBIQSAyMDIzLjQrLgoKLS0tCgojIyBUcm91Ymxlc2hvb3RpbmcKCioqQmxvY2sgZG9lc24ndCBzZWVtIHRvIGFwcGx5KioKLSBDb25maXJtIHRoZSBgcGFyZW50YWxfYmxvY2tlZGAgYWxpYXMgZXhpc3RzIGluIE9QTnNlbnNlIOKGkiBGaXJld2FsbCDihpIgQWxpYXNlcwotIENoZWNrIHRoZSBibG9ja2luZyBydWxlIGlzIGFib3ZlIHRoZSBkZWZhdWx0IExBTiBhbGxvdyBydWxlCi0gVmVyaWZ5IHlvdXIgQVBJIGNyZWRlbnRpYWxzOiB0ZXN0IGluIGEgYnJvd3NlciDihpIgYGh0dHBzOi8vWU9VUl9PUE5TRU5TRS9hcGkvZmlyZXdhbGwvYWxpYXNfdXRpbC9hbGlhc2VzYCDigJQgc2hvdWxkIHJldHVybiBKU09OCgoqKkRldmljZSBub3QgYXBwZWFyaW5nIGluIGRpc2NvdmVyZWQgbGlzdCoqCi0gTWFrZSBzdXJlIHRoZSBPUE5zZW5zZSBpbnRlZ3JhdGlvbiBpcyBpbnN0YWxsZWQgYW5kIHRoZSBkZXZpY2UgaGFzIGJlZW4gc2VlbiByZWNlbnRseQotIFRyeSB1c2luZyBhIG1hbnVhbCBNQUMgZW50cnkgaW5zdGVhZAoKKioiRmFpbGVkIHRvIHNhdmUgY29uZmlnIioqCi0gQ2hlY2sgSEEgbG9ncyBmb3IgYGlucHV0X3RleHQucGFyZW50YWxfY29udHJvbF9jb25maWdgIGVycm9ycwotIEVuc3VyZSB0aGUgcGFja2FnZSBsb2FkZWQgY29ycmVjdGx5IChubyBZQU1MIGVycm9ycyBpbiBzdGFydHVwIGxvZ3MpCgoqKkRhc2hib2FyZCB3b24ndCBjb25uZWN0KioKLSBDb25maXJtIHRoZSBMb25nLUxpdmVkIFRva2VuIGlzIGNvcnJlY3QKLSBJZiB1c2luZyBhIHNlbGYtc2lnbmVkIGNlcnQgb24gSEEsIHRoZSBwYWdlIG1heSBoYXZlIGNlcnQgd2FybmluZ3Mg4oCUIHRydXN0IHRoZSBjZXJ0IGZpcnN0IGJ5IHZpc2l0aW5nIHRoZSBIQSBVUkwgZGlyZWN0bHkK \ No newline at end of file +ha-parental-controls +A Home Assistant dashboard for per-user, per-device internet blocking via OPNsense firewall alias management. Assign devices to users by MAC address, block/unblock individually or per-user, and optionally enforce scheduled access windows. + +How it works + +OPNsense maintains a firewall alias called parental_blocked +A LAN rule blocks all traffic from IPs in that alias +The HA dashboard looks up each device's current IP from device_tracker entities (provided by the OPNsense integration), then adds/removes IPs from the alias via OPNsense API + +Because devices are identified by MAC address, blocks re-apply automatically when a device reconnects with a new IP (handled by a state change listener in the dashboard). + +Step 1 — OPNsense setup +1.1 Create the firewall alias +Firewall → Aliases → + +FieldValueNameparental_blockedTypeHost(s)DescriptionParental Controls — blocked devicesContent(leave empty — managed via API) +Save, then Apply. +1.2 Create the blocking rule +Firewall → Rules → LAN → + +FieldValueActionBlockInterfaceLANDirectioninProtocolanySourceSingle host or alias → parental_blockedDestinationanyDescriptionParental Controls — block internet +Position this rule above your default allow-all LAN rule. Save and Apply. +1.3 Create an API key +System → Access → Users → (your admin user or a dedicated user) → API Keys → + +Permissions needed: Firewall: Alias (read + write) +Note the Key and Secret — you'll need them for secrets.yaml. + +Step 2 — Home Assistant setup +2.1 Add secrets to /config/secrets.yaml +Replace values with your actual OPNsense details: +yamlopnsense_alias_add_url: "https://YOUR_OPNSENSE_IP/api/firewall/alias_util/add/parental_blocked" +opnsense_alias_del_url: "https://YOUR_OPNSENSE_IP/api/firewall/alias_util/delete/parental_blocked" +opnsense_apply_url: "https://YOUR_OPNSENSE_IP/api/firewall/alias/reconfigure" + +# Generate with: echo -n "APIKEY:APISECRET" | base64 +opnsense_basic_auth: "Basic BASE64_OF_KEY_COLON_SECRET" +To generate the base64 string on any Linux/macOS machine: +bashecho -n "your_api_key:your_api_secret" | base64 +2.2 Copy files into place +bash# From your config directory +cp packages/parental_controls.yaml /config/packages/ +cp www/parental_controls.html /config/www/ + +If you don't use packages yet, add this to /config/configuration.yaml: +yamlhomeassistant: + packages: !include_dir_named packages + +2.3 Add the sidebar panel +Add to /config/configuration.yaml: +yamlpanel_iframe: + parental_controls: + title: "Parental Controls" + icon: mdi:shield-account + url: /local/parental_controls.html + require_admin: true +2.4 Restart Home Assistant +Check for errors in Settings → System → Logs. The input_text.parental_control_config entity should now exist. + +Step 3 — Dashboard first run + +Open the Parental Controls sidebar panel +On first load, you'll see a setup screen. Enter: + +HA URL: your HA URL (e.g. https://ha.hideawaygaming.com.au) +Long-Lived Token: from your HA Profile → Security → Long-Lived Access Tokens → Create Token + + +Click Connect + +Your credentials are saved to browser localStorage — you won't need to enter them again on the same device. + +Usage +Adding users +Click + Add User, give them a name and pick a colour. +Adding devices +On a user card, click + Add Device. Either: + +Pick from the discovered devices list (devices seen by the OPNsense integration) +Or enter a MAC address manually + + +Tip: Set DHCP static leases in OPNsense for kids' devices so their IPs stay consistent, though the dashboard handles IP changes automatically. + +Blocking + +User toggle (top right of card): blocks/unblocks all devices for that user simultaneously +Device toggle (on each device row): individual device control + +If a device is offline when you block it, the dashboard marks it blocked and will apply the firewall rule automatically when the device reconnects. +Scheduling +Expand the 🕐 Schedule section on any user card: + +Enable the schedule toggle +Set block and unblock times for weekdays and weekends +Times are enforced every minute while the dashboard page is open + +The time range is the blocked window — e.g. 21:00 → 07:00 means blocked from 9pm until 7am (overnight ranges work correctly). +HA-native schedule enforcement (optional) +For schedules to be enforced even when the dashboard isn't open, uncomment the automation: block at the bottom of packages/parental_controls.yaml and restart HA. Note: the YAML automation requires the from_json Jinja2 filter and HA 2023.4+. + +Troubleshooting +Block doesn't seem to apply + +Confirm the parental_blocked alias exists in OPNsense → Firewall → Aliases +Check the blocking rule is above the default LAN allow rule +Verify your API credentials: test in a browser → https://YOUR_OPNSENSE/api/firewall/alias_util/aliases — should return JSON + +Device not appearing in discovered list + +Make sure the OPNsense integration is installed and the device has been seen recently +Try using a manual MAC entry instead + +"Failed to save config" + +Check HA logs for input_text.parental_control_config errors +Ensure the package loaded correctly (no YAML errors in startup logs) + +Dashboard won't connect + +Confirm the Long-Lived Token is correct +If using a self-signed cert on HA, the page may have cert warnings — trust the cert first by visiting the HA URL directly \ No newline at end of file