Compare commits
229 commits
Author | SHA1 | Date | |
---|---|---|---|
|
e70dcc196d | ||
|
242f8142d2 | ||
|
f9edb6c1fe | ||
|
332b1a6657 | ||
|
477c68cf55 | ||
|
6905bcb6b5 | ||
|
b15940fc8e | ||
|
a1b2851193 | ||
|
fc51c71323 | ||
|
a8547b71dd | ||
|
1eefaa89c4 | ||
|
6487398741 | ||
|
ee886e0365 | ||
|
6f888a5c08 | ||
|
af01e48c05 | ||
|
81dc2d8f73 | ||
|
8e68931e57 | ||
|
955fa33bca | ||
|
013f946681 | ||
|
c26817bd25 | ||
|
5650d7f5ca | ||
|
44fe6a626b | ||
|
07f81b887e | ||
|
339b01dda3 | ||
|
7bada8570e | ||
|
8ca38e19c0 | ||
|
da2bffcf88 | ||
|
464ba7b768 | ||
|
cf1b56c51e | ||
|
6de1a940ed | ||
|
1f03cf55dc | ||
|
b3aa0e3d90 | ||
|
de1ca6a6f6 | ||
|
890dd11e8b | ||
|
b2a77d2de3 | ||
|
502bebb766 | ||
|
b9d87cd6aa | ||
|
b6c89fb152 | ||
|
2aa2191334 | ||
|
ccc097c438 | ||
|
8e8ff310c1 | ||
|
04b0987781 | ||
|
802c218bc9 | ||
|
c02162dfef | ||
|
50abfe86b6 | ||
|
5feb9ad3c2 | ||
|
6bc441558f | ||
|
e828e0bf0a | ||
|
1bbedfaa8b | ||
|
a61dfa5ff7 | ||
|
80bae0c34d | ||
|
593aca593b | ||
|
13e765c10a | ||
|
2cdd645274 | ||
|
355d16ff4f | ||
|
e5f0e40832 | ||
|
5809d0aad9 | ||
|
da894aa32a | ||
|
975f882416 | ||
|
715ead5458 | ||
|
4b610ba4e5 | ||
|
6079affcfa | ||
|
d3a2432eb7 | ||
|
2349584551 | ||
|
4abf5b210d | ||
|
380884d60a | ||
|
c8728238af | ||
|
e1e9f61e08 | ||
|
9c5a270b4f | ||
|
efe3de0c20 | ||
|
ebee5a34e2 | ||
|
ca58ee6007 | ||
|
04e0d875cf | ||
|
51c9c62859 | ||
|
5186956883 | ||
|
c83e735724 | ||
|
b8f4856a5b | ||
|
514a930d04 | ||
|
6caaa4e891 | ||
|
15bc156d74 | ||
|
eae9fd7e2d | ||
|
727a075dbb | ||
|
2ea35dec75 | ||
|
cace2ae3e5 | ||
|
7a892b6800 | ||
|
3869e670a6 | ||
|
7c2e61e4d6 | ||
|
009d8c6085 | ||
|
a21e95c36a | ||
|
4d957aec22 | ||
|
d8d8552dd0 | ||
|
22844e7ae5 | ||
|
251874bdc2 | ||
|
52eeb08c43 | ||
|
b471038cb3 | ||
|
053dcf0354 | ||
|
97d5a80387 | ||
|
ce9ce906f5 | ||
|
a7c00d8fb3 | ||
|
18e0359490 | ||
|
e68bbec8dc | ||
|
4405e7d348 | ||
|
bb8a14999e | ||
|
7b8fe02c7e | ||
|
267fb6a011 | ||
|
10aeb0ba1b | ||
|
f0601f7c19 | ||
|
d49aaa7af4 | ||
|
cb4c5edd26 | ||
|
bd888c12ba | ||
|
0737a93c2c | ||
|
fbbf4d8e33 | ||
|
385092a513 | ||
|
fd0689bf4c | ||
|
c45eff65e0 | ||
|
6b4a92fcb1 | ||
|
0af7bc184c | ||
|
c5570b6d5e | ||
|
5520b25c0f | ||
|
d1d1d1a3b8 | ||
|
e3f7073219 | ||
|
9667dcc171 | ||
|
58f931ad8e | ||
|
ac789a81c2 | ||
|
f487441038 | ||
|
dc2e804f0e | ||
|
e1d397f8e1 | ||
|
717fa2f6ae | ||
|
1294631f8a | ||
|
c468e2776e | ||
|
952f3877b7 | ||
|
12a952a77d | ||
|
1722a3e346 | ||
|
0b4a7a3c72 | ||
|
1a0861fd9c | ||
|
9711006f29 | ||
|
575b7ff192 | ||
|
e4c48353c5 | ||
|
b2d36ebafc | ||
|
bd9009be47 | ||
|
34b373f3ee | ||
|
e22a4c1295 | ||
|
e32ffd19a1 | ||
|
a588cec520 | ||
|
d420a8ad06 | ||
|
cbac88efd2 | ||
|
23f64fd401 | ||
|
3b81c11675 | ||
|
f8b8dd075b | ||
|
968d5c3859 | ||
|
f4bffc55de | ||
|
6a089556db | ||
|
4da561f515 | ||
|
5d391cc09d | ||
|
24e91ba115 | ||
|
1a4479da78 | ||
|
c639d65332 | ||
|
5b1c88fa6a | ||
|
b368ecc532 | ||
|
52c87eaf3a | ||
|
b52ff90a88 | ||
|
1183c4371d | ||
|
5408d60bbd | ||
|
7d1915b566 | ||
|
ce54b6ef1b | ||
|
f180337c33 | ||
|
3ee517463b | ||
|
f1c7cea7c4 | ||
|
c10fb9fedb | ||
|
7782503f78 | ||
|
8efaa72492 | ||
|
a76417c240 | ||
|
e14d018b20 | ||
|
981668032e | ||
|
574d3137b9 | ||
|
085a859dd7 | ||
|
bd56487f42 | ||
|
2c61ba596a | ||
|
9da682b314 | ||
|
bc127cd16e | ||
|
ccb1b1fae7 | ||
|
263b38b301 | ||
|
6b6a240412 | ||
|
c931681371 | ||
|
e4b9fdc278 | ||
|
9f19740c92 | ||
|
39498f073b | ||
|
6306829bf8 | ||
|
817ecaa8c1 | ||
|
0da0f876c3 | ||
|
5bef491779 | ||
|
d558f00a3b | ||
|
22f483e49f | ||
|
84bd65c881 | ||
|
a6040f7f38 | ||
|
e8dde3bb52 | ||
|
36a372db1f | ||
|
ee1141c0a5 | ||
|
379ee9fb55 | ||
|
3b61faa03e | ||
|
c37b046a86 | ||
|
af4af1be14 | ||
|
d8b073dac9 | ||
|
82098ba235 | ||
|
b57bb35eac | ||
|
9b5bdcb27d | ||
|
ddab2394ca | ||
|
56df9acabd | ||
|
b114a85bd7 | ||
|
d863afd7ca | ||
|
40d98f4778 | ||
|
d1a8c624fb | ||
|
0c097e2206 | ||
|
5c8a8d4870 | ||
|
20aadb2ec7 | ||
|
bd063ca8f0 | ||
|
f20496b027 | ||
|
67d8a66066 | ||
|
1259018e56 | ||
|
b306420d47 | ||
|
74e6bd88e6 | ||
|
5e2f35b76c | ||
|
f0f5c4c4a9 | ||
|
84fc3ff7f0 | ||
|
6a0b2e894a | ||
|
6bec0efeb2 | ||
|
1280f9bee1 | ||
|
a49b52fb1a | ||
|
6b4f967111 |
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -33,15 +33,23 @@ A clear and concise description of what you expected to happen.
|
|||
If applicable, add screenshots to help explain your problem.
|
||||
-->
|
||||
|
||||
|
||||
## Software versions
|
||||
<!--
|
||||
All fields in this sections are required.
|
||||
-->
|
||||
- Home Assistant version: <!-- e.g. HA v0.108.3 -->
|
||||
- Home Assistant version: <!-- e.g. HA 2022.2.0 -->
|
||||
- Mikrotik Router integration version: <!-- e.g. v1.0.0 -->
|
||||
- Mikrotik Hardware: <!-- e.g. RB4011iGS+ -->
|
||||
- RouterOS version: <!-- e.g. v6.45 -->
|
||||
|
||||
|
||||
## Diagnostics data
|
||||
<!--
|
||||
If you are seing incorrect data through the integration, please upload integration diagnostics data.
|
||||
-->
|
||||
|
||||
|
||||
## Traceback/Error logs
|
||||
<!--
|
||||
If you come across any trace or error logs, please provide them.
|
||||
|
|
2
.github/bandit.yaml
vendored
|
@ -1,2 +0,0 @@
|
|||
skips:
|
||||
- B105 # :hardcoded_password_string
|
46
.github/stale.yml
vendored
|
@ -1,46 +0,0 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- planned
|
||||
- help wanted
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: true
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: true
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
|
||||
# Handle pull requests a little bit faster and with an adjusted comment.
|
||||
pulls:
|
||||
daysUntilStale: 30
|
||||
exemptProjects: false
|
||||
markComment: >
|
||||
There hasn't been any activity on this pull request recently. This pull
|
||||
request has been automatically marked as stale because of that and will
|
||||
be closed if no further activity occurs within 7 days.
|
||||
Thank you for your contributions.
|
19
.github/workflows/ci.yml
vendored
|
@ -10,6 +10,7 @@ on:
|
|||
paths:
|
||||
- 'custom_components/**'
|
||||
- 'tests/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
|
@ -18,7 +19,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: "actions/checkout@v2"
|
||||
uses: "actions/checkout@v3"
|
||||
- name: Black Code Format Check
|
||||
uses: lgeiger/black-action@master
|
||||
with:
|
||||
|
@ -29,11 +30,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: "actions/checkout@v2"
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
uses: "actions/checkout@v3"
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: '3.13'
|
||||
- name: Generate Requirements lists
|
||||
run: |
|
||||
python3 .github/generate_requirements.py
|
||||
|
@ -45,9 +46,7 @@ jobs:
|
|||
- name: Lint with flake8
|
||||
run: |
|
||||
pip install flake8
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --ignore W503,E722,F722 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=15 --max-line-length=127 --statistics
|
||||
# - name: Test with pytest
|
||||
# run: |
|
||||
|
@ -60,7 +59,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: "actions/checkout@v2"
|
||||
uses: "actions/checkout@v3"
|
||||
- name: Security check - Bandit
|
||||
uses: ioggstream/bandit-report-artifacts@v0.0.2
|
||||
with:
|
||||
|
@ -68,7 +67,7 @@ jobs:
|
|||
config_file: .github/bandit.yaml
|
||||
ignore_failure: false
|
||||
- name: Security check report artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Security report
|
||||
path: output/security_report.txt
|
||||
|
@ -79,6 +78,6 @@ jobs:
|
|||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: "actions/checkout@v2"
|
||||
uses: "actions/checkout@v3"
|
||||
- name: Run hassfest
|
||||
uses: home-assistant/actions/hassfest@master
|
||||
|
|
18
.github/workflows/hacs.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: HACS Action
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
hacs:
|
||||
name: HACS Action
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@v2"
|
||||
- name: HACS Action
|
||||
uses: "hacs/action@main"
|
||||
with:
|
||||
category: "integration"
|
14
.github/workflows/lock.yml
vendored
|
@ -3,6 +3,14 @@ name: Lock
|
|||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
|
@ -11,7 +19,9 @@ jobs:
|
|||
- uses: dessant/lock-threads@v3.0.0
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: "30"
|
||||
exclude-any-issue-labels: 'planned, help-wanted'
|
||||
exclude-any-pr-labels: 'wip'
|
||||
issue-inactive-days: "30"
|
||||
issue-lock-reason: ""
|
||||
pr-lock-inactive-days: "7"
|
||||
pr-inactive-days: "7"
|
||||
pr-lock-reason: ""
|
||||
|
|
4
.github/workflows/release.yml
vendored
|
@ -34,10 +34,10 @@ jobs:
|
|||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.13
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
|
|
20
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: 'Stale'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
name: Stale
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: "actions/stale@v9"
|
||||
with:
|
||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.'
|
||||
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
exempt-issue-labels: 'pinned,security,planned,help wanted'
|
||||
exempt-pr-labels: 'pinned,security,planned,help wanted'
|
3
Pipfile
|
@ -8,7 +8,6 @@ wheel = ">=0.34"
|
|||
pygithub = ">=1.47"
|
||||
homeassistant = ">=2021.4.6"
|
||||
sqlalchemy = "==1.3.16"
|
||||
codecov = "==2.0.15"
|
||||
mock-open = "==1.3.1"
|
||||
mypy = "==0.770"
|
||||
pre-commit = "==2.2.0"
|
||||
|
@ -24,5 +23,5 @@ requests_mock = "==1.7.0"
|
|||
responses = "==0.10.6"
|
||||
|
||||
[packages]
|
||||
librouteros = ">=3.2.0"
|
||||
librouteros = ">=3.4.1"
|
||||
mac-vendor-lookup = ">=0.1.12"
|
59
README.md
|
@ -2,9 +2,11 @@
|
|||

|
||||
[](https://github.com/hacs/integration)
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
[](https://app.lokalise.com/public/581188395e9778a6060128.17699416/)
|
||||
|
||||
|
@ -46,18 +48,21 @@ Monitor and control your Mikrotik device from Home Assistant.
|
|||
* Enable/disable Mangle switches
|
||||
* Enable/disable Filter switches
|
||||
* Monitor and control PPP users
|
||||
* Monitor UPS
|
||||
* Monitor GPS coordinates
|
||||
* Captive Portal
|
||||
* Kid Control
|
||||
* Client Traffic RX/TX WAN/LAN monitoring though Accounting or Kid Control Devices (depending on RouterOS FW version)
|
||||
* Device tracker for hosts in network
|
||||
* System sensors (CPU, Memory, HDD, Temperature)
|
||||
* Check firmware update
|
||||
* Check and update RouterOS and RouterBOARD firmware
|
||||
* Execute scripts
|
||||
* View environment variables
|
||||
* Configurable update interval
|
||||
* Configurable traffic unit (bps, Kbps, Mbps, B/s, KB/s, MB/s)
|
||||
* Supports monitoring of multiple mikrotik devices simultaneously
|
||||
|
||||
## Features
|
||||
# Features
|
||||
## Interfaces
|
||||
Monitor and control status on each Mikrotik interface, both lan and wlan. Both physical and virtual.
|
||||
|
||||
|
@ -90,7 +95,7 @@ NOTE: FastTracked packets are not processed by Simple Queues.
|
|||

|
||||
|
||||
|
||||
### PPP
|
||||
## PPP
|
||||
Control and monitor PPP users.
|
||||
|
||||

|
||||
|
@ -101,6 +106,11 @@ Track availability of all network devices. All devices visible to Mikrotik devic
|
|||
|
||||

|
||||
|
||||
## Netwatch Tracking
|
||||
Track netwatch status.
|
||||
|
||||

|
||||
|
||||
## Scripts
|
||||
Execute Mikrotik Router scripts.
|
||||
You can execute scripts by automatically created switches or using services.
|
||||
|
@ -108,15 +118,14 @@ You can execute scripts by automatically created switches or using services.
|
|||

|
||||
|
||||
## Kid Control
|
||||
Monitor and control.
|
||||
Monitor and control Kid Control.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Client Traffic
|
||||
|
||||
#### RouterOS v6
|
||||
###### Accounting
|
||||
|
||||
### Client Traffic for RouterOS v6
|
||||
Monitor per-IP throughput tracking based on Mikrotik Accounting.
|
||||
|
||||
Feature is present in Winbox IP-Accounting. Make sure that threshold is set to reasonable value to store all connections between user defined scan interval. Max value is 8192 so for piece of mind I recommend setting that value.
|
||||
|
@ -126,9 +135,7 @@ More information about Accounting can be found on [Mikrotik support page](https:
|
|||
NOTE: Accounting does not count in FastTracked packets.
|
||||
|
||||
|
||||
#### RouterOS v7
|
||||
###### Kid Control Devices
|
||||
|
||||
### Client Traffic for RouterOS v7+
|
||||
In RouterOS v7 Accounting feature is deprecated so alternative approach for is to use
|
||||
Kid Control Devices feature (IP - Kid Control - Devices).
|
||||
|
||||
|
@ -141,6 +148,22 @@ Simple dummy Kid entry can be defined with
|
|||
|
||||

|
||||
|
||||
## UPS sensor
|
||||
Monitor your UPS.
|
||||
|
||||

|
||||
|
||||
## GPS sensors
|
||||
Monitor your GPS coordinates.
|
||||
|
||||

|
||||
|
||||
## Update sensor
|
||||
Update Mikrotik OS and firmare directly from Home Assistant.
|
||||
|
||||

|
||||

|
||||
|
||||
# Install integration
|
||||
This integration is distributed using [HACS](https://hacs.xyz/).
|
||||
|
||||
|
@ -156,9 +179,11 @@ Use integration master branch instead of latest release to keep up with RouterOS
|
|||
|
||||
## Setup integration
|
||||
1. Create user for homeassistant on your mikrotik router with following permissions:
|
||||
* read, write, api, test, policy
|
||||
* read, write, api, reboot, policy, test
|
||||
* lower permissions are supported, but it will limit functionality (read and api permissions are mandatory).
|
||||
* system health sensors won't be available without write & reboot permissions. this limitation is on mikrotik side.
|
||||
2. If you want to be able to execute scripts on your mikrotik router from HA, script needs to have only following policies:
|
||||
* read, write, test
|
||||
* read, write
|
||||
or check "Don't Require Permissions" option
|
||||
3. Setup this integration for your Mikrotik device in Home Assistant via `Configuration -> Integrations -> Add -> Mikrotik Router`.
|
||||
You can add this integration several times for different devices.
|
||||
|
@ -174,13 +199,16 @@ NOTES:
|
|||
|
||||
## Configuration
|
||||
First options page:
|
||||
|
||||

|
||||
* "Scan interval" - Scan/refresh time in seconds. HA needs to be reloaded for scan interval change to be applied
|
||||
* "Unit of measurement" - Traffic sensor measurement (bps, Kbps, Mbps, B/s, KB/s, MB/s)
|
||||
* "Show client MAC and IP on interfaces" - Display connected IP and MAC address for devices connected to ports on router
|
||||
* "Track network devices timeout" - Tracked devices will be marked as away after timeout (does not apply to Mikrotik wireless and caps-man)
|
||||
* "Zone for device tracker" - Add new tracked devices to a specified Home Assistant zone
|
||||
|
||||
Second options page:
|
||||
|
||||

|
||||
|
||||
Select sensors you want to use in Home Assistant.
|
||||
|
@ -193,6 +221,11 @@ After you have created your account [click here to join Mikrotik Router project
|
|||
|
||||
If you want to add translations for a language that is not listed please [open a Feature request](https://github.com/tomaae/homeassistant-mikrotik_router/issues/new?labels=enhancement&title=%5BLokalise%5D%20Add%20new%20translations%20language).
|
||||
|
||||
## Diagnostics
|
||||
Download diagnostics data for investigation:
|
||||
|
||||

|
||||
|
||||
## Enabling debug
|
||||
To enable debug for Mikrotik router integration, add following to your configuration.yaml:
|
||||
```
|
||||
|
|
|
@ -1,79 +1,98 @@
|
|||
"""Mikrotik Router integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
import logging
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import device_registry
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
from .const import (
|
||||
PLATFORMS,
|
||||
DOMAIN,
|
||||
RUN_SCRIPT_COMMAND,
|
||||
)
|
||||
from .mikrotik_controller import MikrotikControllerData
|
||||
from homeassistant.const import CONF_VERIFY_SSL
|
||||
|
||||
from .const import PLATFORMS, DOMAIN, DEFAULT_VERIFY_SSL
|
||||
from .coordinator import MikrotikData, MikrotikCoordinator, MikrotikTrackerCoordinator
|
||||
|
||||
SCRIPT_SCHEMA = vol.Schema(
|
||||
{vol.Required("router"): cv.string, vol.Required("script"): cv.string}
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup
|
||||
# ---------------------------
|
||||
async def async_setup(hass, _config):
|
||||
"""Set up configured Mikrotik Controller."""
|
||||
hass.data[DOMAIN] = {}
|
||||
return True
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_listener
|
||||
# ---------------------------
|
||||
async def update_listener(hass, config_entry) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry) -> bool:
|
||||
"""Set up Mikrotik Router as config entry."""
|
||||
controller = MikrotikControllerData(hass, config_entry)
|
||||
await controller.async_hwinfo_update()
|
||||
if not controller.connected():
|
||||
raise ConfigEntryNotReady(f"Cannot connect to host")
|
||||
|
||||
await controller.async_update()
|
||||
|
||||
if not controller.data:
|
||||
raise ConfigEntryNotReady()
|
||||
|
||||
await controller.async_init()
|
||||
hass.data[DOMAIN][config_entry.entry_id] = controller
|
||||
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
config_entry.async_on_unload(config_entry.add_update_listener(update_listener))
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, RUN_SCRIPT_COMMAND, controller.run_script, schema=SCRIPT_SCHEMA
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up a config entry."""
|
||||
coordinator = MikrotikCoordinator(hass, config_entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
coordinatorTracker = MikrotikTrackerCoordinator(hass, config_entry, coordinator)
|
||||
await coordinatorTracker.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = MikrotikData(
|
||||
data_coordinator=coordinator,
|
||||
tracker_coordinator=coordinatorTracker,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_reload_entry
|
||||
# ---------------------------
|
||||
async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""Reload the config entry when it changed."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_unload_entry
|
||||
# ---------------------------
|
||||
async def async_unload_entry(hass, config_entry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
if unload_ok:
|
||||
controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
await controller.async_reset()
|
||||
hass.services.async_remove(DOMAIN, RUN_SCRIPT_COMMAND)
|
||||
):
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_remove_config_entry_device
|
||||
# ---------------------------
|
||||
async def async_remove_config_entry_device(
|
||||
hass, config_entry: ConfigEntry, device_entry: device_registry.DeviceEntry
|
||||
) -> bool:
|
||||
"""Remove a config entry from a device."""
|
||||
return True
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_migrate_entry
|
||||
# ---------------------------
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
_LOGGER.debug(
|
||||
"Migrating configuration from version %s.%s",
|
||||
config_entry.version,
|
||||
config_entry.minor_version,
|
||||
)
|
||||
|
||||
if config_entry.version < 2:
|
||||
new_data = {**config_entry.data}
|
||||
new_data[CONF_VERIFY_SSL] = DEFAULT_VERIFY_SSL
|
||||
hass.config_entries.async_update_entry(config_entry, data=new_data, version=2)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Migration to configuration version %s.%s successful",
|
||||
config_entry.version,
|
||||
config_entry.minor_version,
|
||||
)
|
||||
return True
|
||||
|
|
|
@ -1,77 +1,83 @@
|
|||
"""API parser functions for Mikrotik Router."""
|
||||
"""API parser for JSON APIs."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from logging import getLogger
|
||||
|
||||
from pytz import utc
|
||||
from voluptuous import Optional
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .const import TO_REDACT
|
||||
|
||||
TO_REDACT = {
|
||||
"ip-address",
|
||||
"client-ip-address",
|
||||
"address",
|
||||
"active-address",
|
||||
"mac-address",
|
||||
"active-mac-address",
|
||||
"orig-mac-address",
|
||||
"port-mac-address",
|
||||
"client-mac-address",
|
||||
"client-id",
|
||||
"active-client-id",
|
||||
"eeprom",
|
||||
"sfp-vendor-serial",
|
||||
"gateway",
|
||||
"dns-server",
|
||||
"wins-server",
|
||||
"ntp-server",
|
||||
"caps-manager",
|
||||
"serial-number",
|
||||
"source",
|
||||
"from-addresses",
|
||||
"to-addresses",
|
||||
"src-address",
|
||||
"dst-address",
|
||||
"username",
|
||||
"password",
|
||||
"caller-id",
|
||||
"target",
|
||||
"ssid",
|
||||
}
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# utc_from_timestamp
|
||||
# ---------------------------
|
||||
def utc_from_timestamp(timestamp: float) -> datetime:
|
||||
"""Return a UTC time from a timestamp."""
|
||||
return utc.localize(datetime.utcfromtimestamp(timestamp))
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# from_entry
|
||||
# ---------------------------
|
||||
def from_entry(entry, param, default="") -> str:
|
||||
"""Validate and return str value from Mikrotik API dict"""
|
||||
if param not in entry:
|
||||
"""Validate and return str value an API dict."""
|
||||
if "/" in param:
|
||||
for tmp_param in param.split("/"):
|
||||
if isinstance(entry, dict) and tmp_param in entry:
|
||||
entry = entry[tmp_param]
|
||||
else:
|
||||
return default
|
||||
|
||||
ret = entry
|
||||
elif param in entry:
|
||||
ret = entry[param]
|
||||
else:
|
||||
return default
|
||||
|
||||
return (
|
||||
entry[param][:255]
|
||||
if isinstance(entry[param], str) and len(entry[param]) > 255
|
||||
else entry[param]
|
||||
)
|
||||
if default != "":
|
||||
if isinstance(ret, str):
|
||||
ret = str(ret)
|
||||
elif isinstance(ret, int):
|
||||
ret = int(ret)
|
||||
elif isinstance(ret, float):
|
||||
ret = round(float(ret), 2)
|
||||
|
||||
return ret[:255] if isinstance(ret, str) and len(ret) > 255 else ret
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# from_entry_bool
|
||||
# ---------------------------
|
||||
def from_entry_bool(entry, param, default=False, reverse=False) -> bool:
|
||||
"""Validate and return a bool value from a Mikrotik API dict"""
|
||||
if param not in entry:
|
||||
return default
|
||||
"""Validate and return a bool value from an API dict."""
|
||||
if "/" in param:
|
||||
for tmp_param in param.split("/"):
|
||||
if isinstance(entry, dict) and tmp_param in entry:
|
||||
entry = entry[tmp_param]
|
||||
else:
|
||||
return default
|
||||
|
||||
if not reverse:
|
||||
ret = entry
|
||||
elif param in entry:
|
||||
ret = entry[param]
|
||||
else:
|
||||
if entry[param]:
|
||||
ret = False
|
||||
else:
|
||||
ret = True
|
||||
return default
|
||||
|
||||
return ret
|
||||
if isinstance(ret, str):
|
||||
if ret in ("on", "On", "ON", "yes", "Yes", "YES", "up", "Up", "UP"):
|
||||
ret = True
|
||||
elif ret in ("off", "Off", "OFF", "no", "No", "NO", "down", "Down", "DOWN"):
|
||||
ret = False
|
||||
|
||||
if not isinstance(ret, bool):
|
||||
ret = default
|
||||
|
||||
return not ret if reverse else ret
|
||||
|
||||
|
||||
# ---------------------------
|
||||
|
@ -89,15 +95,17 @@ def parse_api(
|
|||
only=None,
|
||||
skip=None,
|
||||
) -> dict:
|
||||
"""Get data from API"""
|
||||
debug = False
|
||||
if _LOGGER.getEffectiveLevel() == 10:
|
||||
debug = True
|
||||
"""Get data from API."""
|
||||
debug = _LOGGER.getEffectiveLevel() == 10
|
||||
if type(source) == dict:
|
||||
tmp = source
|
||||
source = [tmp]
|
||||
|
||||
if not source:
|
||||
if not key and not key_search:
|
||||
data = fill_defaults(data, vals)
|
||||
return data
|
||||
|
||||
if debug:
|
||||
_LOGGER.debug("Processing source %s", async_redact_data(source, TO_REDACT))
|
||||
|
||||
|
@ -137,13 +145,10 @@ def parse_api(
|
|||
# get_uid
|
||||
# ---------------------------
|
||||
def get_uid(entry, key, key_secondary, key_search, keymap) -> Optional(str):
|
||||
"""Get UID for data list"""
|
||||
"""Get UID for data list."""
|
||||
uid = None
|
||||
if not key_search:
|
||||
key_primary_found = True
|
||||
if key not in entry:
|
||||
key_primary_found = False
|
||||
|
||||
key_primary_found = key in entry
|
||||
if key_primary_found and key not in entry and not entry[key]:
|
||||
return None
|
||||
|
||||
|
@ -157,38 +162,31 @@ def get_uid(entry, key, key_secondary, key_search, keymap) -> Optional(str):
|
|||
return None
|
||||
|
||||
uid = entry[key_secondary]
|
||||
elif keymap and key_search in entry and entry[key_search] in keymap:
|
||||
uid = keymap[entry[key_search]]
|
||||
else:
|
||||
if keymap and key_search in entry and entry[key_search] in keymap:
|
||||
uid = keymap[entry[key_search]]
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
return uid if uid else None
|
||||
return uid or None
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# generate_keymap
|
||||
# ---------------------------
|
||||
def generate_keymap(data, key_search) -> Optional(dict):
|
||||
"""Generate keymap"""
|
||||
if not key_search:
|
||||
return None
|
||||
|
||||
keymap = {}
|
||||
for uid in data:
|
||||
if key_search not in data[uid]:
|
||||
continue
|
||||
|
||||
keymap[data[uid][key_search]] = uid
|
||||
|
||||
return keymap
|
||||
"""Generate keymap."""
|
||||
return (
|
||||
{data[uid][key_search]: uid for uid in data if key_search in data[uid]}
|
||||
if key_search
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# matches_only
|
||||
# ---------------------------
|
||||
def matches_only(entry, only) -> bool:
|
||||
"""Return True if all variables are matched"""
|
||||
"""Return True if all variables are matched."""
|
||||
ret = False
|
||||
for val in only:
|
||||
if val["key"] in entry and entry[val["key"]] == val["value"]:
|
||||
|
@ -204,7 +202,7 @@ def matches_only(entry, only) -> bool:
|
|||
# can_skip
|
||||
# ---------------------------
|
||||
def can_skip(entry, skip) -> bool:
|
||||
"""Return True if at least one variable matches"""
|
||||
"""Return True if at least one variable matches."""
|
||||
ret = False
|
||||
for val in skip:
|
||||
if val["name"] in entry and entry[val["name"]] == val["value"]:
|
||||
|
@ -222,7 +220,7 @@ def can_skip(entry, skip) -> bool:
|
|||
# fill_defaults
|
||||
# ---------------------------
|
||||
def fill_defaults(data, vals) -> dict:
|
||||
"""Fill defaults if source is not present"""
|
||||
"""Fill defaults if source is not present."""
|
||||
for val in vals:
|
||||
_name = val["name"]
|
||||
_type = val["type"] if "type" in val else "str"
|
||||
|
@ -251,11 +249,12 @@ def fill_defaults(data, vals) -> dict:
|
|||
# fill_vals
|
||||
# ---------------------------
|
||||
def fill_vals(data, entry, uid, vals) -> dict:
|
||||
"""Fill all data"""
|
||||
"""Fill all data."""
|
||||
for val in vals:
|
||||
_name = val["name"]
|
||||
_type = val["type"] if "type" in val else "str"
|
||||
_source = val["source"] if "source" in val else _name
|
||||
_convert = val["convert"] if "convert" in val else None
|
||||
|
||||
if _type == "str":
|
||||
_default = val["default"] if "default" in val else ""
|
||||
|
@ -280,6 +279,19 @@ def fill_vals(data, entry, uid, vals) -> dict:
|
|||
entry, _source, default=_default, reverse=_reverse
|
||||
)
|
||||
|
||||
if _convert == "utc_from_timestamp":
|
||||
if uid:
|
||||
if isinstance(data[uid][_name], int) and data[uid][_name] > 0:
|
||||
if data[uid][_name] > 100000000000:
|
||||
data[uid][_name] = data[uid][_name] / 1000
|
||||
|
||||
data[uid][_name] = utc_from_timestamp(data[uid][_name])
|
||||
elif isinstance(data[_name], int) and data[_name] > 0:
|
||||
if data[_name] > 100000000000:
|
||||
data[_name] = data[_name] / 1000
|
||||
|
||||
data[_name] = utc_from_timestamp(data[_name])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
@ -287,16 +299,16 @@ def fill_vals(data, entry, uid, vals) -> dict:
|
|||
# fill_ensure_vals
|
||||
# ---------------------------
|
||||
def fill_ensure_vals(data, uid, ensure_vals) -> dict:
|
||||
"""Add required keys which are not available in data"""
|
||||
"""Add required keys which are not available in data."""
|
||||
for val in ensure_vals:
|
||||
if uid:
|
||||
if val["name"] not in data[uid]:
|
||||
_default = val["default"] if "default" in val else ""
|
||||
data[uid][val["name"]] = _default
|
||||
else:
|
||||
if val["name"] not in data:
|
||||
_default = val["default"] if "default" in val else ""
|
||||
data[val["name"]] = _default
|
||||
|
||||
elif val["name"] not in data:
|
||||
_default = val["default"] if "default" in val else ""
|
||||
data[val["name"]] = _default
|
||||
|
||||
return data
|
||||
|
||||
|
@ -305,7 +317,7 @@ def fill_ensure_vals(data, uid, ensure_vals) -> dict:
|
|||
# fill_vals_proc
|
||||
# ---------------------------
|
||||
def fill_vals_proc(data, uid, vals_proc) -> dict:
|
||||
"""Add custom keys"""
|
||||
"""Add custom keys."""
|
||||
_data = data[uid] if uid else data
|
||||
for val_sub in vals_proc:
|
||||
_name = None
|
||||
|
@ -326,17 +338,11 @@ def fill_vals_proc(data, uid, vals_proc) -> dict:
|
|||
if _action == "combine":
|
||||
if "key" in val:
|
||||
tmp = _data[val["key"]] if val["key"] in _data else "unknown"
|
||||
if not _value:
|
||||
_value = tmp
|
||||
else:
|
||||
_value = f"{_value}{tmp}"
|
||||
_value = f"{_value}{tmp}" if _value else tmp
|
||||
|
||||
if "text" in val:
|
||||
tmp = val["text"]
|
||||
if not _value:
|
||||
_value = tmp
|
||||
else:
|
||||
_value = f"{_value}{tmp}"
|
||||
_value = f"{_value}{tmp}" if _value else tmp
|
||||
|
||||
if _name and _value:
|
||||
if uid:
|
||||
|
|
|
@ -1,332 +1,80 @@
|
|||
"""Support for the Mikrotik Router binary sensor service."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from collections.abc import Mapping
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .binary_sensor_types import (
|
||||
SENSOR_TYPES,
|
||||
SENSOR_SERVICES,
|
||||
DEVICE_ATTRIBUTES_IFACE_ETHER,
|
||||
DEVICE_ATTRIBUTES_IFACE_SFP,
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS,
|
||||
DEVICE_ATTRIBUTES_NETWATCH,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_HOST,
|
||||
ATTR_ATTRIBUTION,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from .helper import format_attribute
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
ATTRIBUTION,
|
||||
CONF_SENSOR_PPP,
|
||||
DEFAULT_SENSOR_PPP,
|
||||
CONF_SENSOR_PORT_TRACKER,
|
||||
DEFAULT_SENSOR_PORT_TRACKER,
|
||||
CONF_SENSOR_NETWATCH_TRACKER,
|
||||
DEFAULT_SENSOR_NETWATCH_TRACKER,
|
||||
)
|
||||
from .binary_sensor_types import (
|
||||
MikrotikBinarySensorEntityDescription,
|
||||
SENSOR_TYPES,
|
||||
DEVICE_ATTRIBUTES_IFACE_ETHER,
|
||||
DEVICE_ATTRIBUTES_IFACE_SFP,
|
||||
)
|
||||
from .entity import MikrotikEntity, async_add_entities
|
||||
from .helper import format_attribute
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up device tracker for Mikrotik Router component."""
|
||||
inst = config_entry.data[CONF_NAME]
|
||||
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
sensors = {}
|
||||
|
||||
@callback
|
||||
def update_controller():
|
||||
"""Update the values of the controller."""
|
||||
update_items(
|
||||
inst, config_entry, mikrotik_controller, async_add_entities, sensors
|
||||
)
|
||||
|
||||
mikrotik_controller.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, mikrotik_controller.signal_update, update_controller
|
||||
)
|
||||
)
|
||||
update_controller()
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikBinarySensor": MikrotikBinarySensor,
|
||||
"MikrotikPPPSecretBinarySensor": MikrotikPPPSecretBinarySensor,
|
||||
"MikrotikPortBinarySensor": MikrotikPortBinarySensor,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_items
|
||||
# MikrotikBinarySensor
|
||||
# ---------------------------
|
||||
@callback
|
||||
def update_items(inst, config_entry, mikrotik_controller, async_add_entities, sensors):
|
||||
"""Update sensor state from the controller."""
|
||||
new_sensors = []
|
||||
|
||||
for sensor, sid_func in zip(
|
||||
# Sensor type name
|
||||
["ppp_tracker", "interface"],
|
||||
# Entity function
|
||||
[MikrotikControllerPPPSecretBinarySensor, MikrotikControllerPortBinarySensor],
|
||||
):
|
||||
if sensor == "interface" and not config_entry.options.get(
|
||||
CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER
|
||||
):
|
||||
continue
|
||||
|
||||
uid_sensor = SENSOR_TYPES[sensor]
|
||||
for uid in mikrotik_controller.data[uid_sensor.data_path]:
|
||||
uid_data = mikrotik_controller.data[uid_sensor.data_path]
|
||||
if uid_sensor.data_path == "interface" and uid_data[uid]["type"] == "wlan":
|
||||
continue
|
||||
|
||||
item_id = f"{inst}-{sensor}-{uid_data[uid][uid_sensor.data_reference]}"
|
||||
_LOGGER.debug("Updating binary sensor %s", item_id)
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
sensors[item_id] = sid_func(
|
||||
inst=inst,
|
||||
uid=uid,
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_sensor,
|
||||
config_entry=config_entry,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
for sensor in SENSOR_TYPES:
|
||||
if sensor.startswith("system_"):
|
||||
uid_sensor = SENSOR_TYPES[sensor]
|
||||
item_id = f"{inst}-{sensor}"
|
||||
_LOGGER.debug("Updating binary sensor %s", item_id)
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
sensors[item_id] = MikrotikControllerBinarySensor(
|
||||
inst=inst,
|
||||
uid="",
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_sensor,
|
||||
config_entry=config_entry,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
#
|
||||
# # Add switches
|
||||
# for sid, sid_uid, sid_name, sid_ref, sid_attr, sid_func in zip(
|
||||
# # Data point name
|
||||
# ["ppp_secret", "interface"],
|
||||
# # Data point unique id
|
||||
# ["name", "default-name"],
|
||||
# # Entry Name
|
||||
# ["name", "name"],
|
||||
# # Entry Unique id
|
||||
# ["name", "port-mac-address"],
|
||||
# # Attr
|
||||
# [None, DEVICE_ATTRIBUTES_IFACE],
|
||||
# # Tracker function
|
||||
# [
|
||||
# MikrotikControllerPPPSecretBinarySensor,
|
||||
# MikrotikControllerPortBinarySensor,
|
||||
# ],
|
||||
# ):
|
||||
# if (
|
||||
# sid_func == MikrotikControllerPortBinarySensor
|
||||
# and not config_entry.options.get(
|
||||
# CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER
|
||||
# )
|
||||
# ):
|
||||
# continue
|
||||
# for uid in mikrotik_controller.data[sid]:
|
||||
# if (
|
||||
# # Skip if interface is wlan
|
||||
# sid == "interface"
|
||||
# and mikrotik_controller.data[sid][uid]["type"] == "wlan"
|
||||
# ):
|
||||
# continue
|
||||
# # Update entity
|
||||
# item_id = f"{inst}-{sid}-{mikrotik_controller.data[sid][uid][sid_uid]}"
|
||||
# _LOGGER.debug("Updating binary_sensor %s", item_id)
|
||||
# if item_id in sensors:
|
||||
# if sensors[item_id].enabled:
|
||||
# sensors[item_id].async_schedule_update_ha_state()
|
||||
# continue
|
||||
#
|
||||
# # Create new entity
|
||||
# sid_data = {
|
||||
# "sid": sid,
|
||||
# "sid_uid": sid_uid,
|
||||
# "sid_name": sid_name,
|
||||
# "sid_ref": sid_ref,
|
||||
# "sid_attr": sid_attr,
|
||||
# }
|
||||
# sensors[item_id] = sid_func(
|
||||
# inst, uid, mikrotik_controller, config_entry, sid_data
|
||||
# )
|
||||
# new_sensors.append(sensors[item_id])
|
||||
#
|
||||
# for sensor in SENSOR_TYPES:
|
||||
# item_id = f"{inst}-{sensor}"
|
||||
# _LOGGER.debug("Updating binary_sensor %s", item_id)
|
||||
# if item_id in sensors:
|
||||
# if sensors[item_id].enabled:
|
||||
# sensors[item_id].async_schedule_update_ha_state()
|
||||
# continue
|
||||
#
|
||||
# sensors[item_id] = MikrotikControllerBinarySensor(
|
||||
# mikrotik_controller=mikrotik_controller, inst=inst, sid_data=sensor
|
||||
# )
|
||||
# new_sensors.append(sensors[item_id])
|
||||
|
||||
if new_sensors:
|
||||
async_add_entities(new_sensors, True)
|
||||
|
||||
|
||||
class MikrotikControllerBinarySensor(BinarySensorEntity):
|
||||
class MikrotikBinarySensor(MikrotikEntity, BinarySensorEntity):
|
||||
"""Define an Mikrotik Controller Binary Sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inst,
|
||||
uid: "",
|
||||
mikrotik_controller,
|
||||
entity_description: MikrotikBinarySensorEntityDescription,
|
||||
config_entry,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.entity_description = entity_description
|
||||
self._config_entry = config_entry
|
||||
self._inst = inst
|
||||
self._ctrl = mikrotik_controller
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._uid = uid
|
||||
if self._uid:
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path][
|
||||
self._uid
|
||||
]
|
||||
else:
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path]
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if self._uid:
|
||||
if self.entity_description.name:
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
||||
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]}"
|
||||
else:
|
||||
return f"{self._inst} {self.entity_description.name}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
if self._uid:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}-{self._data[self.entity_description.data_reference].lower()}"
|
||||
else:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
return self._ctrl.connected()
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._data[self.entity_description.data_is_on]
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
if self.entity_description.icon_enabled:
|
||||
if self._data[self.entity_description.data_is_on]:
|
||||
if self._data[self.entity_description.data_attribute]:
|
||||
return self.entity_description.icon_enabled
|
||||
else:
|
||||
return self.entity_description.icon_disabled
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a description for device registry."""
|
||||
dev_connection = DOMAIN
|
||||
dev_connection_value = self.entity_description.data_reference
|
||||
dev_group = self.entity_description.ha_group
|
||||
if self.entity_description.ha_group == "System":
|
||||
dev_group = self._ctrl.data["resource"]["board-name"]
|
||||
dev_connection_value = self._ctrl.data["routerboard"]["serial-number"]
|
||||
|
||||
if self.entity_description.ha_group.startswith("data__"):
|
||||
dev_group = self.entity_description.ha_group[6:]
|
||||
if dev_group in self._data:
|
||||
dev_group = self._data[dev_group]
|
||||
dev_connection_value = dev_group
|
||||
|
||||
if self.entity_description.ha_connection:
|
||||
dev_connection = self.entity_description.ha_connection
|
||||
|
||||
if self.entity_description.ha_connection_value:
|
||||
dev_connection_value = self.entity_description.ha_connection_value
|
||||
if dev_connection_value.startswith("data__"):
|
||||
dev_connection_value = dev_connection_value[6:]
|
||||
dev_connection_value = self._data[dev_connection_value]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
identifiers={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{self._inst} {dev_group}",
|
||||
model=f"{self._ctrl.data['resource']['board-name']}",
|
||||
manufacturer=f"{self._ctrl.data['resource']['platform']}",
|
||||
sw_version=f"{self._ctrl.data['resource']['version']}",
|
||||
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}",
|
||||
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"),
|
||||
)
|
||||
|
||||
if "mac-address" in self.entity_description.data_reference:
|
||||
dev_group = self._data[self.entity_description.data_name]
|
||||
dev_manufacturer = ""
|
||||
if dev_connection_value in self._ctrl.data["host"]:
|
||||
dev_group = self._ctrl.data["host"][dev_connection_value]["host-name"]
|
||||
dev_manufacturer = self._ctrl.data["host"][dev_connection_value][
|
||||
"manufacturer"
|
||||
]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{dev_group}",
|
||||
manufacturer=f"{dev_manufacturer}",
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
f"{self._ctrl.data['routerboard']['serial-number']}",
|
||||
),
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
for variable in self.entity_description.data_attributes_list:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added to hass."""
|
||||
_LOGGER.debug("New binary sensor %s (%s)", self._inst, self.unique_id)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerPPPSecretBinarySensor
|
||||
# MikrotikPPPSecretBinarySensor
|
||||
# ---------------------------
|
||||
class MikrotikControllerPPPSecretBinarySensor(MikrotikControllerBinarySensor):
|
||||
class MikrotikPPPSecretBinarySensor(MikrotikBinarySensor):
|
||||
"""Representation of a network device."""
|
||||
|
||||
@property
|
||||
|
@ -337,24 +85,22 @@ class MikrotikControllerPPPSecretBinarySensor(MikrotikControllerBinarySensor):
|
|||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
if not self.option_sensor_ppp:
|
||||
return False
|
||||
return (
|
||||
self._data[self.entity_description.data_attribute]
|
||||
if self.option_sensor_ppp
|
||||
else False
|
||||
)
|
||||
|
||||
return self._data[self.entity_description.data_is_on]
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
if not self.option_sensor_ppp:
|
||||
return False
|
||||
|
||||
return self._ctrl.connected()
|
||||
# @property
|
||||
# def available(self) -> bool:
|
||||
# """Return if controller is available."""
|
||||
# return self._ctrl.connected() if self.option_sensor_ppp else False
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerPortBinarySensor
|
||||
# MikrotikPortBinarySensor
|
||||
# ---------------------------
|
||||
class MikrotikControllerPortBinarySensor(MikrotikControllerBinarySensor):
|
||||
class MikrotikPortBinarySensor(MikrotikBinarySensor):
|
||||
"""Representation of a network port."""
|
||||
|
||||
@property
|
||||
|
@ -364,18 +110,15 @@ class MikrotikControllerPortBinarySensor(MikrotikControllerBinarySensor):
|
|||
CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
if not self.option_sensor_port_tracker:
|
||||
return False
|
||||
|
||||
return self._ctrl.connected()
|
||||
# @property
|
||||
# def available(self) -> bool:
|
||||
# """Return if controller is available."""
|
||||
# return self._ctrl.connected() if self.option_sensor_port_tracker else False
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
if self._data[self.entity_description.data_is_on]:
|
||||
if self._data[self.entity_description.data_attribute]:
|
||||
icon = self.entity_description.icon_enabled
|
||||
else:
|
||||
icon = self.entity_description.icon_disabled
|
||||
|
@ -400,4 +143,9 @@ class MikrotikControllerPortBinarySensor(MikrotikControllerBinarySensor):
|
|||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
elif self._data["type"] == "wlan":
|
||||
for variable in DEVICE_ATTRIBUTES_IFACE_WIRELESS:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
"""Definitions for Mikrotik Router sensor entities."""
|
||||
"""Definitions for Mikrotik Router binary sensor entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
|
@ -66,39 +69,92 @@ DEVICE_ATTRIBUTES_IFACE_SFP = [
|
|||
"eeprom-checksum",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS = [
|
||||
"ssid",
|
||||
"mode",
|
||||
"radio-name",
|
||||
"interface-type",
|
||||
"country",
|
||||
"installation",
|
||||
"antenna-gain",
|
||||
"frequency",
|
||||
"band",
|
||||
"channel-width",
|
||||
"secondary-frequency",
|
||||
"wireless-protocol",
|
||||
"rate-set",
|
||||
"distance",
|
||||
"tx-power-mode",
|
||||
"vlan-id",
|
||||
"wds-mode",
|
||||
"wds-default-bridge",
|
||||
"bridge-mode",
|
||||
"hide-ssid",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_UPS = [
|
||||
"name",
|
||||
"offline-time",
|
||||
"min-runtime",
|
||||
"alarm-setting",
|
||||
"model",
|
||||
"serial",
|
||||
"manufacture-date",
|
||||
"nominal-battery-voltage",
|
||||
"runtime-left",
|
||||
"battery-charge",
|
||||
"battery-voltage",
|
||||
"line-voltage",
|
||||
"load",
|
||||
"hid-self-test",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_NETWATCH = [
|
||||
"host",
|
||||
"type",
|
||||
"interval",
|
||||
"port",
|
||||
"http-codes",
|
||||
"status",
|
||||
"comment",
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MikrotikBinarySensorEntityDescription(BinarySensorEntityDescription):
|
||||
"""Class describing mikrotik entities."""
|
||||
|
||||
icon_enabled: str = ""
|
||||
icon_disabled: str = ""
|
||||
ha_group: str = ""
|
||||
ha_connection: str = ""
|
||||
ha_connection_value: str = ""
|
||||
data_path: str = ""
|
||||
data_is_on: str = "available"
|
||||
data_name: str = ""
|
||||
data_uid: str = ""
|
||||
data_reference: str = ""
|
||||
icon_enabled: str | None = None
|
||||
icon_disabled: str | None = None
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str = "available"
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikBinarySensor"
|
||||
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"system_fwupdate": MikrotikBinarySensorEntityDescription(
|
||||
key="system_fwupdate",
|
||||
name="Firmware update",
|
||||
SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
MikrotikBinarySensorEntityDescription(
|
||||
key="system_ups",
|
||||
name="UPS",
|
||||
icon_enabled="",
|
||||
icon_disabled="",
|
||||
device_class=BinarySensorDeviceClass.UPDATE,
|
||||
device_class=BinarySensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="fw-update",
|
||||
data_name="",
|
||||
data_path="ups",
|
||||
data_attribute="on-line",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_UPS,
|
||||
),
|
||||
"ppp_tracker": MikrotikBinarySensorEntityDescription(
|
||||
MikrotikBinarySensorEntityDescription(
|
||||
key="ppp_tracker",
|
||||
name="PPP",
|
||||
icon_enabled="mdi:account-network-outline",
|
||||
|
@ -108,15 +164,16 @@ SENSOR_TYPES = {
|
|||
ha_connection=DOMAIN,
|
||||
ha_connection_value="PPP",
|
||||
data_path="ppp_secret",
|
||||
data_is_on="connected",
|
||||
data_attribute="connected",
|
||||
data_name="name",
|
||||
data_uid="name",
|
||||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_PPP_SECRET,
|
||||
func="MikrotikPPPSecretBinarySensor",
|
||||
),
|
||||
"interface": MikrotikBinarySensorEntityDescription(
|
||||
MikrotikBinarySensorEntityDescription(
|
||||
key="interface",
|
||||
name="",
|
||||
name="Connection",
|
||||
icon_enabled="mdi:lan-connect",
|
||||
icon_disabled="mdi:lan-pending",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
|
@ -124,10 +181,31 @@ SENSOR_TYPES = {
|
|||
ha_connection=CONNECTION_NETWORK_MAC,
|
||||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_is_on="running",
|
||||
data_name="name",
|
||||
data_attribute="running",
|
||||
data_name="default-name",
|
||||
data_uid="default-name",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikPortBinarySensor",
|
||||
),
|
||||
}
|
||||
MikrotikBinarySensorEntityDescription(
|
||||
key="netwatch",
|
||||
name="Netwatch",
|
||||
icon_enabled="mdi:lan-connect",
|
||||
icon_disabled="mdi:lan-pending",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
ha_group="Netwatch",
|
||||
ha_connection=DOMAIN,
|
||||
ha_connection_value="Netwatch",
|
||||
data_path="netwatch",
|
||||
data_attribute="status",
|
||||
data_name="host",
|
||||
data_name_comment=True,
|
||||
data_uid="host",
|
||||
data_reference="host",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_NETWATCH,
|
||||
func="MikrotikBinarySensor",
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_SERVICES = {}
|
||||
|
|
|
@ -1,199 +1,62 @@
|
|||
"""Support for the Mikrotik Router buttons."""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from .helper import format_attribute
|
||||
from .const import DOMAIN, ATTRIBUTION
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .entity import MikrotikEntity, async_add_entities
|
||||
from .button_types import (
|
||||
SENSOR_TYPES,
|
||||
SENSOR_SERVICES,
|
||||
)
|
||||
from .exceptions import ApiEntryNotFound
|
||||
|
||||
DEVICE_ATTRIBUTES_SCRIPT = [
|
||||
"last-started",
|
||||
"run-count",
|
||||
]
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up buttons for Mikrotik Router component."""
|
||||
inst = config_entry.data[CONF_NAME]
|
||||
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
buttons = {}
|
||||
|
||||
@callback
|
||||
def update_controller():
|
||||
"""Update the values of the controller."""
|
||||
update_items(inst, mikrotik_controller, async_add_entities, buttons)
|
||||
|
||||
mikrotik_controller.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, mikrotik_controller.signal_update, update_controller
|
||||
)
|
||||
)
|
||||
|
||||
update_controller()
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikButton": MikrotikButton,
|
||||
"MikrotikScriptButton": MikrotikScriptButton,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_items
|
||||
# MikrotikButton
|
||||
# ---------------------------
|
||||
@callback
|
||||
def update_items(inst, mikrotik_controller, async_add_entities, buttons):
|
||||
"""Update device button state from the controller."""
|
||||
new_buttons = []
|
||||
|
||||
# Add buttons
|
||||
for sid, sid_uid, sid_name, sid_ref, sid_attr, sid_func in zip(
|
||||
# Data point name
|
||||
[
|
||||
"script",
|
||||
],
|
||||
# Data point unique id
|
||||
[
|
||||
"name",
|
||||
],
|
||||
# Entry Name
|
||||
[
|
||||
"name",
|
||||
],
|
||||
# Entry Unique id
|
||||
[
|
||||
"name",
|
||||
],
|
||||
# Attr
|
||||
[
|
||||
DEVICE_ATTRIBUTES_SCRIPT,
|
||||
],
|
||||
# Button function
|
||||
[
|
||||
MikrotikControllerScriptButton,
|
||||
],
|
||||
):
|
||||
for uid in mikrotik_controller.data[sid]:
|
||||
item_id = f"{inst}-{sid}-{mikrotik_controller.data[sid][uid][sid_uid]}"
|
||||
|
||||
_LOGGER.debug("Updating button %s", item_id)
|
||||
if item_id in buttons:
|
||||
if buttons[item_id].enabled:
|
||||
buttons[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
# Create new entity
|
||||
sid_data = {
|
||||
"sid": sid,
|
||||
"sid_uid": sid_uid,
|
||||
"sid_name": sid_name,
|
||||
"sid_ref": sid_ref,
|
||||
"sid_attr": sid_attr,
|
||||
}
|
||||
buttons[item_id] = sid_func(inst, uid, mikrotik_controller, sid_data)
|
||||
new_buttons.append(buttons[item_id])
|
||||
|
||||
if new_buttons:
|
||||
async_add_entities(new_buttons)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerButton
|
||||
# ---------------------------
|
||||
class MikrotikControllerButton(ButtonEntity, RestoreEntity):
|
||||
class MikrotikButton(MikrotikEntity, ButtonEntity):
|
||||
"""Representation of a button."""
|
||||
|
||||
def __init__(self, inst, uid, mikrotik_controller, sid_data):
|
||||
"""Initialize."""
|
||||
self._sid_data = sid_data
|
||||
self._inst = inst
|
||||
self._ctrl = mikrotik_controller
|
||||
self._data = mikrotik_controller.data[self._sid_data["sid"]][uid]
|
||||
|
||||
self._attrs = {
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
}
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added to hass."""
|
||||
_LOGGER.debug(
|
||||
"New button %s (%s %s)",
|
||||
self._inst,
|
||||
self._sid_data["sid"],
|
||||
self._data[self._sid_data["sid_uid"]],
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Synchronize state with controller."""
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
return self._ctrl.connected()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
return f"{self._inst} {self._sid_data['sid']} {self._data[self._sid_data['sid_name']]}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
return f"{self._inst.lower()}-{self._sid_data['sid']}_button-{self._data[self._sid_data['sid_ref']]}"
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = self._attrs
|
||||
|
||||
for variable in self._sid_data["sid_attr"]:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
async def async_press(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerScriptButton
|
||||
# MikrotikScriptButton
|
||||
# ---------------------------
|
||||
class MikrotikControllerScriptButton(MikrotikControllerButton):
|
||||
class MikrotikScriptButton(MikrotikButton):
|
||||
"""Representation of a script button."""
|
||||
|
||||
def __init__(self, inst, uid, mikrotik_controller, sid_data):
|
||||
"""Initialize."""
|
||||
super().__init__(inst, uid, mikrotik_controller, sid_data)
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
return "mdi:script-text-outline"
|
||||
|
||||
@property
|
||||
def device_info(self) -> Dict[str, Any]:
|
||||
"""Return a description for device registry."""
|
||||
info = {
|
||||
"identifiers": {
|
||||
(
|
||||
DOMAIN,
|
||||
"serial-number",
|
||||
f"{self._ctrl.data['routerboard']['serial-number']}",
|
||||
"button",
|
||||
"Scripts",
|
||||
)
|
||||
},
|
||||
"manufacturer": self._ctrl.data["resource"]["platform"],
|
||||
"model": self._ctrl.data["resource"]["board-name"],
|
||||
"name": f"{self._inst} Scripts",
|
||||
}
|
||||
return info
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Process the button press."""
|
||||
self._ctrl.run_script(self._data["name"])
|
||||
await self._ctrl.force_update()
|
||||
"""Run script using Mikrotik API"""
|
||||
try:
|
||||
self.coordinator.api.run_script(self._data["name"])
|
||||
except ApiEntryNotFound as error:
|
||||
_LOGGER.error("Failed to run script: %s", error)
|
||||
|
|
56
custom_components/mikrotik_router/button_types.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
"""Definitions for Mikrotik Router button entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
)
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
DEVICE_ATTRIBUTES_SCRIPT = [
|
||||
"last-started",
|
||||
"run-count",
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MikrotikButtonEntityDescription(SensorEntityDescription):
|
||||
"""Class describing mikrotik entities."""
|
||||
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str | None = None
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikButton"
|
||||
|
||||
|
||||
SENSOR_TYPES: tuple[MikrotikButtonEntityDescription, ...] = (
|
||||
MikrotikButtonEntityDescription(
|
||||
key="script",
|
||||
name="",
|
||||
icon="mdi:script-text-outline",
|
||||
device_class=None,
|
||||
entity_category=None,
|
||||
ha_group="Script",
|
||||
ha_connection=DOMAIN,
|
||||
ha_connection_value="Script",
|
||||
data_path="script",
|
||||
data_name="name",
|
||||
data_uid="name",
|
||||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_SCRIPT,
|
||||
func="MikrotikScriptButton",
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_SERVICES = {}
|
|
@ -12,10 +12,10 @@ from homeassistant.const import (
|
|||
CONF_NAME,
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_USERNAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
CONF_VERIFY_SSL,
|
||||
CONF_ZONE,
|
||||
STATE_HOME,
|
||||
)
|
||||
|
@ -35,6 +35,8 @@ from .const import (
|
|||
DEFAULT_SENSOR_PORT_TRAFFIC,
|
||||
CONF_SENSOR_CLIENT_TRAFFIC,
|
||||
DEFAULT_SENSOR_CLIENT_TRAFFIC,
|
||||
CONF_SENSOR_CLIENT_CAPTIVE,
|
||||
DEFAULT_SENSOR_CLIENT_CAPTIVE,
|
||||
CONF_SENSOR_SIMPLE_QUEUES,
|
||||
DEFAULT_SENSOR_SIMPLE_QUEUES,
|
||||
CONF_SENSOR_NAT,
|
||||
|
@ -53,14 +55,14 @@ from .const import (
|
|||
DEFAULT_SENSOR_ENVIRONMENT,
|
||||
CONF_TRACK_HOSTS_TIMEOUT,
|
||||
DEFAULT_TRACK_HOST_TIMEOUT,
|
||||
LIST_UNIT_OF_MEASUREMENT,
|
||||
DEFAULT_UNIT_OF_MEASUREMENT,
|
||||
DEFAULT_HOST,
|
||||
DEFAULT_USERNAME,
|
||||
DEFAULT_PASSWORD,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_DEVICE_NAME,
|
||||
DEFAULT_SSL,
|
||||
DEFAULT_VERIFY_SSL,
|
||||
DEFAULT_SENSOR_NETWATCH_TRACKER,
|
||||
CONF_SENSOR_NETWATCH_TRACKER,
|
||||
)
|
||||
from .mikrotikapi import MikrotikAPI
|
||||
|
||||
|
@ -84,7 +86,7 @@ def configured_instances(hass):
|
|||
class MikrotikControllerConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""MikrotikControllerConfigFlow class"""
|
||||
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
CONNECTION_CLASS = CONN_CLASS_LOCAL_POLL
|
||||
|
||||
def __init__(self):
|
||||
|
@ -115,6 +117,7 @@ class MikrotikControllerConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
password=user_input[CONF_PASSWORD],
|
||||
port=user_input[CONF_PORT],
|
||||
use_ssl=user_input[CONF_SSL],
|
||||
ssl_verify=user_input[CONF_VERIFY_SSL],
|
||||
)
|
||||
if not api.connect():
|
||||
errors[CONF_HOST] = api.error
|
||||
|
@ -132,9 +135,10 @@ class MikrotikControllerConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
CONF_NAME: DEFAULT_DEVICE_NAME,
|
||||
CONF_HOST: DEFAULT_HOST,
|
||||
CONF_USERNAME: DEFAULT_USERNAME,
|
||||
CONF_PASSWORD: DEFAULT_PASSWORD,
|
||||
CONF_PASSWORD: DEFAULT_USERNAME,
|
||||
CONF_PORT: DEFAULT_PORT,
|
||||
CONF_SSL: DEFAULT_SSL,
|
||||
CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL,
|
||||
},
|
||||
errors=errors,
|
||||
)
|
||||
|
@ -154,6 +158,9 @@ class MikrotikControllerConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
vol.Required(CONF_PASSWORD, default=user_input[CONF_PASSWORD]): str,
|
||||
vol.Optional(CONF_PORT, default=user_input[CONF_PORT]): int,
|
||||
vol.Optional(CONF_SSL, default=user_input[CONF_SSL]): bool,
|
||||
vol.Optional(
|
||||
CONF_VERIFY_SSL, default=user_input[CONF_VERIFY_SSL]
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
|
@ -183,6 +190,7 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow):
|
|||
|
||||
return self.async_show_form(
|
||||
step_id="basic_options",
|
||||
last_step=False,
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
|
@ -191,12 +199,6 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow):
|
|||
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
||||
),
|
||||
): int,
|
||||
vol.Optional(
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_UNIT_OF_MEASUREMENT, DEFAULT_UNIT_OF_MEASUREMENT
|
||||
),
|
||||
): vol.In(LIST_UNIT_OF_MEASUREMENT),
|
||||
vol.Optional(
|
||||
CONF_TRACK_IFACE_CLIENTS,
|
||||
default=self.config_entry.options.get(
|
||||
|
@ -251,6 +253,12 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow):
|
|||
CONF_SENSOR_CLIENT_TRAFFIC, DEFAULT_SENSOR_CLIENT_TRAFFIC
|
||||
),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SENSOR_CLIENT_CAPTIVE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_SENSOR_CLIENT_CAPTIVE, DEFAULT_SENSOR_CLIENT_CAPTIVE
|
||||
),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SENSOR_SIMPLE_QUEUES,
|
||||
default=self.config_entry.options.get(
|
||||
|
@ -281,6 +289,13 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow):
|
|||
CONF_SENSOR_KIDCONTROL, DEFAULT_SENSOR_KIDCONTROL
|
||||
),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SENSOR_NETWATCH_TRACKER,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_SENSOR_NETWATCH_TRACKER,
|
||||
DEFAULT_SENSOR_NETWATCH_TRACKER,
|
||||
),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SENSOR_PPP,
|
||||
default=self.config_entry.options.get(
|
||||
|
|
|
@ -1,59 +1,94 @@
|
|||
"""Constants used by the Mikrotik Router component and platforms."""
|
||||
from homeassistant.const import Platform
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.SENSOR,
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.SWITCH,
|
||||
Platform.BUTTON,
|
||||
]
|
||||
DOMAIN = "mikrotik_router"
|
||||
DEFAULT_NAME = "Mikrotik Router"
|
||||
ATTRIBUTION = "Data provided by Mikrotik"
|
||||
|
||||
RUN_SCRIPT_COMMAND = "run_script"
|
||||
|
||||
DEFAULT_ENCODING = "ISO-8859-1"
|
||||
DEFAULT_LOGIN_METHOD = "plain"
|
||||
|
||||
DEFAULT_HOST = "10.0.0.1"
|
||||
DEFAULT_USERNAME = "admin"
|
||||
DEFAULT_PASSWORD = ""
|
||||
DEFAULT_PORT = 0
|
||||
DEFAULT_DEVICE_NAME = "Mikrotik"
|
||||
DEFAULT_SSL = False
|
||||
|
||||
CONF_SCAN_INTERVAL = "scan_interval"
|
||||
DEFAULT_SCAN_INTERVAL = 30
|
||||
LIST_UNIT_OF_MEASUREMENT = ["bps", "Kbps", "Mbps", "B/s", "KB/s", "MB/s"]
|
||||
DEFAULT_UNIT_OF_MEASUREMENT = "Kbps"
|
||||
CONF_TRACK_IFACE_CLIENTS = "track_iface_clients"
|
||||
DEFAULT_TRACK_IFACE_CLIENTS = True
|
||||
CONF_TRACK_HOSTS = "track_network_hosts"
|
||||
DEFAULT_TRACK_HOSTS = False
|
||||
CONF_TRACK_HOSTS_TIMEOUT = "track_network_hosts_timeout"
|
||||
DEFAULT_TRACK_HOST_TIMEOUT = 180
|
||||
|
||||
CONF_SENSOR_PORT_TRACKER = "sensor_port_tracker"
|
||||
DEFAULT_SENSOR_PORT_TRACKER = False
|
||||
CONF_SENSOR_PORT_TRAFFIC = "sensor_port_traffic"
|
||||
DEFAULT_SENSOR_PORT_TRAFFIC = False
|
||||
CONF_SENSOR_CLIENT_TRAFFIC = "sensor_client_traffic"
|
||||
DEFAULT_SENSOR_CLIENT_TRAFFIC = False
|
||||
CONF_SENSOR_SIMPLE_QUEUES = "sensor_simple_queues"
|
||||
DEFAULT_SENSOR_SIMPLE_QUEUES = False
|
||||
CONF_SENSOR_NAT = "sensor_nat"
|
||||
DEFAULT_SENSOR_NAT = False
|
||||
CONF_SENSOR_MANGLE = "sensor_mangle"
|
||||
DEFAULT_SENSOR_MANGLE = False
|
||||
CONF_SENSOR_FILTER = "sensor_filter"
|
||||
DEFAULT_SENSOR_FILTER = False
|
||||
CONF_SENSOR_PPP = "sensor_ppp"
|
||||
DEFAULT_SENSOR_PPP = False
|
||||
CONF_SENSOR_KIDCONTROL = "sensor_kidcontrol"
|
||||
DEFAULT_SENSOR_KIDCONTROL = False
|
||||
CONF_SENSOR_SCRIPTS = "sensor_scripts"
|
||||
DEFAULT_SENSOR_SCRIPTS = False
|
||||
CONF_SENSOR_ENVIRONMENT = "sensor_environment"
|
||||
DEFAULT_SENSOR_ENVIRONMENT = False
|
||||
"""Constants used by the Mikrotik Router component and platforms."""
|
||||
|
||||
from homeassistant.const import Platform
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.SENSOR,
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.SWITCH,
|
||||
Platform.BUTTON,
|
||||
Platform.UPDATE,
|
||||
]
|
||||
|
||||
DOMAIN = "mikrotik_router"
|
||||
DEFAULT_NAME = "Mikrotik Router"
|
||||
ATTRIBUTION = "Data provided by Mikrotik"
|
||||
|
||||
DEFAULT_ENCODING = "ISO-8859-1"
|
||||
DEFAULT_LOGIN_METHOD = "plain"
|
||||
|
||||
DEFAULT_HOST = "10.0.0.1"
|
||||
DEFAULT_USERNAME = "admin"
|
||||
DEFAULT_PORT = 0
|
||||
DEFAULT_DEVICE_NAME = "Mikrotik"
|
||||
DEFAULT_SSL = False
|
||||
DEFAULT_VERIFY_SSL = False
|
||||
|
||||
CONF_SCAN_INTERVAL = "scan_interval"
|
||||
DEFAULT_SCAN_INTERVAL = 30
|
||||
CONF_TRACK_IFACE_CLIENTS = "track_iface_clients"
|
||||
DEFAULT_TRACK_IFACE_CLIENTS = True
|
||||
CONF_TRACK_HOSTS = "track_network_hosts"
|
||||
DEFAULT_TRACK_HOSTS = False
|
||||
CONF_TRACK_HOSTS_TIMEOUT = "track_network_hosts_timeout"
|
||||
DEFAULT_TRACK_HOST_TIMEOUT = 180
|
||||
|
||||
CONF_SENSOR_PORT_TRACKER = "sensor_port_tracker"
|
||||
DEFAULT_SENSOR_PORT_TRACKER = False
|
||||
CONF_SENSOR_PORT_TRAFFIC = "sensor_port_traffic"
|
||||
DEFAULT_SENSOR_PORT_TRAFFIC = False
|
||||
CONF_SENSOR_CLIENT_TRAFFIC = "sensor_client_traffic"
|
||||
DEFAULT_SENSOR_CLIENT_TRAFFIC = False
|
||||
CONF_SENSOR_CLIENT_CAPTIVE = "sensor_client_captive"
|
||||
DEFAULT_SENSOR_CLIENT_CAPTIVE = False
|
||||
CONF_SENSOR_SIMPLE_QUEUES = "sensor_simple_queues"
|
||||
DEFAULT_SENSOR_SIMPLE_QUEUES = False
|
||||
CONF_SENSOR_NAT = "sensor_nat"
|
||||
DEFAULT_SENSOR_NAT = False
|
||||
CONF_SENSOR_MANGLE = "sensor_mangle"
|
||||
DEFAULT_SENSOR_MANGLE = False
|
||||
CONF_SENSOR_FILTER = "sensor_filter"
|
||||
DEFAULT_SENSOR_FILTER = False
|
||||
CONF_SENSOR_PPP = "sensor_ppp"
|
||||
DEFAULT_SENSOR_PPP = False
|
||||
CONF_SENSOR_KIDCONTROL = "sensor_kidcontrol"
|
||||
DEFAULT_SENSOR_KIDCONTROL = False
|
||||
CONF_SENSOR_SCRIPTS = "sensor_scripts"
|
||||
DEFAULT_SENSOR_SCRIPTS = False
|
||||
CONF_SENSOR_ENVIRONMENT = "sensor_environment"
|
||||
DEFAULT_SENSOR_ENVIRONMENT = False
|
||||
CONF_SENSOR_NETWATCH_TRACKER = "sensor_netwatch_tracker"
|
||||
DEFAULT_SENSOR_NETWATCH_TRACKER = False
|
||||
|
||||
TO_REDACT = {
|
||||
"ip-address",
|
||||
"client-ip-address",
|
||||
"address",
|
||||
"active-address",
|
||||
"mac-address",
|
||||
"active-mac-address",
|
||||
"orig-mac-address",
|
||||
"port-mac-address",
|
||||
"client-mac-address",
|
||||
"client-id",
|
||||
"active-client-id",
|
||||
"eeprom",
|
||||
"sfp-vendor-serial",
|
||||
"gateway",
|
||||
"dns-server",
|
||||
"wins-server",
|
||||
"ntp-server",
|
||||
"caps-manager",
|
||||
"serial-number",
|
||||
"source",
|
||||
"from-addresses",
|
||||
"to-addresses",
|
||||
"src-address",
|
||||
"dst-address",
|
||||
"username",
|
||||
"password",
|
||||
"caller-id",
|
||||
"target",
|
||||
"ssid",
|
||||
}
|
||||
|
|
|
@ -1,151 +1,139 @@
|
|||
"""Support for the Mikrotik Router device tracker."""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from collections.abc import Mapping
|
||||
from datetime import timedelta
|
||||
from typing import Any, Callable
|
||||
|
||||
from homeassistant.components.device_tracker.config_entry import ScannerEntity
|
||||
from homeassistant.components.device_tracker.const import SOURCE_TYPE_ROUTER
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_HOST,
|
||||
ATTR_ATTRIBUTION,
|
||||
STATE_NOT_HOME,
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import STATE_NOT_HOME
|
||||
from homeassistant.helpers import (
|
||||
entity_platform as ep,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.util.dt import get_age, utcnow
|
||||
from .helper import format_attribute, format_value
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import slugify
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from homeassistant.components.device_tracker.const import SourceType
|
||||
|
||||
from .device_tracker_types import SENSOR_TYPES, SENSOR_SERVICES
|
||||
from .coordinator import MikrotikCoordinator
|
||||
from .entity import _skip_sensor, MikrotikEntity
|
||||
from .helper import format_attribute
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
ATTRIBUTION,
|
||||
CONF_TRACK_HOSTS,
|
||||
DEFAULT_TRACK_HOSTS,
|
||||
CONF_TRACK_HOSTS_TIMEOUT,
|
||||
DEFAULT_TRACK_HOST_TIMEOUT,
|
||||
)
|
||||
from .device_tracker_types import (
|
||||
MikrotikDeviceTrackerEntityDescription,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
async def async_add_entities(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, dispatcher: dict[str, Callable]
|
||||
):
|
||||
"""Add entities."""
|
||||
platform = ep.async_get_current_platform()
|
||||
services = platform.platform.SENSOR_SERVICES
|
||||
descriptions = platform.platform.SENSOR_TYPES
|
||||
|
||||
for service in services:
|
||||
platform.async_register_entity_service(service[0], service[1], service[2])
|
||||
|
||||
@callback
|
||||
async def async_update_controller(coordinator):
|
||||
"""Update the values of the controller."""
|
||||
if coordinator.data is None:
|
||||
return
|
||||
|
||||
async def async_check_exist(obj, coordinator, uid: None) -> None:
|
||||
"""Check entity exists."""
|
||||
entity_registry = er.async_get(hass)
|
||||
if uid:
|
||||
unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}-{slugify(str(obj._data[obj.entity_description.data_reference]).lower())}"
|
||||
else:
|
||||
unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}"
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
platform.domain, DOMAIN, unique_id
|
||||
)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
if entity is None or (
|
||||
(entity_id not in platform.entities) and (entity.disabled is False)
|
||||
):
|
||||
_LOGGER.debug("Add entity %s", entity_id)
|
||||
await platform.async_add_entities([obj])
|
||||
|
||||
for entity_description in descriptions:
|
||||
data = coordinator.data[entity_description.data_path]
|
||||
if not entity_description.data_reference:
|
||||
if data.get(entity_description.data_attribute) is None:
|
||||
continue
|
||||
obj = dispatcher[entity_description.func](
|
||||
coordinator, entity_description
|
||||
)
|
||||
await async_check_exist(obj, coordinator, None)
|
||||
else:
|
||||
for uid in data:
|
||||
if _skip_sensor(config_entry, entity_description, data, uid):
|
||||
continue
|
||||
obj = dispatcher[entity_description.func](
|
||||
coordinator, entity_description, uid
|
||||
)
|
||||
await async_check_exist(obj, coordinator, uid)
|
||||
|
||||
await async_update_controller(
|
||||
hass.data[DOMAIN][config_entry.entry_id].tracker_coordinator
|
||||
)
|
||||
|
||||
unsub = async_dispatcher_connect(hass, "update_sensors", async_update_controller)
|
||||
config_entry.async_on_unload(unsub)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up device tracker for Mikrotik Router component."""
|
||||
inst = config_entry.data[CONF_NAME]
|
||||
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
trackers = {}
|
||||
|
||||
@callback
|
||||
def update_controller():
|
||||
"""Update the values of the controller."""
|
||||
update_items(
|
||||
inst, config_entry, mikrotik_controller, async_add_entities, trackers
|
||||
)
|
||||
|
||||
mikrotik_controller.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, mikrotik_controller.signal_update, update_controller
|
||||
)
|
||||
)
|
||||
|
||||
update_controller()
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikDeviceTracker": MikrotikDeviceTracker,
|
||||
"MikrotikHostDeviceTracker": MikrotikHostDeviceTracker,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_items
|
||||
# MikrotikDeviceTracker
|
||||
# ---------------------------
|
||||
@callback
|
||||
def update_items(inst, config_entry, mikrotik_controller, async_add_entities, trackers):
|
||||
"""Update trackers device state from the controller."""
|
||||
new_trackers = []
|
||||
|
||||
for sensor, sid_func in zip(
|
||||
# Sensor type name
|
||||
["host"],
|
||||
# Entity function
|
||||
[MikrotikControllerHostDeviceTracker],
|
||||
):
|
||||
uid_sensor = SENSOR_TYPES[sensor]
|
||||
if (
|
||||
# Skip if host tracking is disabled
|
||||
sensor == "host"
|
||||
and not config_entry.options.get(CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS)
|
||||
):
|
||||
continue
|
||||
|
||||
for uid in mikrotik_controller.data[uid_sensor.data_path]:
|
||||
uid_data = mikrotik_controller.data[uid_sensor.data_path]
|
||||
item_id = f"{inst}-{sensor}-{uid_data[uid][uid_sensor.data_reference]}"
|
||||
_LOGGER.debug("Updating device tracker %s", item_id)
|
||||
if item_id in trackers:
|
||||
if trackers[item_id].enabled:
|
||||
trackers[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
trackers[item_id] = sid_func(
|
||||
inst=inst,
|
||||
uid=uid,
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_sensor,
|
||||
config_entry=config_entry,
|
||||
)
|
||||
new_trackers.append(trackers[item_id])
|
||||
|
||||
# Register new entities
|
||||
if new_trackers:
|
||||
async_add_entities(new_trackers)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerDeviceTracker
|
||||
# ---------------------------
|
||||
class MikrotikControllerDeviceTracker(ScannerEntity):
|
||||
class MikrotikDeviceTracker(MikrotikEntity, ScannerEntity):
|
||||
"""Representation of a device tracker."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inst,
|
||||
uid: "",
|
||||
mikrotik_controller,
|
||||
entity_description: MikrotikDeviceTrackerEntityDescription,
|
||||
config_entry,
|
||||
coordinator: MikrotikCoordinator,
|
||||
entity_description,
|
||||
uid: str | None = None,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.entity_description = entity_description
|
||||
self._config_entry = config_entry
|
||||
self._inst = inst
|
||||
self._ctrl = mikrotik_controller
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path][uid]
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if self.entity_description.name:
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
||||
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}-{self._data[self.entity_description.data_reference].lower()}"
|
||||
"""Initialize entity"""
|
||||
super().__init__(coordinator, entity_description, uid)
|
||||
self._attr_name = None
|
||||
|
||||
@property
|
||||
def ip_address(self) -> str:
|
||||
"""Return the primary ip address of the device."""
|
||||
if "address" in self._data:
|
||||
return self._data["address"]
|
||||
|
||||
return None
|
||||
return self._data["address"] if "address" in self._data else None
|
||||
|
||||
@property
|
||||
def mac_address(self) -> str:
|
||||
|
@ -153,7 +141,7 @@ class MikrotikControllerDeviceTracker(ScannerEntity):
|
|||
if self.entity_description.data_reference in self._data:
|
||||
return self._data[self.entity_description.data_reference]
|
||||
|
||||
return None
|
||||
return ""
|
||||
|
||||
@property
|
||||
def hostname(self) -> str:
|
||||
|
@ -161,105 +149,23 @@ class MikrotikControllerDeviceTracker(ScannerEntity):
|
|||
if self.entity_description.data_name in self._data:
|
||||
return self._data[self.entity_description.data_name]
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def device_info(self) -> Dict[str, Any]:
|
||||
"""Return a description for device registry."""
|
||||
info = {
|
||||
"connections": {
|
||||
(CONNECTION_NETWORK_MAC, self._data[self._sid_data["sid_ref"]])
|
||||
},
|
||||
"manufacturer": self._ctrl.data["resource"]["platform"],
|
||||
"model": self._ctrl.data["resource"]["board-name"],
|
||||
"name": self._data[self._sid_data["sid_name"]],
|
||||
}
|
||||
if self._sid_data["sid"] == "interface":
|
||||
info["name"] = f"{self._inst} {self._data[self._sid_data['sid_name']]}"
|
||||
return info
|
||||
return ""
|
||||
|
||||
@property
|
||||
def is_connected(self) -> bool:
|
||||
"""Return true if device is connected."""
|
||||
return self._data[self.entity_description.data_is_on]
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a description for device registry."""
|
||||
dev_connection = DOMAIN
|
||||
dev_connection_value = self.entity_description.data_reference
|
||||
dev_group = self.entity_description.ha_group
|
||||
if self.entity_description.ha_group.startswith("data__"):
|
||||
dev_group = self.entity_description.ha_group[6:]
|
||||
if dev_group in self._data:
|
||||
dev_group = self._data[dev_group]
|
||||
dev_connection_value = dev_group
|
||||
|
||||
if self.entity_description.ha_connection:
|
||||
dev_connection = self.entity_description.ha_connection
|
||||
|
||||
if self.entity_description.ha_connection_value:
|
||||
dev_connection_value = self.entity_description.ha_connection_value
|
||||
if dev_connection_value.startswith("data__"):
|
||||
dev_connection_value = dev_connection_value[6:]
|
||||
dev_connection_value = self._data[dev_connection_value]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
identifiers={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{self._inst} {dev_group}",
|
||||
model=f"{self._ctrl.data['resource']['board-name']}",
|
||||
manufacturer=f"{self._ctrl.data['resource']['platform']}",
|
||||
sw_version=f"{self._ctrl.data['resource']['version']}",
|
||||
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}",
|
||||
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"),
|
||||
)
|
||||
|
||||
if "mac-address" in self.entity_description.data_reference:
|
||||
dev_group = self._data[self.entity_description.data_name]
|
||||
dev_manufacturer = ""
|
||||
if dev_connection_value in self._ctrl.data["host"]:
|
||||
dev_group = self._ctrl.data["host"][dev_connection_value]["host-name"]
|
||||
dev_manufacturer = self._ctrl.data["host"][dev_connection_value][
|
||||
"manufacturer"
|
||||
]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{dev_group}",
|
||||
manufacturer=f"{dev_manufacturer}",
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
f"{self._ctrl.data['routerboard']['serial-number']}",
|
||||
),
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
for variable in self.entity_description.data_attributes_list:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
@property
|
||||
def source_type(self) -> str:
|
||||
"""Return the source type of the port."""
|
||||
return SOURCE_TYPE_ROUTER
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added to hass."""
|
||||
_LOGGER.debug("New device tracker %s (%s)", self._inst, self.unique_id)
|
||||
return SourceType.ROUTER
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerHostDeviceTracker
|
||||
# MikrotikHostDeviceTracker
|
||||
# ---------------------------
|
||||
class MikrotikControllerHostDeviceTracker(MikrotikControllerDeviceTracker):
|
||||
class MikrotikHostDeviceTracker(MikrotikDeviceTracker):
|
||||
"""Representation of a network device."""
|
||||
|
||||
@property
|
||||
|
@ -275,16 +181,6 @@ class MikrotikControllerHostDeviceTracker(MikrotikControllerDeviceTracker):
|
|||
)
|
||||
return timedelta(seconds=track_network_hosts_timeout)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
return f"{self._data[self.entity_description.data_name]}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
return f"{self._data[self.entity_description.data_reference].lower()}"
|
||||
|
||||
@property
|
||||
def is_connected(self) -> bool:
|
||||
"""Return true if the host is connected to the network."""
|
||||
|
@ -292,22 +188,19 @@ class MikrotikControllerHostDeviceTracker(MikrotikControllerDeviceTracker):
|
|||
return False
|
||||
|
||||
if self._data["source"] in ["capsman", "wireless"]:
|
||||
return self._data[self.entity_description.data_is_on]
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
if (
|
||||
return bool(
|
||||
self._data["last-seen"]
|
||||
and (utcnow() - self._data["last-seen"])
|
||||
and utcnow() - self._data["last-seen"]
|
||||
< self.option_track_network_hosts_timeout
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
)
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
if self._data["source"] in ["capsman", "wireless"]:
|
||||
if self._data[self.entity_description.data_is_on]:
|
||||
if self._data[self.entity_description.data_attribute]:
|
||||
return self.entity_description.icon_enabled
|
||||
else:
|
||||
return self.entity_description.icon_disabled
|
||||
|
@ -323,17 +216,16 @@ class MikrotikControllerHostDeviceTracker(MikrotikControllerDeviceTracker):
|
|||
@property
|
||||
def state(self) -> str:
|
||||
"""Return the state of the device."""
|
||||
if self.is_connected:
|
||||
return self._ctrl.option_zone
|
||||
return STATE_NOT_HOME
|
||||
return self.coordinator.option_zone if self.is_connected else STATE_NOT_HOME
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Dict[str, Any]:
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
if self._data["last-seen"]:
|
||||
attributes[format_attribute("last-seen")] = get_age(self._data["last-seen"])
|
||||
else:
|
||||
attributes[format_attribute("last-seen")] = "unknown"
|
||||
if self.is_connected:
|
||||
attributes[format_attribute("last-seen")] = "Now"
|
||||
|
||||
if not attributes[format_attribute("last-seen")]:
|
||||
attributes[format_attribute("last-seen")] = "Unknown"
|
||||
|
||||
return attributes
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
"""Definitions for Mikrotik Router device tracker entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.components.switch import (
|
||||
SwitchEntityDescription,
|
||||
|
@ -9,7 +13,13 @@ from homeassistant.components.switch import (
|
|||
DEVICE_ATTRIBUTES_HOST = [
|
||||
"interface",
|
||||
"source",
|
||||
"authorized",
|
||||
"bypassed",
|
||||
"last-seen",
|
||||
"signal-strength",
|
||||
"tx-ccq",
|
||||
"tx-rate",
|
||||
"rx-rate",
|
||||
]
|
||||
|
||||
|
||||
|
@ -17,24 +27,26 @@ DEVICE_ATTRIBUTES_HOST = [
|
|||
class MikrotikDeviceTrackerEntityDescription(SwitchEntityDescription):
|
||||
"""Class describing mikrotik entities."""
|
||||
|
||||
key: str = ""
|
||||
name: str = ""
|
||||
key: str | None = None
|
||||
name: str | None = None
|
||||
device_class = None
|
||||
icon_enabled: str = ""
|
||||
icon_disabled: str = ""
|
||||
ha_group: str = ""
|
||||
ha_connection: str = ""
|
||||
ha_connection_value: str = ""
|
||||
data_path: str = ""
|
||||
data_is_on: str = "available"
|
||||
data_name: str = ""
|
||||
data_uid: str = ""
|
||||
data_reference: str = ""
|
||||
icon_enabled: str | None = None
|
||||
icon_disabled: str | None = None
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str = "available"
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikDeviceTracker"
|
||||
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"host": MikrotikDeviceTrackerEntityDescription(
|
||||
SENSOR_TYPES: tuple[MikrotikDeviceTrackerEntityDescription, ...] = (
|
||||
MikrotikDeviceTrackerEntityDescription(
|
||||
key="host",
|
||||
name="",
|
||||
icon_enabled="mdi:lan-connect",
|
||||
|
@ -47,5 +59,8 @@ SENSOR_TYPES = {
|
|||
data_uid="mac-address",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_HOST,
|
||||
func="MikrotikHostDeviceTracker",
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
SENSOR_SERVICES = {}
|
||||
|
|
|
@ -1,57 +1,25 @@
|
|||
"""Diagnostics support for Mikrotik Router."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
TO_REDACT = {
|
||||
"ip-address",
|
||||
"client-ip-address",
|
||||
"address",
|
||||
"active-address",
|
||||
"mac-address",
|
||||
"active-mac-address",
|
||||
"orig-mac-address",
|
||||
"port-mac-address",
|
||||
"client-mac-address",
|
||||
"client-id",
|
||||
"active-client-id",
|
||||
"eeprom",
|
||||
"sfp-vendor-serial",
|
||||
"gateway",
|
||||
"dns-server",
|
||||
"wins-server",
|
||||
"ntp-server",
|
||||
"caps-manager",
|
||||
"serial-number",
|
||||
"source",
|
||||
"from-addresses",
|
||||
"to-addresses",
|
||||
"src-address",
|
||||
"dst-address",
|
||||
"username",
|
||||
"password",
|
||||
"caller-id",
|
||||
"target",
|
||||
"ssid",
|
||||
}
|
||||
from .const import DOMAIN, TO_REDACT
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
diag: dict[str, Any] = {}
|
||||
diag["entry"]: dict[str, Any] = {}
|
||||
data_coordinator = hass.data[DOMAIN][config_entry.entry_id].data_coordinator
|
||||
tracker_coordinator = hass.data[DOMAIN][config_entry.entry_id].data_coordinator
|
||||
|
||||
diag["entry"]["data"] = async_redact_data(config_entry.data, TO_REDACT)
|
||||
diag["entry"]["options"] = async_redact_data(config_entry.options, TO_REDACT)
|
||||
diag["data"] = async_redact_data(controller.data, TO_REDACT)
|
||||
|
||||
return diag
|
||||
return {
|
||||
"entry": {
|
||||
"data": async_redact_data(config_entry.data, TO_REDACT),
|
||||
"options": async_redact_data(config_entry.options, TO_REDACT),
|
||||
},
|
||||
"data": async_redact_data(data_coordinator.data, TO_REDACT),
|
||||
"tracker": async_redact_data(tracker_coordinator.data, TO_REDACT),
|
||||
}
|
||||
|
|
323
custom_components/mikrotik_router/entity.py
Normal file
|
@ -0,0 +1,323 @@
|
|||
"""Mikrotik HA shared entity model"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from logging import getLogger
|
||||
from typing import Any, Callable, TypeVar
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME, CONF_HOST
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import (
|
||||
entity_platform as ep,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
ATTRIBUTION,
|
||||
CONF_SENSOR_PORT_TRAFFIC,
|
||||
DEFAULT_SENSOR_PORT_TRAFFIC,
|
||||
CONF_TRACK_HOSTS,
|
||||
DEFAULT_TRACK_HOSTS,
|
||||
CONF_SENSOR_PORT_TRACKER,
|
||||
DEFAULT_SENSOR_PORT_TRACKER,
|
||||
CONF_SENSOR_NETWATCH_TRACKER,
|
||||
DEFAULT_SENSOR_NETWATCH_TRACKER,
|
||||
)
|
||||
from .coordinator import MikrotikCoordinator, MikrotikTrackerCoordinator
|
||||
from .helper import format_attribute
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
def _skip_sensor(config_entry, entity_description, data, uid) -> bool:
|
||||
# Sensors
|
||||
if (
|
||||
entity_description.func == "MikrotikInterfaceTrafficSensor"
|
||||
and not config_entry.options.get(
|
||||
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
|
||||
)
|
||||
):
|
||||
return True
|
||||
|
||||
if (
|
||||
entity_description.func == "MikrotikInterfaceTrafficSensor"
|
||||
and data[uid]["type"] == "bridge"
|
||||
):
|
||||
return True
|
||||
|
||||
if (
|
||||
entity_description.data_path == "client_traffic"
|
||||
and entity_description.data_attribute not in data[uid].keys()
|
||||
):
|
||||
return True
|
||||
|
||||
# Binary sensors
|
||||
if (
|
||||
entity_description.func == "MikrotikPortBinarySensor"
|
||||
and data[uid]["type"] == "wlan"
|
||||
):
|
||||
return True
|
||||
|
||||
if (
|
||||
entity_description.func == "MikrotikPortBinarySensor"
|
||||
and not config_entry.options.get(
|
||||
CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER
|
||||
)
|
||||
):
|
||||
return True
|
||||
|
||||
if entity_description.data_path == "netwatch" and not config_entry.options.get(
|
||||
CONF_SENSOR_NETWATCH_TRACKER, DEFAULT_SENSOR_NETWATCH_TRACKER
|
||||
):
|
||||
return True
|
||||
|
||||
# Device Tracker
|
||||
if (
|
||||
# Skip if host tracking is disabled
|
||||
entity_description.func == "MikrotikHostDeviceTracker"
|
||||
and not config_entry.options.get(CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS)
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_add_entities
|
||||
# ---------------------------
|
||||
async def async_add_entities(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, dispatcher: dict[str, Callable]
|
||||
):
|
||||
"""Add entities."""
|
||||
platform = ep.async_get_current_platform()
|
||||
services = platform.platform.SENSOR_SERVICES
|
||||
descriptions = platform.platform.SENSOR_TYPES
|
||||
|
||||
for service in services:
|
||||
platform.async_register_entity_service(service[0], service[1], service[2])
|
||||
|
||||
@callback
|
||||
async def async_update_controller(coordinator):
|
||||
"""Update the values of the controller."""
|
||||
|
||||
async def async_check_exist(obj, coordinator, uid: None) -> None:
|
||||
"""Check entity exists."""
|
||||
entity_registry = er.async_get(hass)
|
||||
if uid:
|
||||
unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}-{slugify(str(obj._data[obj.entity_description.data_reference]).lower())}"
|
||||
else:
|
||||
unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}"
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
platform.domain, DOMAIN, unique_id
|
||||
)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
if entity is None or (
|
||||
(entity_id not in platform.entities) and (entity.disabled is False)
|
||||
):
|
||||
_LOGGER.debug("Add entity %s", entity_id)
|
||||
await platform.async_add_entities([obj])
|
||||
|
||||
for entity_description in descriptions:
|
||||
data = coordinator.data[entity_description.data_path]
|
||||
if not entity_description.data_reference:
|
||||
if data.get(entity_description.data_attribute) is None:
|
||||
continue
|
||||
obj = dispatcher[entity_description.func](
|
||||
coordinator, entity_description
|
||||
)
|
||||
await async_check_exist(obj, coordinator, None)
|
||||
else:
|
||||
for uid in data:
|
||||
if _skip_sensor(config_entry, entity_description, data, uid):
|
||||
continue
|
||||
obj = dispatcher[entity_description.func](
|
||||
coordinator, entity_description, uid
|
||||
)
|
||||
await async_check_exist(obj, coordinator, uid)
|
||||
|
||||
await async_update_controller(
|
||||
hass.data[DOMAIN][config_entry.entry_id].data_coordinator
|
||||
)
|
||||
|
||||
unsub = async_dispatcher_connect(hass, "update_sensors", async_update_controller)
|
||||
config_entry.async_on_unload(unsub)
|
||||
|
||||
|
||||
_MikrotikCoordinatorT = TypeVar(
|
||||
"_MikrotikCoordinatorT",
|
||||
bound=MikrotikCoordinator | MikrotikTrackerCoordinator,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikEntity
|
||||
# ---------------------------
|
||||
class MikrotikEntity(CoordinatorEntity[_MikrotikCoordinatorT], Entity):
|
||||
"""Define entity"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: MikrotikCoordinator,
|
||||
entity_description,
|
||||
uid: str | None = None,
|
||||
):
|
||||
"""Initialize entity"""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = entity_description
|
||||
self._inst = coordinator.config_entry.data[CONF_NAME]
|
||||
self._config_entry = self.coordinator.config_entry
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._uid = uid
|
||||
self._data = coordinator.data[self.entity_description.data_path]
|
||||
if self._uid:
|
||||
self._data = coordinator.data[self.entity_description.data_path][self._uid]
|
||||
|
||||
self._attr_name = self.custom_name
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
self._data = self.coordinator.data[self.entity_description.data_path]
|
||||
if self._uid:
|
||||
self._data = self.coordinator.data[self.entity_description.data_path][
|
||||
self._uid
|
||||
]
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def custom_name(self) -> str:
|
||||
"""Return the name for this entity"""
|
||||
if not self._uid:
|
||||
if self.entity_description.data_name_comment and self._data["comment"]:
|
||||
return f"{self._data['comment']}"
|
||||
|
||||
return f"{self.entity_description.name}"
|
||||
|
||||
if self.entity_description.data_name_comment and self._data["comment"]:
|
||||
return f"{self._data['comment']}"
|
||||
|
||||
if self.entity_description.name:
|
||||
if (
|
||||
self._data[self.entity_description.data_reference]
|
||||
== self._data[self.entity_description.data_name]
|
||||
):
|
||||
return f"{self.entity_description.name}"
|
||||
|
||||
return f"{self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
||||
|
||||
return f"{self._data[self.entity_description.data_name]}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity"""
|
||||
if self._uid:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}-{slugify(str(self._data[self.entity_description.data_reference]).lower())}"
|
||||
else:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}"
|
||||
|
||||
# @property
|
||||
# def available(self) -> bool:
|
||||
# """Return if controller is available"""
|
||||
# return self.coordinator.connected()
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a description for device registry."""
|
||||
dev_connection = DOMAIN
|
||||
dev_connection_value = self.entity_description.data_reference
|
||||
dev_group = self.entity_description.ha_group
|
||||
if self.entity_description.ha_group == "System":
|
||||
dev_group = self.coordinator.data["resource"]["board-name"]
|
||||
dev_connection_value = self.coordinator.data["routerboard"]["serial-number"]
|
||||
|
||||
if self.entity_description.ha_group.startswith("data__"):
|
||||
dev_group = self.entity_description.ha_group[6:]
|
||||
if dev_group in self._data:
|
||||
dev_group = self._data[dev_group]
|
||||
dev_connection_value = dev_group
|
||||
|
||||
if self.entity_description.ha_connection:
|
||||
dev_connection = self.entity_description.ha_connection
|
||||
|
||||
if self.entity_description.ha_connection_value:
|
||||
dev_connection_value = self.entity_description.ha_connection_value
|
||||
if dev_connection_value.startswith("data__"):
|
||||
dev_connection_value = dev_connection_value[6:]
|
||||
dev_connection_value = self._data[dev_connection_value]
|
||||
|
||||
if self.entity_description.ha_group == "System":
|
||||
return DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
identifiers={(dev_connection, f"{dev_connection_value}")},
|
||||
name=f"{self._inst} {dev_group}",
|
||||
model=f"{self.coordinator.data['resource']['board-name']}",
|
||||
manufacturer=f"{self.coordinator.data['resource']['platform']}",
|
||||
sw_version=f"{self.coordinator.data['resource']['version']}",
|
||||
configuration_url=f"http://{self.coordinator.config_entry.data[CONF_HOST]}",
|
||||
)
|
||||
elif "mac-address" in self.entity_description.data_reference:
|
||||
dev_group = self._data[self.entity_description.data_name]
|
||||
dev_manufacturer = ""
|
||||
if dev_connection_value in self.coordinator.data["host"]:
|
||||
dev_group = self.coordinator.data["host"][dev_connection_value][
|
||||
"host-name"
|
||||
]
|
||||
dev_manufacturer = self.coordinator.data["host"][dev_connection_value][
|
||||
"manufacturer"
|
||||
]
|
||||
|
||||
return DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{dev_group}",
|
||||
default_manufacturer=f"{dev_manufacturer}",
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
f"{self.coordinator.data['routerboard']['serial-number']}",
|
||||
),
|
||||
)
|
||||
else:
|
||||
return DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{self._inst} {dev_group}",
|
||||
default_model=f"{self.coordinator.data['resource']['board-name']}",
|
||||
default_manufacturer=f"{self.coordinator.data['resource']['platform']}",
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
f"{self.coordinator.data['routerboard']['serial-number']}",
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
for variable in self.entity_description.data_attributes_list:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
async def start(self):
|
||||
"""Dummy run function"""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def stop(self):
|
||||
"""Dummy stop function"""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def restart(self):
|
||||
"""Dummy restart function"""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def reload(self):
|
||||
"""Dummy reload function"""
|
||||
raise NotImplementedError()
|
|
@ -5,15 +5,9 @@
|
|||
# format_attribute
|
||||
# ---------------------------
|
||||
def format_attribute(attr):
|
||||
res = attr.replace("-", " ")
|
||||
res = res.capitalize()
|
||||
res = res.replace(" ip ", " IP ")
|
||||
res = res.replace(" mac ", " MAC ")
|
||||
res = res.replace(" mtu", " MTU")
|
||||
res = res.replace("Sfp", "SFP")
|
||||
res = res.replace("Poe", "POE")
|
||||
res = res.replace(" tx", " TX")
|
||||
res = res.replace(" rx", " RX")
|
||||
res = attr.replace("-", "_")
|
||||
res = res.replace(" ", "_")
|
||||
res = res.lower()
|
||||
return res
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"issue_tracker": "https://github.com/tomaae/homeassistant-mikrotik_router/issues",
|
||||
"dependencies": [],
|
||||
"requirements": [
|
||||
"librouteros>=3.2.0",
|
||||
"librouteros>=3.4.1",
|
||||
"mac-vendor-lookup>=0.1.12"
|
||||
],
|
||||
"codeowners": [
|
||||
|
|
|
@ -4,16 +4,13 @@ import logging
|
|||
import ssl
|
||||
from time import time
|
||||
from threading import Lock
|
||||
|
||||
from voluptuous import Optional
|
||||
|
||||
from .const import (
|
||||
DEFAULT_LOGIN_METHOD,
|
||||
DEFAULT_ENCODING,
|
||||
)
|
||||
|
||||
import librouteros
|
||||
from socket import error as socket_error, timeout as socket_timeout
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -31,12 +28,14 @@ class MikrotikAPI:
|
|||
password,
|
||||
port=0,
|
||||
use_ssl=True,
|
||||
ssl_verify=True,
|
||||
login_method=DEFAULT_LOGIN_METHOD,
|
||||
encoding=DEFAULT_ENCODING,
|
||||
):
|
||||
"""Initialize the Mikrotik Client."""
|
||||
self._host = host
|
||||
self._use_ssl = use_ssl
|
||||
self._ssl_verify = ssl_verify
|
||||
self._port = port
|
||||
self._username = username
|
||||
self._password = password
|
||||
|
@ -47,12 +46,13 @@ class MikrotikAPI:
|
|||
|
||||
self._connection = None
|
||||
self._connected = False
|
||||
self._reconnected = False
|
||||
self._reconnected = True
|
||||
self._connection_epoch = 0
|
||||
self._connection_retry_sec = 58
|
||||
self.error = None
|
||||
self.connection_error_reported = False
|
||||
self.client_traffic_last_run = None
|
||||
self.disable_health = False
|
||||
|
||||
# Default ports
|
||||
if not self._port:
|
||||
|
@ -121,47 +121,28 @@ class MikrotikAPI:
|
|||
"port": self._port,
|
||||
}
|
||||
|
||||
if self._use_ssl:
|
||||
if self._ssl_wrapper is None:
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.check_hostname = False
|
||||
ssl_context.verify_mode = ssl.CERT_NONE
|
||||
self._ssl_wrapper = ssl_context.wrap_socket
|
||||
kwargs["ssl_wrapper"] = self._ssl_wrapper
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if self._use_ssl:
|
||||
if self._ssl_wrapper is None:
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.check_hostname = False
|
||||
if self._ssl_verify:
|
||||
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
||||
ssl_context.verify_flags &= ~ssl.VERIFY_X509_STRICT
|
||||
else:
|
||||
ssl_context.verify_mode = ssl.CERT_NONE
|
||||
self._ssl_wrapper = ssl_context.wrap_socket
|
||||
kwargs["ssl_wrapper"] = self._ssl_wrapper
|
||||
self._connection = librouteros.connect(
|
||||
self._host, self._username, self._password, **kwargs
|
||||
)
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ConnectionClosed,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
) as api_error:
|
||||
except Exception as e:
|
||||
if not self.connection_error_reported:
|
||||
_LOGGER.error(
|
||||
"Mikrotik %s error while connecting: %s", self._host, api_error
|
||||
)
|
||||
self.connection_error_reported = True
|
||||
|
||||
self.error_to_strings("%s" % api_error)
|
||||
self._connection = None
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
if not self.connection_error_reported:
|
||||
_LOGGER.error(
|
||||
"Mikrotik %s error while connecting: %s", self._host, "Unknown"
|
||||
)
|
||||
_LOGGER.error("Mikrotik %s error while connecting: %s", self._host, e)
|
||||
self.connection_error_reported = True
|
||||
|
||||
self.error_to_strings(f"{e}")
|
||||
self._connection = None
|
||||
self.lock.release()
|
||||
return False
|
||||
|
@ -190,6 +171,9 @@ class MikrotikAPI:
|
|||
if "ALERT_HANDSHAKE_FAILURE" in error:
|
||||
self.error = "ssl_handshake_failure"
|
||||
|
||||
if "CERTIFICATE_VERIFY_FAILED" in error:
|
||||
self.error = "ssl_verify_failure"
|
||||
|
||||
# ---------------------------
|
||||
# connected
|
||||
# ---------------------------
|
||||
|
@ -198,11 +182,16 @@ class MikrotikAPI:
|
|||
return self._connected
|
||||
|
||||
# ---------------------------
|
||||
# path
|
||||
# query
|
||||
# ---------------------------
|
||||
def path(self, path, return_list=True) -> Optional(list):
|
||||
def query(self, path, command=None, args=None, return_list=True) -> Optional(list):
|
||||
"""Retrieve data from Mikrotik API."""
|
||||
"""Returns generator object, unless return_list passed as True"""
|
||||
if path == "/system/health" and self.disable_health:
|
||||
return None
|
||||
|
||||
if args is None:
|
||||
args = {}
|
||||
|
||||
if not self.connection_check():
|
||||
return None
|
||||
|
@ -211,56 +200,47 @@ class MikrotikAPI:
|
|||
try:
|
||||
_LOGGER.debug("API query: %s", path)
|
||||
response = self._connection.path(path)
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return None
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("path", api_error)
|
||||
self.lock.release()
|
||||
return None
|
||||
except:
|
||||
self.disconnect("path")
|
||||
except Exception as e:
|
||||
self.disconnect("path", e)
|
||||
self.lock.release()
|
||||
return None
|
||||
|
||||
if return_list:
|
||||
if response and return_list and not command:
|
||||
try:
|
||||
response = list(response)
|
||||
except librouteros.exceptions.ConnectionClosed as api_error:
|
||||
self.disconnect("building list for path", api_error)
|
||||
except Exception as e:
|
||||
if path == "/system/health" and "no such command prefix" in str(e):
|
||||
self.disable_health = True
|
||||
self.lock.release()
|
||||
return None
|
||||
|
||||
self.disconnect(f"building list for path {path}", e)
|
||||
self.lock.release()
|
||||
return None
|
||||
except:
|
||||
self.disconnect("building list for path")
|
||||
|
||||
elif response and command:
|
||||
_LOGGER.debug("API query: %s, %s, %s", path, command, args)
|
||||
try:
|
||||
response = list(response(command, **args))
|
||||
except Exception as e:
|
||||
self.disconnect("path", e)
|
||||
self.lock.release()
|
||||
return None
|
||||
|
||||
self.lock.release()
|
||||
return response if response else None
|
||||
return response or None
|
||||
|
||||
# ---------------------------
|
||||
# update
|
||||
# set_value
|
||||
# ---------------------------
|
||||
def update(self, path, param, value, mod_param, mod_value) -> bool:
|
||||
def set_value(self, path, param, value, mod_param, mod_value) -> bool:
|
||||
"""Modify a parameter"""
|
||||
entry_found = None
|
||||
|
||||
if not self.connection_check():
|
||||
return False
|
||||
|
||||
response = self.path(path, return_list=False)
|
||||
response = self.query(path, return_list=False)
|
||||
if response is None:
|
||||
return False
|
||||
|
||||
|
@ -275,7 +255,7 @@ class MikrotikAPI:
|
|||
|
||||
if not entry_found:
|
||||
_LOGGER.error(
|
||||
"Mikrotik %s Update parameter %s with value %s not found",
|
||||
"Mikrotik %s set_value parameter %s with value %s not found",
|
||||
self._host,
|
||||
param,
|
||||
value,
|
||||
|
@ -286,27 +266,8 @@ class MikrotikAPI:
|
|||
self.lock.acquire()
|
||||
try:
|
||||
response.update(**params)
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return False
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("update", api_error)
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
self.disconnect("update")
|
||||
except Exception as e:
|
||||
self.disconnect("set_value", e)
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
|
@ -316,61 +277,48 @@ class MikrotikAPI:
|
|||
# ---------------------------
|
||||
# execute
|
||||
# ---------------------------
|
||||
def execute(self, path, command, param, value) -> bool:
|
||||
def execute(self, path, command, param, value, attributes=None) -> bool:
|
||||
"""Execute a command"""
|
||||
entry_found = None
|
||||
params = {}
|
||||
|
||||
if not self.connection_check():
|
||||
return False
|
||||
|
||||
response = self.path(path, return_list=False)
|
||||
response = self.query(path, return_list=False)
|
||||
if response is None:
|
||||
return False
|
||||
|
||||
for tmp in response:
|
||||
if param not in tmp:
|
||||
continue
|
||||
if param:
|
||||
for tmp in response:
|
||||
if param not in tmp:
|
||||
continue
|
||||
|
||||
if tmp[param] != value:
|
||||
continue
|
||||
if tmp[param] != value:
|
||||
continue
|
||||
|
||||
entry_found = tmp[".id"]
|
||||
entry_found = tmp[".id"]
|
||||
|
||||
if not entry_found:
|
||||
_LOGGER.error(
|
||||
"Mikrotik %s Execute %s parameter %s with value %s not found",
|
||||
self._host,
|
||||
command,
|
||||
param,
|
||||
value,
|
||||
)
|
||||
return True
|
||||
if not entry_found:
|
||||
_LOGGER.error(
|
||||
"Mikrotik %s Execute %s parameter %s with value %s not found",
|
||||
self._host,
|
||||
command,
|
||||
param,
|
||||
value,
|
||||
)
|
||||
return True
|
||||
|
||||
params = {".id": entry_found}
|
||||
|
||||
if attributes:
|
||||
params.update(attributes)
|
||||
|
||||
params = {".id": entry_found}
|
||||
self.lock.acquire()
|
||||
try:
|
||||
tuple(response(command, **params))
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return False
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("execute", api_error)
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
self.disconnect("execute")
|
||||
except Exception as e:
|
||||
self.disconnect("execute", e)
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
|
@ -386,7 +334,7 @@ class MikrotikAPI:
|
|||
if not self.connection_check():
|
||||
return False
|
||||
|
||||
response = self.path("/system/script", return_list=False)
|
||||
response = self.query("/system/script", return_list=False)
|
||||
if response is None:
|
||||
return False
|
||||
|
||||
|
@ -407,88 +355,14 @@ class MikrotikAPI:
|
|||
try:
|
||||
run = response("run", **{".id": entry_found})
|
||||
tuple(run)
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return False
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("run_script", api_error)
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
self.disconnect("run_script")
|
||||
except Exception as e:
|
||||
self.disconnect("run_script", e)
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
self.lock.release()
|
||||
return True
|
||||
|
||||
# ---------------------------
|
||||
# get_sfp
|
||||
# ---------------------------
|
||||
def get_sfp(self, interfaces) -> Optional(list):
|
||||
"""Get sfp info"""
|
||||
if not self.connection_check():
|
||||
return None
|
||||
|
||||
response = self.path("/interface/ethernet", return_list=False)
|
||||
if response is None:
|
||||
return None
|
||||
|
||||
args = {".id": interfaces, "once": True}
|
||||
self.lock.acquire()
|
||||
try:
|
||||
_LOGGER.debug("API query: %s %s", "/interface/ethernet/monitor", interfaces)
|
||||
sfpinfo = response("monitor", **args)
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return None
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
ssl.SSLError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("get_sfp", api_error)
|
||||
self.lock.release()
|
||||
return None
|
||||
except:
|
||||
self.disconnect("get_sfp")
|
||||
self.lock.release()
|
||||
return None
|
||||
|
||||
try:
|
||||
sfpinfo = list(sfpinfo)
|
||||
except librouteros.exceptions.ConnectionClosed as api_error:
|
||||
self.disconnect("get_sfp", api_error)
|
||||
self.lock.release()
|
||||
return None
|
||||
except:
|
||||
self.disconnect("get_sfp")
|
||||
self.lock.release()
|
||||
return None
|
||||
|
||||
self.lock.release()
|
||||
return sfpinfo if sfpinfo else None
|
||||
|
||||
# ---------------------------
|
||||
# arp_ping
|
||||
# ---------------------------
|
||||
|
@ -497,7 +371,7 @@ class MikrotikAPI:
|
|||
if not self.connection_check():
|
||||
return False
|
||||
|
||||
response = self.path("/ping", return_list=False)
|
||||
response = self.query("/ping", return_list=False)
|
||||
if response is None:
|
||||
return False
|
||||
|
||||
|
@ -512,39 +386,15 @@ class MikrotikAPI:
|
|||
try:
|
||||
# _LOGGER.debug("Ping host query: %s", args["address"])
|
||||
ping = response("/ping", **args)
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("arp_ping", api_error)
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
self.disconnect("arp_ping")
|
||||
except Exception as e:
|
||||
self.disconnect("arp_ping", e)
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
try:
|
||||
ping = list(ping)
|
||||
except librouteros.exceptions.ConnectionClosed as api_error:
|
||||
self.disconnect("arp_ping", api_error)
|
||||
self.lock.release()
|
||||
return False
|
||||
except:
|
||||
self.disconnect("arp_ping")
|
||||
except Exception as e:
|
||||
self.disconnect("arp_ping", e)
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
|
@ -570,7 +420,7 @@ class MikrotikAPI:
|
|||
if not self.connection_check():
|
||||
return False, False
|
||||
|
||||
response = self.path("/ip/accounting")
|
||||
response = self.query("/ip/accounting")
|
||||
if response is None:
|
||||
return False, False
|
||||
|
||||
|
@ -598,44 +448,21 @@ class MikrotikAPI:
|
|||
return 0
|
||||
|
||||
if use_accounting:
|
||||
accounting = self.path("/ip/accounting", return_list=False)
|
||||
accounting = self.query("/ip/accounting", return_list=False)
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
# Prepare command
|
||||
take = accounting("snapshot/take")
|
||||
except librouteros.exceptions.ConnectionClosed:
|
||||
self.disconnect()
|
||||
self.lock.release()
|
||||
return 0
|
||||
except (
|
||||
librouteros.exceptions.TrapError,
|
||||
librouteros.exceptions.MultiTrapError,
|
||||
librouteros.exceptions.ProtocolError,
|
||||
librouteros.exceptions.FatalError,
|
||||
socket_timeout,
|
||||
socket_error,
|
||||
ssl.SSLError,
|
||||
BrokenPipeError,
|
||||
OSError,
|
||||
ValueError,
|
||||
) as api_error:
|
||||
self.disconnect("accounting_snapshot", api_error)
|
||||
self.lock.release()
|
||||
return 0
|
||||
except:
|
||||
self.disconnect("accounting_snapshot")
|
||||
except Exception as e:
|
||||
self.disconnect("accounting_snapshot", e)
|
||||
self.lock.release()
|
||||
return 0
|
||||
|
||||
try:
|
||||
list(take)
|
||||
except librouteros.exceptions.ConnectionClosed as api_error:
|
||||
self.disconnect("accounting_snapshot", api_error)
|
||||
self.lock.release()
|
||||
return 0
|
||||
except:
|
||||
self.disconnect("accounting_snapshot")
|
||||
except Exception as e:
|
||||
self.disconnect("accounting_snapshot", e)
|
||||
self.lock.release()
|
||||
return 0
|
||||
|
||||
|
|
|
@ -1,307 +1,90 @@
|
|||
"""Implementation of Mikrotik Router sensor entities."""
|
||||
"""Mikrotik sensor platform."""
|
||||
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from collections.abc import Mapping
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_HOST,
|
||||
ATTR_ATTRIBUTION,
|
||||
)
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .coordinator import MikrotikCoordinator
|
||||
from .entity import MikrotikEntity, async_add_entities
|
||||
from .helper import format_attribute
|
||||
from .const import (
|
||||
CONF_SENSOR_PORT_TRAFFIC,
|
||||
DEFAULT_SENSOR_PORT_TRAFFIC,
|
||||
DOMAIN,
|
||||
ATTRIBUTION,
|
||||
)
|
||||
from .sensor_types import (
|
||||
MikrotikSensorEntityDescription,
|
||||
SENSOR_TYPES,
|
||||
SENSOR_SERVICES,
|
||||
DEVICE_ATTRIBUTES_IFACE_ETHER,
|
||||
DEVICE_ATTRIBUTES_IFACE_SFP,
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up device tracker for Mikrotik Router component."""
|
||||
inst = config_entry.data[CONF_NAME]
|
||||
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
sensors = {}
|
||||
|
||||
@callback
|
||||
def update_controller():
|
||||
"""Update the values of the controller."""
|
||||
update_items(
|
||||
inst, config_entry, mikrotik_controller, async_add_entities, sensors
|
||||
)
|
||||
|
||||
mikrotik_controller.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, mikrotik_controller.signal_update, update_controller
|
||||
)
|
||||
)
|
||||
|
||||
update_controller()
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikSensor": MikrotikSensor,
|
||||
"MikrotikInterfaceTrafficSensor": MikrotikInterfaceTrafficSensor,
|
||||
"MikrotikClientTrafficSensor": MikrotikClientTrafficSensor,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_items
|
||||
# MikrotikSensor
|
||||
# ---------------------------
|
||||
@callback
|
||||
def update_items(inst, config_entry, mikrotik_controller, async_add_entities, sensors):
|
||||
"""Update sensor state from the controller."""
|
||||
new_sensors = []
|
||||
|
||||
for sensor, sid_func in zip(
|
||||
# Sensor type name
|
||||
[
|
||||
"environment",
|
||||
"traffic_rx",
|
||||
"traffic_tx",
|
||||
"client_traffic_rx",
|
||||
"client_traffic_tx",
|
||||
"client_traffic_lan_rx",
|
||||
"client_traffic_lan_tx",
|
||||
"client_traffic_wan_rx",
|
||||
"client_traffic_wan_tx",
|
||||
],
|
||||
# Entity function
|
||||
[
|
||||
MikrotikControllerSensor,
|
||||
MikrotikInterfaceTrafficSensor,
|
||||
MikrotikInterfaceTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
MikrotikClientTrafficSensor,
|
||||
],
|
||||
):
|
||||
if sensor.startswith("traffic_") and not config_entry.options.get(
|
||||
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
|
||||
):
|
||||
continue
|
||||
|
||||
uid_sensor = SENSOR_TYPES[sensor]
|
||||
for uid in mikrotik_controller.data[uid_sensor.data_path]:
|
||||
uid_data = mikrotik_controller.data[uid_sensor.data_path]
|
||||
if (
|
||||
uid_sensor.data_path == "interface"
|
||||
and uid_data[uid]["type"] == "bridge"
|
||||
):
|
||||
continue
|
||||
|
||||
if (
|
||||
uid_sensor.data_path == "client_traffic"
|
||||
and uid_sensor.data_attribute not in uid_data[uid].keys()
|
||||
):
|
||||
continue
|
||||
|
||||
item_id = f"{inst}-{sensor}-{uid_data[uid][uid_sensor.data_reference]}"
|
||||
_LOGGER.debug("Updating sensor %s", item_id)
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
sensors[item_id] = sid_func(
|
||||
inst=inst,
|
||||
uid=uid,
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_sensor,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
for sensor in SENSOR_TYPES:
|
||||
if sensor.startswith("system_"):
|
||||
uid_sensor = SENSOR_TYPES[sensor]
|
||||
if (
|
||||
uid_sensor.data_attribute
|
||||
not in mikrotik_controller.data[uid_sensor.data_path]
|
||||
or mikrotik_controller.data[uid_sensor.data_path][
|
||||
uid_sensor.data_attribute
|
||||
]
|
||||
== "unknown"
|
||||
):
|
||||
continue
|
||||
item_id = f"{inst}-{sensor}"
|
||||
_LOGGER.debug("Updating sensor %s", item_id)
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
sensors[item_id] = MikrotikControllerSensor(
|
||||
inst=inst,
|
||||
uid="",
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_sensor,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
if new_sensors:
|
||||
async_add_entities(new_sensors, True)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerSensor
|
||||
# ---------------------------
|
||||
class MikrotikControllerSensor(SensorEntity):
|
||||
"""Define an Mikrotik Controller sensor."""
|
||||
class MikrotikSensor(MikrotikEntity, SensorEntity):
|
||||
"""Define an Mikrotik sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inst,
|
||||
uid: "",
|
||||
mikrotik_controller,
|
||||
entity_description: MikrotikSensorEntityDescription,
|
||||
coordinator: MikrotikCoordinator,
|
||||
entity_description,
|
||||
uid: str | None = None,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.entity_description = entity_description
|
||||
self._inst = inst
|
||||
self._ctrl = mikrotik_controller
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._uid = uid
|
||||
if self._uid:
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path][
|
||||
self._uid
|
||||
]
|
||||
else:
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path]
|
||||
super().__init__(coordinator, entity_description, uid)
|
||||
self._attr_suggested_unit_of_measurement = (
|
||||
self.entity_description.suggested_unit_of_measurement
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if self._uid:
|
||||
if self.entity_description.name:
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
||||
|
||||
return f"{self._inst} {self._data[self.entity_description.data_name]}"
|
||||
else:
|
||||
return f"{self._inst} {self.entity_description.name}"
|
||||
def native_value(self) -> StateType | date | datetime | Decimal:
|
||||
"""Return the value reported by the sensor."""
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
if self._uid:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}-{self._data[self.entity_description.data_reference].lower()}"
|
||||
else:
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def state(self) -> Optional[str]:
|
||||
"""Return the state."""
|
||||
if self.entity_description.data_attribute:
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
else:
|
||||
return "unknown"
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self):
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit the value is expressed in."""
|
||||
if self.entity_description.native_unit_of_measurement:
|
||||
if self.entity_description.native_unit_of_measurement.startswith("data__"):
|
||||
uom = self.entity_description.native_unit_of_measurement[6:]
|
||||
if uom in self._data:
|
||||
uom = self._data[uom]
|
||||
return uom
|
||||
return self._data[uom]
|
||||
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
return self._ctrl.connected()
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a description for device registry."""
|
||||
dev_connection = DOMAIN
|
||||
dev_connection_value = self.entity_description.data_reference
|
||||
dev_group = self.entity_description.ha_group
|
||||
if self.entity_description.ha_group == "System":
|
||||
dev_group = self._ctrl.data["resource"]["board-name"]
|
||||
dev_connection_value = self._ctrl.data["routerboard"]["serial-number"]
|
||||
|
||||
if self.entity_description.ha_group.startswith("data__"):
|
||||
dev_group = self.entity_description.ha_group[6:]
|
||||
if dev_group in self._data:
|
||||
dev_group = self._data[dev_group]
|
||||
dev_connection_value = dev_group
|
||||
|
||||
if self.entity_description.ha_connection:
|
||||
dev_connection = self.entity_description.ha_connection
|
||||
|
||||
if self.entity_description.ha_connection_value:
|
||||
dev_connection_value = self.entity_description.ha_connection_value
|
||||
if dev_connection_value.startswith("data__"):
|
||||
dev_connection_value = dev_connection_value[6:]
|
||||
dev_connection_value = self._data[dev_connection_value]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
identifiers={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{self._inst} {dev_group}",
|
||||
model=f"{self._ctrl.data['resource']['board-name']}",
|
||||
manufacturer=f"{self._ctrl.data['resource']['platform']}",
|
||||
sw_version=f"{self._ctrl.data['resource']['version']}",
|
||||
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}",
|
||||
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"),
|
||||
)
|
||||
|
||||
if "mac-address" in self.entity_description.data_reference:
|
||||
dev_group = self._data[self.entity_description.data_name]
|
||||
dev_manufacturer = ""
|
||||
if dev_connection_value in self._ctrl.data["host"]:
|
||||
dev_group = self._ctrl.data["host"][dev_connection_value]["host-name"]
|
||||
dev_manufacturer = self._ctrl.data["host"][dev_connection_value][
|
||||
"manufacturer"
|
||||
]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{dev_group}",
|
||||
manufacturer=f"{dev_manufacturer}",
|
||||
via_device=(
|
||||
DOMAIN,
|
||||
f"{self._ctrl.data['routerboard']['serial-number']}",
|
||||
),
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
for variable in self.entity_description.data_attributes_list:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added to hass."""
|
||||
_LOGGER.debug("New sensor %s (%s)", self._inst, self.unique_id)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikInterfaceTrafficSensor
|
||||
# ---------------------------
|
||||
class MikrotikInterfaceTrafficSensor(MikrotikControllerSensor):
|
||||
class MikrotikInterfaceTrafficSensor(MikrotikSensor):
|
||||
"""Define an Mikrotik MikrotikInterfaceTrafficSensor sensor."""
|
||||
|
||||
@property
|
||||
|
@ -319,30 +102,35 @@ class MikrotikInterfaceTrafficSensor(MikrotikControllerSensor):
|
|||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
elif self._data["type"] == "wlan":
|
||||
for variable in DEVICE_ATTRIBUTES_IFACE_WIRELESS:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikClientTrafficSensor
|
||||
# ---------------------------
|
||||
class MikrotikClientTrafficSensor(MikrotikControllerSensor):
|
||||
class MikrotikClientTrafficSensor(MikrotikSensor):
|
||||
"""Define an Mikrotik MikrotikClientTrafficSensor sensor."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
return f"{self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
||||
def custom_name(self) -> str:
|
||||
"""Return the name for this entity"""
|
||||
return f"{self.entity_description.name}"
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller and accounting feature in Mikrotik is available.
|
||||
Additional check for lan-tx/rx sensors
|
||||
"""
|
||||
if self.entity_description.data_attribute in ["lan-tx", "lan-rx"]:
|
||||
return (
|
||||
self._ctrl.connected()
|
||||
and self._data["available"]
|
||||
and self._data["local_accounting"]
|
||||
)
|
||||
else:
|
||||
return self._ctrl.connected() and self._data["available"]
|
||||
# @property
|
||||
# def available(self) -> bool:
|
||||
# """Return if controller and accounting feature in Mikrotik is available.
|
||||
# Additional check for lan-tx/rx sensors
|
||||
# """
|
||||
# if self.entity_description.data_attribute in ["lan-tx", "lan-rx"]:
|
||||
# return (
|
||||
# self.coordinator.connected()
|
||||
# and self._data["available"]
|
||||
# and self._data["local_accounting"]
|
||||
# )
|
||||
# else:
|
||||
# return self.coordinator.connected() and self._data["available"]
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
"""Definitions for Mikrotik Router sensor entities."""
|
||||
"""Definitions for sensor entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.components.sensor import (
|
||||
|
@ -9,11 +13,16 @@ from homeassistant.components.sensor import (
|
|||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
TEMP_CELSIUS,
|
||||
ELECTRIC_POTENTIAL_VOLT,
|
||||
POWER_WATT,
|
||||
PERCENTAGE,
|
||||
REVOLUTIONS_PER_MINUTE,
|
||||
UnitOfTemperature,
|
||||
UnitOfDataRate,
|
||||
UnitOfInformation,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfElectricCurrent,
|
||||
UnitOfPower,
|
||||
)
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
DEVICE_ATTRIBUTES_IFACE = [
|
||||
|
@ -63,33 +72,79 @@ DEVICE_ATTRIBUTES_IFACE_SFP = [
|
|||
"eeprom-checksum",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_CLIENT_TRAFFIC = ["address", "mac-address", "host-name"]
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS = [
|
||||
"ssid",
|
||||
"mode",
|
||||
"radio-name",
|
||||
"interface-type",
|
||||
"country",
|
||||
"installation",
|
||||
"antenna-gain",
|
||||
"frequency",
|
||||
"band",
|
||||
"channel-width",
|
||||
"secondary-frequency",
|
||||
"wireless-protocol",
|
||||
"rate-set",
|
||||
"distance",
|
||||
"tx-power-mode",
|
||||
"vlan-id",
|
||||
"wds-mode",
|
||||
"wds-default-bridge",
|
||||
"bridge-mode",
|
||||
"hide-ssid",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_CLIENT_TRAFFIC = [
|
||||
"address",
|
||||
"mac-address",
|
||||
"host-name",
|
||||
"authorized",
|
||||
"bypassed",
|
||||
]
|
||||
DEVICE_ATTRIBUTES_GPS = [
|
||||
"valid",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"altitude",
|
||||
"speed",
|
||||
"destination-bearing",
|
||||
"true-bearing",
|
||||
"magnetic-bearing",
|
||||
"satellites",
|
||||
"fix-quality",
|
||||
"horizontal-dilution",
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MikrotikSensorEntityDescription(SensorEntityDescription):
|
||||
"""Class describing mikrotik entities."""
|
||||
|
||||
ha_group: str = ""
|
||||
ha_connection: str = ""
|
||||
ha_connection_value: str = ""
|
||||
data_path: str = ""
|
||||
data_attribute: str = ""
|
||||
data_name: str = ""
|
||||
data_uid: str = ""
|
||||
data_reference: str = ""
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str | None = None
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikSensor"
|
||||
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"system_temperature": MikrotikSensorEntityDescription(
|
||||
SENSOR_TYPES: tuple[MikrotikSensorEntityDescription, ...] = (
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_temperature",
|
||||
name="Temperature",
|
||||
icon="mdi:thermometer",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="temperature",
|
||||
|
@ -97,11 +152,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_voltage": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_voltage",
|
||||
name="Voltage",
|
||||
icon="mdi:lightning-bolt",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
|
@ -112,14 +169,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_cpu-temperature": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_cpu-temperature",
|
||||
name="CPU temperature",
|
||||
icon="mdi:thermometer",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="cpu-temperature",
|
||||
|
@ -127,14 +186,33 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_board-temperature1": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_switch-temperature",
|
||||
name="Switch temperature",
|
||||
icon="mdi:thermometer",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="switch-temperature",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_board-temperature1",
|
||||
name="Board temperature",
|
||||
icon="mdi:thermometer",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="board-temperature1",
|
||||
|
@ -142,14 +220,33 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_power-consumption": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_phy-temperature",
|
||||
name="PHY temperature",
|
||||
icon="mdi:thermometer",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="phy-temperature",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_power-consumption",
|
||||
name="Power consumption",
|
||||
icon="mdi:transmission-tower",
|
||||
native_unit_of_measurement=POWER_WATT,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="power-consumption",
|
||||
|
@ -157,14 +254,31 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_fan1-speed": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_poe_out_consumption",
|
||||
name="PoE out power consumption",
|
||||
icon="mdi:flash",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="poe-out-consumption",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_fan1-speed",
|
||||
name="Fan1 speed",
|
||||
icon="mdi:fan",
|
||||
native_unit_of_measurement="RPM",
|
||||
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
entity_category=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="fan1-speed",
|
||||
|
@ -172,14 +286,14 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_fan2-speed": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_fan2-speed",
|
||||
name="Fan2 speed",
|
||||
icon="mdi:fan",
|
||||
native_unit_of_measurement="RPM",
|
||||
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
entity_category=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="fan2-speed",
|
||||
|
@ -187,7 +301,122 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_uptime": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_fan3-speed",
|
||||
name="Fan3 speed",
|
||||
icon="mdi:fan",
|
||||
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
|
||||
device_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="fan3-speed",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_fan4-speed",
|
||||
name="Fan4 speed",
|
||||
icon="mdi:fan",
|
||||
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
|
||||
device_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="fan4-speed",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_poe_out_consumption",
|
||||
name="PoE out power consumption",
|
||||
icon="mdi:transmission-tower",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_unit_of_measurement=UnitOfPower.WATT,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="poe-out-consumption",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_psu1_current",
|
||||
name="PSU 1 power consumption",
|
||||
icon="mdi:lightning-bolt-circle",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
suggested_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="psu1-current",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_psu1_voltage",
|
||||
name="PSU 1 Voltage",
|
||||
icon="mdi:lightning-bolt",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="psu1-voltage",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_psu2_current",
|
||||
name="PSU 2 power consumption",
|
||||
icon="mdi:lightning-bolt-circle",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
suggested_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="psu2-current",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_psu2_voltage",
|
||||
name="PSU 2 Voltage",
|
||||
icon="mdi:lightning-bolt",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="health",
|
||||
data_attribute="psu2-voltage",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_uptime",
|
||||
name="Uptime",
|
||||
icon=None,
|
||||
|
@ -202,13 +431,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_cpu-load": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_cpu-load",
|
||||
name="CPU load",
|
||||
icon="mdi:speedometer",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
|
@ -217,13 +446,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_memory-usage": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_memory-usage",
|
||||
name="Memory usage",
|
||||
icon="mdi:memory",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
|
@ -232,13 +461,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_hdd-usage": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_hdd-usage",
|
||||
name="HDD usage",
|
||||
icon="mdi:harddisk",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
|
@ -247,13 +476,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_clients-wired": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_clients-wired",
|
||||
name="Wired Clients",
|
||||
name="Wired clients",
|
||||
icon="mdi:lan",
|
||||
native_unit_of_measurement=None,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
|
@ -262,13 +491,13 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"system_clients-wireless": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_clients-wireless",
|
||||
name="Wireless Clients",
|
||||
name="Wireless clients",
|
||||
icon="mdi:wifi",
|
||||
native_unit_of_measurement=None,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
|
@ -277,12 +506,61 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
"traffic_tx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_captive-authorized",
|
||||
name="Captive portal clients",
|
||||
icon="mdi:key-wireless",
|
||||
native_unit_of_measurement=None,
|
||||
device_class=None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="System",
|
||||
data_path="resource",
|
||||
data_attribute="captive_authorized",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_gps-latitude",
|
||||
name="Latitude",
|
||||
icon="mdi:latitude",
|
||||
native_unit_of_measurement=None,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="gps",
|
||||
data_attribute="latitude",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_GPS,
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="system_gps-longitude",
|
||||
name="Longitude",
|
||||
icon="mdi:longitude",
|
||||
native_unit_of_measurement=None,
|
||||
device_class=None,
|
||||
state_class=None,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ha_group="System",
|
||||
data_path="gps",
|
||||
data_attribute="longitude",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_GPS,
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="traffic_tx",
|
||||
name="TX",
|
||||
icon="mdi:upload-network-outline",
|
||||
native_unit_of_measurement="data__tx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="data__default-name",
|
||||
|
@ -290,17 +568,20 @@ SENSOR_TYPES = {
|
|||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_attribute="tx",
|
||||
data_name="name",
|
||||
data_name="default-name",
|
||||
data_uid="",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikInterfaceTrafficSensor",
|
||||
),
|
||||
"traffic_rx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="traffic_rx",
|
||||
name="RX",
|
||||
icon="mdi:download-network-outline",
|
||||
native_unit_of_measurement="data__rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="data__default-name",
|
||||
|
@ -308,17 +589,62 @@ SENSOR_TYPES = {
|
|||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_attribute="rx",
|
||||
data_name="name",
|
||||
data_name="default-name",
|
||||
data_uid="",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikInterfaceTrafficSensor",
|
||||
),
|
||||
"client_traffic_lan_tx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="tx-total",
|
||||
name="TX total",
|
||||
icon="mdi:upload-network",
|
||||
native_unit_of_measurement=UnitOfInformation.BYTES,
|
||||
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_category=None,
|
||||
ha_group="data__default-name",
|
||||
ha_connection=CONNECTION_NETWORK_MAC,
|
||||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_attribute="tx-current",
|
||||
data_name="default-name",
|
||||
data_uid="",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikInterfaceTrafficSensor",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="rx-total",
|
||||
name="RX total",
|
||||
icon="mdi:download-network",
|
||||
native_unit_of_measurement=UnitOfInformation.BYTES,
|
||||
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_category=None,
|
||||
ha_group="data__default-name",
|
||||
ha_connection=CONNECTION_NETWORK_MAC,
|
||||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_attribute="rx-current",
|
||||
data_name="default-name",
|
||||
data_uid="",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikInterfaceTrafficSensor",
|
||||
),
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_lan_tx",
|
||||
name="LAN TX",
|
||||
icon="mdi:upload-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -330,13 +656,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"client_traffic_lan_rx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_lan_rx",
|
||||
name="LAN RX",
|
||||
icon="mdi:download-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -348,13 +677,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"client_traffic_wan_tx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_wan_tx",
|
||||
name="WAN TX",
|
||||
icon="mdi:upload-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -366,13 +698,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"client_traffic_wan_rx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_wan_rx",
|
||||
name="WAN RX",
|
||||
icon="mdi:download-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -384,13 +719,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"client_traffic_tx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_tx",
|
||||
name="TX",
|
||||
icon="mdi:upload-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -402,13 +740,16 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"client_traffic_rx": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="client_traffic_rx",
|
||||
name="RX",
|
||||
icon="mdi:download-network",
|
||||
native_unit_of_measurement="data__tx-rx-attr",
|
||||
device_class=None,
|
||||
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
|
||||
suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
suggested_display_precision=1,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=None,
|
||||
ha_group="",
|
||||
|
@ -420,8 +761,9 @@ SENSOR_TYPES = {
|
|||
data_uid="",
|
||||
data_reference="mac-address",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
|
||||
func="MikrotikClientTrafficSensor",
|
||||
),
|
||||
"environment": MikrotikSensorEntityDescription(
|
||||
MikrotikSensorEntityDescription(
|
||||
key="environment",
|
||||
name="",
|
||||
icon="mdi:clipboard-list",
|
||||
|
@ -438,4 +780,6 @@ SENSOR_TYPES = {
|
|||
data_uid="name",
|
||||
data_reference="name",
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
SENSOR_SERVICES = []
|
||||
|
|
|
@ -1,9 +1 @@
|
|||
run_script:
|
||||
description: Run script on Mikrotik
|
||||
fields:
|
||||
router:
|
||||
description: Name of the router
|
||||
example: "Mikrotik"
|
||||
script:
|
||||
description: Name of the script
|
||||
example: "MyScript"
|
||||
---
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect to Mikrotik.",
|
||||
"connection_timeout": "Mikrotik connection timeout.",
|
||||
"name_exists": "Name already exists.",
|
||||
"ssl_handshake_failure": "SSL handshake failure",
|
||||
"wrong_login": "Invalid user name or password."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host",
|
||||
"name": "Name of the integration",
|
||||
"password": "Password",
|
||||
"port": "Port",
|
||||
"ssl": "Use SSL",
|
||||
"username": "Username"
|
||||
},
|
||||
"title": "Set up Mikrotik Router",
|
||||
"description": "Set up Mikrotik Router integration.",
|
||||
"title": "Mikrotik Router"
|
||||
"data": {
|
||||
"name": "Name of the integration",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"ssl": "Use SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Name already exists.",
|
||||
"cannot_connect": "Cannot connect to Mikrotik.",
|
||||
"ssl_handshake_failure": "SSL handshake failure",
|
||||
"ssl_verify_failure": "Certificate verify failed",
|
||||
"connection_timeout": "Mikrotik connection timeout.",
|
||||
"wrong_login": "Invalid user name or password."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -32,24 +34,28 @@
|
|||
"track_network_hosts_timeout": "Track network devices timeout (seconds)",
|
||||
"zone": "Zone for device tracker"
|
||||
},
|
||||
"title": "Basic options"
|
||||
"title": "Mikrotik Router options (1/2)",
|
||||
"description": "Configure integration"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"sensor_port_tracker": "Port tracker sensors",
|
||||
"sensor_port_traffic": "Port traffic sensors",
|
||||
"track_network_hosts": "Track network devices",
|
||||
"sensor_port_tracker": "Port tracker sensors",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors",
|
||||
"sensor_port_traffic": "Port traffic sensors",
|
||||
"sensor_client_traffic": "Client traffic sensors",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_simple_queues": "Simple queues switches",
|
||||
"sensor_nat": "NAT switches",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_filter": "Filter switches",
|
||||
"sensor_scripts": "Script switches",
|
||||
"sensor_environment": "Environment variable sensors",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_scripts": "Scripts switches",
|
||||
"sensor_environment": "Environment variable sensors"
|
||||
"sensor_filter": "Filter switches"
|
||||
},
|
||||
"title": "Sensor selection"
|
||||
"title": "Mikrotik Router options (2/2)",
|
||||
"description": "Enable sensors and switches"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,166 +1,70 @@
|
|||
"""Support for the Mikrotik Router switches."""
|
||||
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Optional
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.const import CONF_NAME, CONF_HOST, ATTR_ATTRIBUTION
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .entity import MikrotikEntity, async_add_entities
|
||||
from .helper import format_attribute
|
||||
from .const import DOMAIN, ATTRIBUTION
|
||||
from .switch_types import (
|
||||
MikrotikSwitchEntityDescription,
|
||||
SWITCH_TYPES,
|
||||
SENSOR_TYPES,
|
||||
SENSOR_SERVICES,
|
||||
DEVICE_ATTRIBUTES_IFACE_ETHER,
|
||||
DEVICE_ATTRIBUTES_IFACE_SFP,
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up switches for Mikrotik Router component."""
|
||||
inst = config_entry.data[CONF_NAME]
|
||||
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id]
|
||||
switches = {}
|
||||
|
||||
@callback
|
||||
def update_controller():
|
||||
"""Update the values of the controller."""
|
||||
update_items(inst, mikrotik_controller, async_add_entities, switches)
|
||||
|
||||
mikrotik_controller.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, mikrotik_controller.signal_update, update_controller
|
||||
)
|
||||
)
|
||||
|
||||
update_controller()
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikSwitch": MikrotikSwitch,
|
||||
"MikrotikPortSwitch": MikrotikPortSwitch,
|
||||
"MikrotikNATSwitch": MikrotikNATSwitch,
|
||||
"MikrotikMangleSwitch": MikrotikMangleSwitch,
|
||||
"MikrotikFilterSwitch": MikrotikFilterSwitch,
|
||||
"MikrotikQueueSwitch": MikrotikQueueSwitch,
|
||||
"MikrotikKidcontrolPauseSwitch": MikrotikKidcontrolPauseSwitch,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# update_items
|
||||
# MikrotikSwitch
|
||||
# ---------------------------
|
||||
@callback
|
||||
def update_items(inst, mikrotik_controller, async_add_entities, switches):
|
||||
"""Update device switch state from the controller."""
|
||||
new_switches = []
|
||||
|
||||
# Add switches
|
||||
for switch, sid_func in zip(
|
||||
# Switch type name
|
||||
[
|
||||
"interface",
|
||||
"nat",
|
||||
"mangle",
|
||||
"filter",
|
||||
"ppp_secret",
|
||||
"queue",
|
||||
"kidcontrol_enable",
|
||||
"kidcontrol_pause",
|
||||
],
|
||||
# Entity function
|
||||
[
|
||||
MikrotikControllerPortSwitch,
|
||||
MikrotikControllerNATSwitch,
|
||||
MikrotikControllerMangleSwitch,
|
||||
MikrotikControllerFilterSwitch,
|
||||
MikrotikControllerSwitch,
|
||||
MikrotikControllerQueueSwitch,
|
||||
MikrotikControllerSwitch,
|
||||
MikrotikControllerKidcontrolPauseSwitch,
|
||||
],
|
||||
):
|
||||
uid_switch = SWITCH_TYPES[switch]
|
||||
for uid in mikrotik_controller.data[SWITCH_TYPES[switch].data_path]:
|
||||
uid_data = mikrotik_controller.data[SWITCH_TYPES[switch].data_path]
|
||||
item_id = f"{inst}-{switch}-{uid_data[uid][uid_switch.data_reference]}"
|
||||
_LOGGER.debug("Updating sensor %s", item_id)
|
||||
if item_id in switches:
|
||||
if switches[item_id].enabled:
|
||||
switches[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
switches[item_id] = sid_func(
|
||||
inst=inst,
|
||||
uid=uid,
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
entity_description=uid_switch,
|
||||
)
|
||||
new_switches.append(switches[item_id])
|
||||
|
||||
if new_switches:
|
||||
async_add_entities(new_switches)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerSwitch(SwitchEntity, RestoreEntity):
|
||||
class MikrotikSwitch(MikrotikEntity, SwitchEntity, RestoreEntity):
|
||||
"""Representation of a switch."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inst,
|
||||
uid,
|
||||
mikrotik_controller,
|
||||
entity_description: MikrotikSwitchEntityDescription,
|
||||
):
|
||||
self.entity_description = entity_description
|
||||
self._inst = inst
|
||||
self._ctrl = mikrotik_controller
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._data = mikrotik_controller.data[self.entity_description.data_path][uid]
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if controller is available."""
|
||||
return self._ctrl.connected()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if self.entity_description.data_name_comment and self._data["comment"]:
|
||||
return (
|
||||
f"{self._inst} {self.entity_description.name} {self._data['comment']}"
|
||||
)
|
||||
|
||||
return f"{self._inst} {self.entity_description.name} {self._data[self.entity_description.data_name]}"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique id for this entity."""
|
||||
return f"{self._inst.lower()}-{self.entity_description.key}-{self._data[self.entity_description.data_reference].lower()}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._data[self.entity_description.data_is_on]
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
if self._data[self.entity_description.data_is_on]:
|
||||
if self._data[self.entity_description.data_attribute]:
|
||||
return self.entity_description.icon_enabled
|
||||
else:
|
||||
return self.entity_description.icon_disabled
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = super().extra_state_attributes
|
||||
for variable in self.entity_description.data_attributes_list:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Required abstract method."""
|
||||
pass
|
||||
|
@ -171,65 +75,33 @@ class MikrotikControllerSwitch(SwitchEntity, RestoreEntity):
|
|||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
await self._ctrl.async_update()
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a description for device registry."""
|
||||
dev_connection = DOMAIN
|
||||
dev_connection_value = self.entity_description.data_reference
|
||||
dev_group = self.entity_description.ha_group
|
||||
if self.entity_description.ha_group.startswith("data__"):
|
||||
dev_group = self.entity_description.ha_group[6:]
|
||||
if dev_group in self._data:
|
||||
dev_group = self._data[dev_group]
|
||||
dev_connection_value = dev_group
|
||||
|
||||
if self.entity_description.ha_connection:
|
||||
dev_connection = self.entity_description.ha_connection
|
||||
|
||||
if self.entity_description.ha_connection_value:
|
||||
dev_connection_value = self.entity_description.ha_connection_value
|
||||
if dev_connection_value.startswith("data__"):
|
||||
dev_connection_value = dev_connection_value[6:]
|
||||
dev_connection_value = self._data[dev_connection_value]
|
||||
|
||||
info = DeviceInfo(
|
||||
connections={(dev_connection, f"{dev_connection_value}")},
|
||||
identifiers={(dev_connection, f"{dev_connection_value}")},
|
||||
default_name=f"{self._inst} {dev_group}",
|
||||
model=f"{self._ctrl.data['resource']['board-name']}",
|
||||
manufacturer=f"{self._ctrl.data['resource']['platform']}",
|
||||
sw_version=f"{self._ctrl.data['resource']['version']}",
|
||||
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}",
|
||||
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"),
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added to hass."""
|
||||
_LOGGER.debug("New switch %s (%s)", self._inst, self.unique_id)
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerPortSwitch
|
||||
# MikrotikPortSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerPortSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikPortSwitch(MikrotikSwitch):
|
||||
"""Representation of a network port switch."""
|
||||
|
||||
@property
|
||||
|
@ -247,6 +119,11 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch):
|
|||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
elif self._data["type"] == "wlan":
|
||||
for variable in DEVICE_ATTRIBUTES_IFACE_WIRELESS:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
return attributes
|
||||
|
||||
@property
|
||||
|
@ -264,6 +141,9 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch):
|
|||
|
||||
async def async_turn_on(self) -> Optional[str]:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
if self._data["about"] == "managed by CAPsMAN":
|
||||
|
@ -273,16 +153,19 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch):
|
|||
param = "name"
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
|
||||
if "poe-out" in self._data and self._data["poe-out"] == "off":
|
||||
path = "/interface/ethernet"
|
||||
self._ctrl.set_value(path, param, value, "poe-out", "auto-on")
|
||||
self.coordinator.set_value(path, param, value, "poe-out", "auto-on")
|
||||
|
||||
await self._ctrl.force_update()
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> Optional[str]:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
if self._data["about"] == "managed by CAPsMAN":
|
||||
|
@ -292,201 +175,223 @@ class MikrotikControllerPortSwitch(MikrotikControllerSwitch):
|
|||
param = "name"
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
|
||||
if "poe-out" in self._data and self._data["poe-out"] == "auto-on":
|
||||
path = "/interface/ethernet"
|
||||
self._ctrl.set_value(path, param, value, "poe-out", "off")
|
||||
self.coordinator.set_value(path, param, value, "poe-out", "off")
|
||||
|
||||
await self._ctrl.async_update()
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerNATSwitch
|
||||
# MikrotikNATSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerNATSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikNATSwitch(MikrotikSwitch):
|
||||
"""Representation of a NAT switch."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name."""
|
||||
if self._data["comment"]:
|
||||
return f"{self._inst} NAT {self._data['comment']}"
|
||||
|
||||
return f"{self._inst} NAT {self._data['name']}"
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["nat"]:
|
||||
if self._ctrl.data["nat"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["nat"]:
|
||||
if self.coordinator.data["nat"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},"
|
||||
f"{self._data['in-interface']}:{self._data['dst-port']}-"
|
||||
f"{self._data['out-interface']}:{self._data['to-addresses']}:{self._data['to-ports']}"
|
||||
):
|
||||
value = self._ctrl.data["nat"][uid][".id"]
|
||||
value = self.coordinator.data["nat"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["nat"]:
|
||||
if self._ctrl.data["nat"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["nat"]:
|
||||
if self.coordinator.data["nat"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},"
|
||||
f"{self._data['in-interface']}:{self._data['dst-port']}-"
|
||||
f"{self._data['out-interface']}:{self._data['to-addresses']}:{self._data['to-ports']}"
|
||||
):
|
||||
value = self._ctrl.data["nat"][uid][".id"]
|
||||
value = self.coordinator.data["nat"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
await self._ctrl.async_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerMangleSwitch
|
||||
# MikrotikMangleSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerMangleSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikMangleSwitch(MikrotikSwitch):
|
||||
"""Representation of a Mangle switch."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["mangle"]:
|
||||
if self._ctrl.data["mangle"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["mangle"]:
|
||||
if self.coordinator.data["mangle"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},"
|
||||
f"{self._data['src-address']}:{self._data['src-port']}-"
|
||||
f"{self._data['dst-address']}:{self._data['dst-port']},"
|
||||
f"{self._data['src-address-list']}-{self._data['dst-address-list']}"
|
||||
):
|
||||
value = self._ctrl.data["mangle"][uid][".id"]
|
||||
value = self.coordinator.data["mangle"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["mangle"]:
|
||||
if self._ctrl.data["mangle"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["mangle"]:
|
||||
if self.coordinator.data["mangle"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},"
|
||||
f"{self._data['src-address']}:{self._data['src-port']}-"
|
||||
f"{self._data['dst-address']}:{self._data['dst-port']},"
|
||||
f"{self._data['src-address-list']}-{self._data['dst-address-list']}"
|
||||
):
|
||||
value = self._ctrl.data["mangle"][uid][".id"]
|
||||
value = self.coordinator.data["mangle"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
await self._ctrl.async_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerFilterSwitch
|
||||
# MikrotikFilterSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerFilterSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikFilterSwitch(MikrotikSwitch):
|
||||
"""Representation of a Filter switch."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["filter"]:
|
||||
if self._ctrl.data["filter"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["filter"]:
|
||||
if self.coordinator.data["filter"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},{self._data['layer7-protocol']},"
|
||||
f"{self._data['in-interface']},{self._data['in-interface-list']}:{self._data['src-address']},{self._data['src-address-list']}:{self._data['src-port']}-"
|
||||
f"{self._data['out-interface']},{self._data['out-interface-list']}:{self._data['dst-address']},{self._data['dst-address-list']}:{self._data['dst-port']}"
|
||||
):
|
||||
value = self._ctrl.data["filter"][uid][".id"]
|
||||
value = self.coordinator.data["filter"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["filter"]:
|
||||
if self._ctrl.data["filter"][uid]["uniq-id"] == (
|
||||
for uid in self.coordinator.data["filter"]:
|
||||
if self.coordinator.data["filter"][uid]["uniq-id"] == (
|
||||
f"{self._data['chain']},{self._data['action']},{self._data['protocol']},{self._data['layer7-protocol']},"
|
||||
f"{self._data['in-interface']},{self._data['in-interface-list']}:{self._data['src-address']},{self._data['src-address-list']}:{self._data['src-port']}-"
|
||||
f"{self._data['out-interface']},{self._data['out-interface-list']}:{self._data['dst-address']},{self._data['dst-address-list']}:{self._data['dst-port']}"
|
||||
):
|
||||
value = self._ctrl.data["filter"][uid][".id"]
|
||||
value = self.coordinator.data["filter"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
await self._ctrl.async_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerQueueSwitch
|
||||
# MikrotikQueueSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerQueueSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikQueueSwitch(MikrotikSwitch):
|
||||
"""Representation of a queue switch."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["queue"]:
|
||||
if self._ctrl.data["queue"][uid]["name"] == f"{self._data['name']}":
|
||||
value = self._ctrl.data["queue"][uid][".id"]
|
||||
for uid in self.coordinator.data["queue"]:
|
||||
if self.coordinator.data["queue"][uid]["name"] == f"{self._data['name']}":
|
||||
value = self.coordinator.data["queue"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, False)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = ".id"
|
||||
value = None
|
||||
for uid in self._ctrl.data["queue"]:
|
||||
if self._ctrl.data["queue"][uid]["name"] == f"{self._data['name']}":
|
||||
value = self._ctrl.data["queue"][uid][".id"]
|
||||
for uid in self.coordinator.data["queue"]:
|
||||
if self.coordinator.data["queue"][uid]["name"] == f"{self._data['name']}":
|
||||
value = self.coordinator.data["queue"][uid][".id"]
|
||||
|
||||
mod_param = self.entity_description.data_switch_parameter
|
||||
self._ctrl.set_value(path, param, value, mod_param, True)
|
||||
await self._ctrl.async_update()
|
||||
self.coordinator.set_value(path, param, value, mod_param, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikControllerKidcontrolPauseSwitch
|
||||
# MikrotikKidcontrolPauseSwitch
|
||||
# ---------------------------
|
||||
class MikrotikControllerKidcontrolPauseSwitch(MikrotikControllerSwitch):
|
||||
class MikrotikKidcontrolPauseSwitch(MikrotikSwitch):
|
||||
"""Representation of a queue switch."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
command = "resume"
|
||||
self._ctrl.execute(path, command, param, value)
|
||||
await self._ctrl.force_update()
|
||||
self.coordinator.execute(path, command, param, value)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off the switch."""
|
||||
if "write" not in self.coordinator.data["access"]:
|
||||
return
|
||||
|
||||
path = self.entity_description.data_switch_path
|
||||
param = self.entity_description.data_reference
|
||||
value = self._data[self.entity_description.data_reference]
|
||||
command = "pause"
|
||||
self._ctrl.execute(path, command, param, value)
|
||||
await self._ctrl.async_update()
|
||||
self.coordinator.execute(path, command, param, value)
|
||||
await self.coordinator.async_refresh()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
"""Definitions for Mikrotik Router sensor entities."""
|
||||
"""Definitions for Mikrotik Router switch entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.components.switch import (
|
||||
SwitchDeviceClass,
|
||||
SwitchEntityDescription,
|
||||
|
@ -57,6 +59,29 @@ DEVICE_ATTRIBUTES_IFACE_SFP = [
|
|||
"eeprom-checksum",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_IFACE_WIRELESS = [
|
||||
"ssid",
|
||||
"mode",
|
||||
"radio-name",
|
||||
"interface-type",
|
||||
"country",
|
||||
"installation",
|
||||
"antenna-gain",
|
||||
"frequency",
|
||||
"band",
|
||||
"channel-width",
|
||||
"secondary-frequency",
|
||||
"wireless-protocol",
|
||||
"rate-set",
|
||||
"distance",
|
||||
"tx-power-mode",
|
||||
"vlan-id",
|
||||
"wds-mode",
|
||||
"wds-default-bridge",
|
||||
"bridge-mode",
|
||||
"hide-ssid",
|
||||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_NAT = [
|
||||
"protocol",
|
||||
"dst-port",
|
||||
|
@ -105,6 +130,7 @@ DEVICE_ATTRIBUTES_PPP_SECRET = [
|
|||
]
|
||||
|
||||
DEVICE_ATTRIBUTES_KIDCONTROL = [
|
||||
"blocked",
|
||||
"rate-limit",
|
||||
"mon",
|
||||
"tue",
|
||||
|
@ -141,26 +167,27 @@ class MikrotikSwitchEntityDescription(SwitchEntityDescription):
|
|||
|
||||
device_class: str = SwitchDeviceClass.SWITCH
|
||||
|
||||
icon_enabled: str = ""
|
||||
icon_disabled: str = ""
|
||||
ha_group: str = ""
|
||||
ha_connection: str = ""
|
||||
ha_connection_value: str = ""
|
||||
data_path: str = ""
|
||||
data_is_on: str = "enabled"
|
||||
data_switch_path: str = ""
|
||||
icon_enabled: str | None = None
|
||||
icon_disabled: str | None = None
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str = "enabled"
|
||||
data_switch_path: str | None = None
|
||||
data_switch_parameter: str = "disabled"
|
||||
data_name: str = ""
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str = ""
|
||||
data_reference: str = ""
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikSwitch"
|
||||
|
||||
|
||||
SWITCH_TYPES = {
|
||||
"interface": MikrotikSwitchEntityDescription(
|
||||
SENSOR_TYPES: tuple[MikrotikSwitchEntityDescription, ...] = (
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="interface",
|
||||
name="port",
|
||||
name="Port",
|
||||
icon_enabled="mdi:lan-connect",
|
||||
icon_disabled="mdi:lan-pending",
|
||||
entity_category=None,
|
||||
|
@ -169,14 +196,15 @@ SWITCH_TYPES = {
|
|||
ha_connection_value="data__port-mac-address",
|
||||
data_path="interface",
|
||||
data_switch_path="/interface",
|
||||
data_name="name",
|
||||
data_name="default-name",
|
||||
data_uid="name",
|
||||
data_reference="default-name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
|
||||
func="MikrotikPortSwitch",
|
||||
),
|
||||
"nat": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="nat",
|
||||
name="NAT",
|
||||
name="",
|
||||
icon_enabled="mdi:network-outline",
|
||||
icon_disabled="mdi:network-off-outline",
|
||||
entity_category=None,
|
||||
|
@ -190,10 +218,11 @@ SWITCH_TYPES = {
|
|||
data_uid="uniq-id",
|
||||
data_reference="uniq-id",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_NAT,
|
||||
func="MikrotikNATSwitch",
|
||||
),
|
||||
"mangle": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="mangle",
|
||||
name="Mangle",
|
||||
name="",
|
||||
icon_enabled="mdi:bookmark-outline",
|
||||
icon_disabled="mdi:bookmark-off-outline",
|
||||
entity_category=None,
|
||||
|
@ -207,10 +236,11 @@ SWITCH_TYPES = {
|
|||
data_uid="uniq-id",
|
||||
data_reference="uniq-id",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_MANGLE,
|
||||
func="MikrotikMangleSwitch",
|
||||
),
|
||||
"filter": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="filter",
|
||||
name="Filter",
|
||||
name="",
|
||||
icon_enabled="mdi:filter-variant",
|
||||
icon_disabled="mdi:filter-variant-remove",
|
||||
entity_category=None,
|
||||
|
@ -224,8 +254,9 @@ SWITCH_TYPES = {
|
|||
data_uid="uniq-id",
|
||||
data_reference="uniq-id",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_FILTER,
|
||||
func="MikrotikFilterSwitch",
|
||||
),
|
||||
"ppp_secret": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="ppp_secret",
|
||||
name="PPP Secret",
|
||||
icon_enabled="mdi:account-outline",
|
||||
|
@ -241,9 +272,9 @@ SWITCH_TYPES = {
|
|||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_PPP_SECRET,
|
||||
),
|
||||
"queue": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="queue",
|
||||
name="Queue",
|
||||
name="",
|
||||
icon_enabled="mdi:leaf",
|
||||
icon_disabled="mdi:leaf-off",
|
||||
entity_category=None,
|
||||
|
@ -256,10 +287,11 @@ SWITCH_TYPES = {
|
|||
data_uid="name",
|
||||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_QUEUE,
|
||||
func="MikrotikQueueSwitch",
|
||||
),
|
||||
"kidcontrol_enable": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="kidcontrol_enable",
|
||||
name="kidcontrol",
|
||||
name="",
|
||||
icon_enabled="mdi:account",
|
||||
icon_disabled="mdi:account-off",
|
||||
entity_category=None,
|
||||
|
@ -273,9 +305,9 @@ SWITCH_TYPES = {
|
|||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_KIDCONTROL,
|
||||
),
|
||||
"kidcontrol_pause": MikrotikSwitchEntityDescription(
|
||||
MikrotikSwitchEntityDescription(
|
||||
key="kidcontrol_paused",
|
||||
name="kidcontrol paused",
|
||||
name="paused",
|
||||
icon_enabled="mdi:account-outline",
|
||||
icon_disabled="mdi:account-off-outline",
|
||||
entity_category=None,
|
||||
|
@ -283,11 +315,14 @@ SWITCH_TYPES = {
|
|||
ha_connection=DOMAIN,
|
||||
ha_connection_value="Kidcontrol",
|
||||
data_path="kid-control",
|
||||
data_is_on="paused",
|
||||
data_attribute="paused",
|
||||
data_switch_path="/ip/kid-control",
|
||||
data_name="name",
|
||||
data_uid="name",
|
||||
data_reference="name",
|
||||
data_attributes_list=DEVICE_ATTRIBUTES_KIDCONTROL,
|
||||
func="MikrotikKidcontrolPauseSwitch",
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
SENSOR_SERVICES = {}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "ضبط جهاز Mikrotik Router",
|
||||
"description": "إعداد دمج موجِّه (Mikrotik).",
|
||||
"data": {
|
||||
"name": "اسم الدّمج",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "منفذ",
|
||||
"username": "اسم المستخدم",
|
||||
"password": "كلمة المرور",
|
||||
"ssl": "استعمل طبقة الوصلات الآمنة (SSL)"
|
||||
"ssl": "استعمل طبقة الوصلات الآمنة (SSL)",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "لا يمكن الاتصال بـ (Mikrotik).",
|
||||
"ssl_handshake_failure": "فشل تأكيد الاتصال (SSL)",
|
||||
"connection_timeout": "مهلة اتصال (Mikrotik).",
|
||||
"wrong_login": "خطأ في اسم المستخدم أو كلمة مرور."
|
||||
"wrong_login": "خطأ في اسم المستخدم أو كلمة مرور.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "إظهار عنوان التحكّم في الوسائط (MAC) و بروتوكول الإنترنت (IP) للعميل على الواجهات",
|
||||
"unit_of_measurement": "وحدة قياس",
|
||||
"track_network_hosts_timeout": "مهلة تتبّع أجهزة الشبكة (بالثواني)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "منطقة لتعقُّب الجهاز"
|
||||
},
|
||||
"title": "الخيارات الأساسية"
|
||||
"title": "خيارات جهاز Mikrotik Router (1\/2)",
|
||||
"description": "تهيئة التكامل"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "مُبدِّلات ترجمة عنوان الشبكة",
|
||||
"sensor_scripts": "مُبدِّلات النصوص",
|
||||
"sensor_environment": "مُستشعرات متغير البيئة",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "مراقبة الأطفال",
|
||||
"sensor_mangle": "مفاتيح المِكواة الأُسطوانية",
|
||||
"sensor_ppp": "مستخدمي PPP",
|
||||
"sensor_filter": "مفاتيح الترشيح",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "حدد المُستشعرات"
|
||||
"title": "خيارات جهاز Mikrotik Router (2\/2)",
|
||||
"description": "تمكين أجهزة الاستشعار والمفاتيح"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Nastavit Mikrotik Router",
|
||||
"description": "Nastavte integraci Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Název integrace",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Uživatel",
|
||||
"password": "Heslo",
|
||||
"ssl": "Použití SSL"
|
||||
"ssl": "Použití SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Nelze se připojit k Mikrotiku.",
|
||||
"ssl_handshake_failure": "Selhání handshake SSL",
|
||||
"connection_timeout": "Časový limit připojení vypršel.",
|
||||
"wrong_login": "Neplatné uživatelské jméno nebo heslo."
|
||||
"wrong_login": "Neplatné uživatelské jméno nebo heslo.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Zobrazit klientské MAC a IP na rozhraních",
|
||||
"unit_of_measurement": "Jednotka měření",
|
||||
"track_network_hosts_timeout": "Časový limit sledování síťových zařízení (v sekundách)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zóna pro sledování zařízení"
|
||||
},
|
||||
"title": "Základní možnosti"
|
||||
"title": "Nastavení Mikrotik Router (1\/2)",
|
||||
"description": "Konfigurovat integraci"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Přepínače NAT",
|
||||
"sensor_scripts": "Přepínače skriptů",
|
||||
"sensor_environment": "Senzory prostředí",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Dětská kontrola",
|
||||
"sensor_mangle": "Mangle spínače",
|
||||
"sensor_ppp": "Uživatelé PPP",
|
||||
"sensor_filter": "Přepínače filtrů",
|
||||
"sensor_client_captive": "Captive portál data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Vyberte senzory"
|
||||
"title": "Nastavení Mikrotik Router (2\/2)",
|
||||
"description": "Povolit senzory a přepínače"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Mikrotik-Router einrichten",
|
||||
"description": "Mikrotik-Router-Integration einstellen",
|
||||
"data": {
|
||||
"name": "Name der Integration",
|
||||
|
@ -11,16 +10,18 @@
|
|||
"port": "Port",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"ssl": "SSL benutzen"
|
||||
"ssl": "SSL benutzen",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Name existiert bereits",
|
||||
"cannot_connect": "Verbindung zu Mikrotik nicht möglich.",
|
||||
"ssl_handshake_failure": "SSL Vereinbarung-Fehler",
|
||||
"cannot_connect": "Verbindung zum MikroTik fehlgeschlagen.",
|
||||
"ssl_handshake_failure": "SSL Handshake Fehler",
|
||||
"connection_timeout": "Mikrotik-Verbindungstimeout.",
|
||||
"wrong_login": "Ungültiger Benutzername oder Passwort."
|
||||
"wrong_login": "Ungültiger Benutzername oder Passwort.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,27 +32,30 @@
|
|||
"track_iface_clients": "Client MAC und IP auf dem Interface anzeigen",
|
||||
"unit_of_measurement": "Maßeinheit",
|
||||
"track_network_hosts_timeout": "Zeitlimit für Netzwerkgeräte verfolgen (Sekunden)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zone für Gerätetracker"
|
||||
},
|
||||
"title": "Grundlegende Optionen"
|
||||
"title": "Mikrotik-Router-Optionen (1\/2)",
|
||||
"description": "Integration konfigurieren"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"track_network_hosts": "Verfolgen Sie Netzwerkgeräte",
|
||||
"sensor_port_tracker": "Hafentracker-Sensoren",
|
||||
"sensor_port_traffic": "Hafen-Verkehrssensoren",
|
||||
"sensor_client_traffic": "Kunden-Verkehrssensoren",
|
||||
"track_network_hosts": "Netzwerkgeräte verfolgen",
|
||||
"sensor_port_tracker": "Port-Statussensoren",
|
||||
"sensor_port_traffic": "Port-Durchsatz Sensoren",
|
||||
"sensor_client_traffic": "Client-Durchsatz Sensoren",
|
||||
"sensor_simple_queues": "Einfache Endschalter",
|
||||
"sensor_nat": "NAT-Schalter",
|
||||
"sensor_scripts": "Skript-Schalter",
|
||||
"sensor_environment": "Umweltvariable Sensoren",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_environment": "Umgebungsvariablen-Sensoren",
|
||||
"sensor_kidcontrol": "Kindersicherung",
|
||||
"sensor_mangle": "Mangle-Schalter",
|
||||
"sensor_ppp": "PPP-Nutzer",
|
||||
"sensor_filter": "Schalter für Filterregeln",
|
||||
"sensor_client_captive": "Captive-Portal Daten",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Sensoren auswählen"
|
||||
"title": "Mikrotik-Router-Optionen (2\/2)",
|
||||
"description": "Sensoren und Schalter aktivieren"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Ρύθμιση του Mikrotik Router",
|
||||
"description": "Ολοκλήρωση εγκατάστασης Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Όνομα ολοκλήρωσης",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Θύρα",
|
||||
"username": "Όνομα χρήστη",
|
||||
"password": "Κωδικός πρόσβασης",
|
||||
"ssl": "Χρήση SSL"
|
||||
"ssl": "Χρήση SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Αδυναμία σύνδεσης στον Mikrotik.",
|
||||
"ssl_handshake_failure": "Αποτυχίας χειραψίας SSL",
|
||||
"connection_timeout": "Διακοπή σύνδεσης με Mikrotik.",
|
||||
"wrong_login": "Το όνομα χρήστη και ο κωδικός πρόσβασης δεν είναι έγκυρα."
|
||||
"wrong_login": "Το όνομα χρήστη και ο κωδικός πρόσβασης δεν είναι έγκυρα.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Προβολή IP και MAC πελάτη στη διεπαφή",
|
||||
"unit_of_measurement": "Μονάδα μέτρησης",
|
||||
"track_network_hosts_timeout": "Εξωχρονισμός ανίχνευσης συσκευών διαδικτύου (δευτερόλεπτα)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Ζώνη για παρακολούθηση συσκευής"
|
||||
},
|
||||
"title": "Βασικές επιογές"
|
||||
"title": "Επιλογές Mikrotik Router (1\/2)",
|
||||
"description": "Διαμόρφωση ολοκλήρωσης"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Διακόπτες NAT",
|
||||
"sensor_scripts": "Διακόπτες δέσμης ενεργειών",
|
||||
"sensor_environment": "Αισθητήρες μεταβολών περιβάλλοντος",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Παιδικός έλεγχος",
|
||||
"sensor_mangle": "Επιλογικοί διακόπτες",
|
||||
"sensor_ppp": "Χρήστες PPP",
|
||||
"sensor_filter": "Διακόπτες φίλτρου",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Επιλέξτε αισθητήρες"
|
||||
"title": "Επιλογές Mikrotik Router (2\/2)",
|
||||
"description": "Ενεργοποίηση αισθητήρων και διακοπτών"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Set up Mikrotik Router",
|
||||
"description": "Set up Mikrotik Router integration.",
|
||||
"data": {
|
||||
"name": "Name of the integration",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"ssl": "Use SSL"
|
||||
"ssl": "Use SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Cannot connect to Mikrotik.",
|
||||
"ssl_handshake_failure": "SSL handshake failure",
|
||||
"connection_timeout": "Mikrotik connection timeout.",
|
||||
"wrong_login": "Invalid user name or password."
|
||||
"wrong_login": "Invalid user name or password.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -33,7 +34,8 @@
|
|||
"track_network_hosts_timeout": "Track network devices timeout (seconds)",
|
||||
"zone": "Zone for device tracker"
|
||||
},
|
||||
"title": "Basic options"
|
||||
"title": "Mikrotik Router options (1\/2)",
|
||||
"description": "Configure integration"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -43,15 +45,17 @@
|
|||
"sensor_client_traffic": "Client traffic sensors",
|
||||
"sensor_simple_queues": "Simple queues switches",
|
||||
"sensor_nat": "NAT switches",
|
||||
"sensor_scripts": "Scripts switches",
|
||||
"sensor_scripts": "Script switches",
|
||||
"sensor_environment": "Environment variable sensors",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_filter": "Filter switches",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Select sensors"
|
||||
"title": "Mikrotik Router options (2\/2)",
|
||||
"description": "Enable sensors and switches"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"description": "Configurar integración del Mikrotik Router.",
|
||||
"title": "Configurar un Mikrotik Router",
|
||||
"description": "Configura la integración del Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Nombre de la integración",
|
||||
"host": "Servidor",
|
||||
"port": "Puerto",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"ssl": "Usar SSL"
|
||||
"ssl": "Usar SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,38 +20,42 @@
|
|||
"cannot_connect": "No es posible conectar con el Mikrotik.",
|
||||
"ssl_handshake_failure": "Fallo en el establecimiento de la comunicación SSL",
|
||||
"connection_timeout": "Conexión con Mikrotik sin respuesta.",
|
||||
"wrong_login": "Nombre de usuario o contraseña no válidos."
|
||||
"wrong_login": "Nombre de usuario o contraseña no válidos.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"basic_options": {
|
||||
"data": {
|
||||
"scan_interval": "Intervalo de escaneo (requiere reinicio del HA)",
|
||||
"scan_interval": "Intervalo de escaneo (requiere reinicio de HA)",
|
||||
"track_iface_clients": "Mostrar MAC e IP del cliente en las interfaces",
|
||||
"unit_of_measurement": "Unidad de medida",
|
||||
"track_network_hosts_timeout": "Monitorizar tiempo hasta desconexión automática de dispositivos de red (segundos)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zona para seguimiento de dispositivo"
|
||||
},
|
||||
"title": "Opciones básicas"
|
||||
"title": "Opciones del Mikrotik Router (1\/2)",
|
||||
"description": "Configurar la integración"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"track_network_hosts": "Monitorizar dispositivos de red",
|
||||
"sensor_port_tracker": "Sensores de seguimiento portuario",
|
||||
"sensor_port_traffic": "Sensores de tráfico portuario",
|
||||
"sensor_port_tracker": "Sensores de seguimiento de puertos",
|
||||
"sensor_port_traffic": "Sensores de tráfico de puertos",
|
||||
"sensor_client_traffic": "Sensores de tráfico de clientes",
|
||||
"sensor_simple_queues": "Interruptores de cola sencillos",
|
||||
"sensor_nat": "Interruptores NAT",
|
||||
"sensor_scripts": "Interruptores de switch",
|
||||
"sensor_scripts": "Interruptores de script",
|
||||
"sensor_environment": "Sensores variables de entorno",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Control de niños",
|
||||
"sensor_mangle": "Interruptor Mangle",
|
||||
"sensor_ppp": "Usuarios PPP",
|
||||
"sensor_filter": "Filtros de interruptores",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Seleccionar sensores"
|
||||
"title": "Opciones del Mikrotik Router 2\/2)",
|
||||
"description": "Activar sensores e interruptores"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Configurar un Mikrotik Router",
|
||||
"description": "Configura la integración del Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Nombre de la integración",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Puerto",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"ssl": "Utilizar SSL"
|
||||
"ssl": "Utilizar SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "No puede conectarse a Mikrotik.",
|
||||
"ssl_handshake_failure": "Fallo del \"handshake\" SSL",
|
||||
"connection_timeout": "Tiempo de espera de la conexión a Mikrotik.",
|
||||
"wrong_login": "Nombre de usuario o contraseña no válida"
|
||||
"wrong_login": "Nombre de usuario o contraseña no válida",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Mostrar MAC e IP de cliente en las interfaces",
|
||||
"unit_of_measurement": "Unidad de medida",
|
||||
"track_network_hosts_timeout": "Rastrear el tiempo de espera de los dispositivos de red (segundos)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zona para rastreador de dispositivos"
|
||||
},
|
||||
"title": "Opciones básicas"
|
||||
"title": "Opciones del Mikrotik Router (1\/2)",
|
||||
"description": "Configurar la integración"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Interruptores NAT",
|
||||
"sensor_scripts": "Interruptores de script",
|
||||
"sensor_environment": "Sensores de variable de entorno",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Control parental",
|
||||
"sensor_mangle": "Conmutadores de mangle",
|
||||
"sensor_ppp": "Usuarios PPP",
|
||||
"sensor_filter": "Conmutadores de filtro",
|
||||
"sensor_client_captive": "Datos portal cautivo",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Seleccionar sensores"
|
||||
"title": "Opciones del Mikrotik Router (2\/2)",
|
||||
"description": "Activar sensores e interruptores"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"description": "Configurer l'intégration de routeur Mikrotik.",
|
||||
"title": "Configurer Mikrotik Router",
|
||||
"description": "Configurer l'intégration de Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Nom de l'intégration",
|
||||
"host": "Hôte",
|
||||
"port": "Port",
|
||||
"port": "Interface",
|
||||
"username": "Identifiant",
|
||||
"password": "Mot de passe",
|
||||
"ssl": "Utiliser SSL"
|
||||
"ssl": "Utiliser SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Impossible de se connecter à Mikrotik.",
|
||||
"ssl_handshake_failure": "Échec de la négociation SSL",
|
||||
"connection_timeout": "Expiration de connexion Mikrotik.",
|
||||
"wrong_login": "Identifiant ou mot de passe invalide."
|
||||
"wrong_login": "Identifiant ou mot de passe invalide.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,27 +32,30 @@
|
|||
"track_iface_clients": "Afficher l'IP et le MAC du client sur les interfaces",
|
||||
"unit_of_measurement": "Unité de mesure",
|
||||
"track_network_hosts_timeout": "Suivre l'expiration des appareils du réseau (secondes)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zone pour le traceur d'appareil"
|
||||
},
|
||||
"title": "Options de base"
|
||||
"title": "Options Mikrotik Router (1\/2)",
|
||||
"description": "Configurer l'intégration"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"track_network_hosts": "Suivre les appareils du réseau",
|
||||
"sensor_port_tracker": "Capteurs de suivi de ports",
|
||||
"sensor_port_traffic": "Capteurs de trafic par ports",
|
||||
"sensor_port_tracker": "Capteurs d'état des interfaces",
|
||||
"sensor_port_traffic": "Capteurs de trafic par interface",
|
||||
"sensor_client_traffic": "Capteurs de trafic de clients",
|
||||
"sensor_simple_queues": "Commutateurs de files d'attente simples",
|
||||
"sensor_nat": "Commutateurs NAT",
|
||||
"sensor_nat": "Règles NAT",
|
||||
"sensor_scripts": "Commutation de scripts",
|
||||
"sensor_environment": "Capteurs de variables d'environnement",
|
||||
"sensor_pppusers": "Utilisateurs PPP",
|
||||
"sensor_kidcontrol": "Contrôle parental",
|
||||
"sensor_mangle": "Commutateurs Mangle",
|
||||
"sensor_mangle": "Règles de Mangle",
|
||||
"sensor_ppp": "Utilisateurs PPP",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_filter": "Règles de filtrage",
|
||||
"sensor_client_captive": "Données du portail captif",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Sélectionner les capteurs"
|
||||
"title": "Options Mikrotik Router (2\/2)",
|
||||
"description": "Activer les capteurs et les sélecteurs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "मिकरोटिक राउटर सेट करें",
|
||||
"description": "Mikrotik राउटर इन्टीग्रेशन सेट अप करें।",
|
||||
"data": {
|
||||
"name": "इन्टीग्रेशन का नाम",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "पोर्ट",
|
||||
"username": "उपयोगकर्ता नाम",
|
||||
"password": "पासवर्ड",
|
||||
"ssl": "SSL का उपयोग करें"
|
||||
"ssl": "SSL का उपयोग करें",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Mikrotik से कनेक्ट नही कर सकते।",
|
||||
"ssl_handshake_failure": "SSL हैन्डशेक विफ़ल हो गया",
|
||||
"connection_timeout": "Mikrotik कनेक्शन टाइम आउट हो गया।",
|
||||
"wrong_login": "अमान्य उपयोगकर्ता नाम या पासवर्ड।"
|
||||
"wrong_login": "अमान्य उपयोगकर्ता नाम या पासवर्ड।",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "ग्राहक का MAC और IP इन्टरफ़ेस पर दिखाएं",
|
||||
"unit_of_measurement": "माप का युनीट",
|
||||
"track_network_hosts_timeout": "नेटवर्क डिवाइस ट्रैक टाइम आउट (सेकन्ड)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "डिवाइस ट्रैकर के लिए ज़ोन"
|
||||
},
|
||||
"title": "बेसिक विकल्प"
|
||||
"title": "Mikrotik Router के विकल्प (1\/2)",
|
||||
"description": "इंटीग्रेशन को कॉन्फ़िगर करें"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT स्विच",
|
||||
"sensor_scripts": "स्क्रिप्ट स्विच",
|
||||
"sensor_environment": "पारिस्थितिक वेरिएबल सेंसर",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "बच्चे के लिए कंट्रोल",
|
||||
"sensor_mangle": "मैंगल स्विच",
|
||||
"sensor_ppp": "PPP यूज़र",
|
||||
"sensor_filter": "फ़िल्टर स्विच",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "सेंसर चुनें"
|
||||
"title": "Mikrotik Router के विकल्प (2\/2)",
|
||||
"description": "सेंसर और स्विच को एनेबल करें"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Állítsa fel a Mikrotik Router",
|
||||
"description": "Állítsd be a Mikrotik Router integrációt.",
|
||||
"data": {
|
||||
"name": "Integráció neve",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Felhasználónév",
|
||||
"password": "Jelszó",
|
||||
"ssl": "SSL használata"
|
||||
"ssl": "SSL használata",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Nem lehet csatlakozni a Mikrotikhez.",
|
||||
"ssl_handshake_failure": "SSL-kézfogás sikertelen",
|
||||
"connection_timeout": "Mikrotik kapcsolat időtúllépés.",
|
||||
"wrong_login": "Érvénytelen felhasználónév vagy jelszó."
|
||||
"wrong_login": "Érvénytelen felhasználónév vagy jelszó.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Ügyfél MAC- és IP-címének mutatása az interfészeken",
|
||||
"unit_of_measurement": "Mértékegység",
|
||||
"track_network_hosts_timeout": "Hálózati eszközök követésének időtúllépése (másodperc)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zóna az eszközkövető számára"
|
||||
},
|
||||
"title": "Alapbeállítások"
|
||||
"title": "Mikrotik Router opciók (1\/2)",
|
||||
"description": "Az integráció konfigurálása"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT kapcsolók",
|
||||
"sensor_scripts": "Szkript kapcsolók",
|
||||
"sensor_environment": "Környezeti változó érzékelők",
|
||||
"sensor_pppusers": "PPP felhasználók",
|
||||
"sensor_kidcontrol": "Szülői felügyelet",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_mangle": "Mangle kapcsolók",
|
||||
"sensor_ppp": "PPP felhasználók",
|
||||
"sensor_filter": "Szűrő kapcsolók",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Válassz érzékelőket"
|
||||
"title": "Mikrotik Router opciók (2\/2)",
|
||||
"description": "Érzékelők és kapcsolók engedélyezése"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Setja upp Mikrotik Router",
|
||||
"description": "Setja upp Mikrotik Router samþættingu.",
|
||||
"data": {
|
||||
"name": "Heiti samþættingar",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Notandanafn",
|
||||
"password": "Lykilorð",
|
||||
"ssl": "Nota SSL"
|
||||
"ssl": "Nota SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Get ekki tengst Mikrotik.",
|
||||
"ssl_handshake_failure": "Villa við að koma á SSL samskiptum",
|
||||
"connection_timeout": "Tengin við Mikrotik rann út á tíma.",
|
||||
"wrong_login": "Ógilt notendanafn eða lykilorð."
|
||||
"wrong_login": "Ógilt notendanafn eða lykilorð.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Sýna MAC og IP vistfang fyrir biðlara á netkortum",
|
||||
"unit_of_measurement": "Mælieining",
|
||||
"track_network_hosts_timeout": "Tímamörk við rakningu á netbúnaði ( sekúndur )",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Svæði fyrir tækjarakningu"
|
||||
},
|
||||
"title": "Grunnvalkostir"
|
||||
"title": "Mikrotik Router valkostir (1\/2)",
|
||||
"description": "Stilla samþættingu"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT rofar",
|
||||
"sensor_scripts": "Skrifturofar",
|
||||
"sensor_environment": "Umhverfisbreytu skynjarar",
|
||||
"sensor_pppusers": "PPP notendur",
|
||||
"sensor_kidcontrol": "Krakkastjórnun",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_mangle": "Bjögunarrofi",
|
||||
"sensor_ppp": "PPP notendur",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_filter": "Síurofar",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Veldu skynjara"
|
||||
"title": "Mikrotik Router valkostir (2\/2)",
|
||||
"description": "Virkja skynjara og rofa"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"description": "Imposta integrazione Router Mikrotik",
|
||||
"title": "Configurare il Mikrotik Router",
|
||||
"description": "Imposta integrazione Mikrotik Router",
|
||||
"data": {
|
||||
"name": "Nome integrazione",
|
||||
"host": "Host",
|
||||
"port": "Porta",
|
||||
"username": "Nome utente",
|
||||
"password": "Password",
|
||||
"ssl": "Usa SSL"
|
||||
"ssl": "Usa SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Impossibile connettersi a Mikrotik.",
|
||||
"ssl_handshake_failure": "Errore SSL handshake",
|
||||
"connection_timeout": "Timeout connessione Mikrotik.",
|
||||
"wrong_login": "Nome utente o password non validi."
|
||||
"wrong_login": "Nome utente o password non validi.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Mostra client MAC e IP sulle interfacce",
|
||||
"unit_of_measurement": "Unità di misura",
|
||||
"track_network_hosts_timeout": "Traccia timeout dei dispositivi di rete (secondi)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zona per localizzatore dispositivo"
|
||||
},
|
||||
"title": "Opzioni di base"
|
||||
"title": "Opzioni Mikrotik Router (1\/2)",
|
||||
"description": "Configura integrazione"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Commutatori NAT",
|
||||
"sensor_scripts": "Commutatori di script",
|
||||
"sensor_environment": "Sensori di variabili ambientali",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Controllo bambini",
|
||||
"sensor_mangle": "Interruttori a manganello",
|
||||
"sensor_ppp": "Utenti PPP",
|
||||
"sensor_filter": "Interruttori del filtro",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Seleziona i sensori"
|
||||
"title": "Opzioni Mikrotik Router (2\/2)",
|
||||
"description": "Abilita sensori e interruttori"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Mikrotik Routerのセットアップ",
|
||||
"description": "Mikrotikルータ統合をセットアップします。",
|
||||
"data": {
|
||||
"name": "統合名",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "ポート",
|
||||
"username": "ユーザ名",
|
||||
"password": "パスワード",
|
||||
"ssl": "SSLを使用します"
|
||||
"ssl": "SSLを使用します",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Mikrotikに接続できません。",
|
||||
"ssl_handshake_failure": "SSLハンドシェイクに失敗しました",
|
||||
"connection_timeout": "Mikrotik接続がタイムアウトしました。",
|
||||
"wrong_login": "ユーザ名またはパスワードが無効です。"
|
||||
"wrong_login": "ユーザ名またはパスワードが無効です。",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "クライアントのMACとインターフェイスのIPを表示します",
|
||||
"unit_of_measurement": "測定単位",
|
||||
"track_network_hosts_timeout": "ネットワーク デバイスのタイムアウト(秒)をトラッキングします",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "デバイス追跡装置用ゾーン"
|
||||
},
|
||||
"title": "基本オプション"
|
||||
"title": "Mikrotik Router オプション (1\/2)",
|
||||
"description": "インテグレーションを設定する"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT スイッチ",
|
||||
"sensor_scripts": "スクリプトスイッチ",
|
||||
"sensor_environment": "環境変数センサー",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "キッドコントロール",
|
||||
"sensor_mangle": "マングルスイッチ",
|
||||
"sensor_ppp": "PPPユーザー",
|
||||
"sensor_filter": "フィルタースイッチ",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "センサーを選択"
|
||||
"title": "Mikrotik Router オプション (2\/2)",
|
||||
"description": "センサーとスイッチを有効にする"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Mikrotik Router 설정",
|
||||
"description": "Mikrotik 라우터 통합을 설정합니다.",
|
||||
"data": {
|
||||
"name": "통합 이름",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "포트",
|
||||
"username": "사용자 이름",
|
||||
"password": "비밀번호",
|
||||
"ssl": "SSL 사용"
|
||||
"ssl": "SSL 사용",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Mikrotik에 연결할 수 없습니다.",
|
||||
"ssl_handshake_failure": "SSL 핸드셰이크 실패",
|
||||
"connection_timeout": "Mikrotik 연결 시간이 초과되었습니다.",
|
||||
"wrong_login": "사용자 이름 또는 비밀번호가 유효하지 않습니다."
|
||||
"wrong_login": "사용자 이름 또는 비밀번호가 유효하지 않습니다.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "인터페이스에 클라이언트 MAC 및 IP 표시",
|
||||
"unit_of_measurement": "측정 단위",
|
||||
"track_network_hosts_timeout": "네트워크 기기 시간 초과 추적(초)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "기기 추적기용 지역"
|
||||
},
|
||||
"title": "기본 설정"
|
||||
"title": "Mikrotik Router 옵션(1\/2)",
|
||||
"description": "통합 환경설정"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "내트 스위치",
|
||||
"sensor_scripts": "스크립트 스위치",
|
||||
"sensor_environment": "환경 변수 센서",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "어린이 이용 제어",
|
||||
"sensor_mangle": "맹글 스위치",
|
||||
"sensor_ppp": "PPP 사용자",
|
||||
"sensor_filter": "필터 스위치",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "센서 선택"
|
||||
"title": "Mikrotik Router 옵션(2\/2)",
|
||||
"description": "센서 및 스위치 활성화"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Lestatiet Mikrotik Router",
|
||||
"description": "Mikrotik maršrutētāja integrācijas konfigurēšana.",
|
||||
"data": {
|
||||
"name": "Integrācijas nosaukums",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Ports",
|
||||
"username": "Lietotājvārds",
|
||||
"password": "Parole",
|
||||
"ssl": "Izmantot SSL"
|
||||
"ssl": "Izmantot SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Neizdodas savienoties ar Mikrotik.",
|
||||
"ssl_handshake_failure": "SSL savienojuma kļūda",
|
||||
"connection_timeout": "Mikrotik savienojuma noilgums.",
|
||||
"wrong_login": "Nederīgs lietotājvārds vai parole."
|
||||
"wrong_login": "Nederīgs lietotājvārds vai parole.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Rādīt klienta MAC un IP interfeisā",
|
||||
"unit_of_measurement": "Mērvienības",
|
||||
"track_network_hosts_timeout": "Tīkla ierīces uzraudzības noilgums (sekundēs)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Lerīces izsekotāja zona"
|
||||
},
|
||||
"title": "Pamata parametri"
|
||||
"title": "Mikrotik Router opcijas (1\/2)",
|
||||
"description": "Konfigurēt integrāciju"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT kārtulu slēdži",
|
||||
"sensor_scripts": "Skriptu slēdži",
|
||||
"sensor_environment": "Mainīgie vides sensori",
|
||||
"sensor_pppusers": "PPP lietotāji",
|
||||
"sensor_kidcontrol": "Bērnu kontrole",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_mangle": "Manglu slēdži",
|
||||
"sensor_ppp": "PPP lietotāji",
|
||||
"sensor_filter": "Filtra slēdži",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Sensora izvēle"
|
||||
"title": "Mikrotik Router opcijas (2\/2)",
|
||||
"description": "Sensoru un slēdžu aktivizēšana"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Mikrotik Router instellen",
|
||||
"description": "Stel Mikrotik Router-integratie in.",
|
||||
"data": {
|
||||
"name": "Naam van de integratie",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Poort",
|
||||
"username": "Gebruikersnaam",
|
||||
"password": "Wachtwoord",
|
||||
"ssl": "Gebruik SSL"
|
||||
"ssl": "Gebruik SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Kan geen verbinding maken met Mikrotik.",
|
||||
"ssl_handshake_failure": "SSL overeenkomings-storing",
|
||||
"connection_timeout": "Mikrotik verbinding time-out.",
|
||||
"wrong_login": "Ongeldige gebruikersnaam of wachtwoord."
|
||||
"wrong_login": "Ongeldige gebruikersnaam of wachtwoord.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Mac en IP van client weergeven op interfaces",
|
||||
"unit_of_measurement": "Meet eenheid",
|
||||
"track_network_hosts_timeout": "Volg de time-out van netwerkapparaten (seconden)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zone voor apparaattracker"
|
||||
},
|
||||
"title": "Basisopties"
|
||||
"title": "Opties Mikrotik Router (1\/2)",
|
||||
"description": "Integratie configureren"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT-switches",
|
||||
"sensor_scripts": "Script-switches",
|
||||
"sensor_environment": "Omgevingsvariabelesensors",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Kindercontrole",
|
||||
"sensor_mangle": "Mangelschakelaars",
|
||||
"sensor_ppp": "PPP-gebruikers",
|
||||
"sensor_filter": "Filterschakelaars",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Selecteer sensors"
|
||||
"title": "Opties Mikrotik Router (2\/2)",
|
||||
"description": "Sensoren en schakelaars inschakelen"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
custom_components/mikrotik_router/translations/no.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Sett opp Mikrotik router",
|
||||
"description": "Sett opp Mikrotik Router integrasjon",
|
||||
"data": {
|
||||
"name": "Navn på integreringen",
|
||||
"host": "Adresse",
|
||||
"port": "Port",
|
||||
"username": "Brukernavn",
|
||||
"password": "Passord",
|
||||
"ssl": "Bruk SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Navnet eksisterer allerede",
|
||||
"cannot_connect": "Kunne ikke koble til Mikrotik",
|
||||
"ssl_handshake_failure": "SSH håndtrykkfeil",
|
||||
"connection_timeout": "Mikrotik tilkoblingen fikk tidsavbrudd",
|
||||
"wrong_login": "Feil brukernavn og\/eller passord",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"basic_options": {
|
||||
"data": {
|
||||
"scan_interval": "Søke interval (krever HA omstart)",
|
||||
"track_iface_clients": "Vis klient MAC og IP på grensesnittet",
|
||||
"unit_of_measurement": "Måleenhet",
|
||||
"track_network_hosts_timeout": "Spor nettverks enhet tidsavbrudd (sekunder)",
|
||||
"zone": "Sone for enhetssporer"
|
||||
},
|
||||
"title": "Mikrotik Router alternativer",
|
||||
"description": "Konfigurer integrasjon"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"track_network_hosts": "Spor nettverksenheter",
|
||||
"sensor_port_tracker": "Port sporings sensorer",
|
||||
"sensor_port_traffic": "Port trafikk sensorer",
|
||||
"sensor_client_traffic": "Klient trafikk sensorer",
|
||||
"sensor_simple_queues": "Enklel kø brytere",
|
||||
"sensor_nat": "NAT Brytere",
|
||||
"sensor_scripts": "Script brytere",
|
||||
"sensor_environment": "Miljøvariabel sensorer",
|
||||
"sensor_kidcontrol": "Barnekontroll",
|
||||
"sensor_mangle": "Mangle brytere",
|
||||
"sensor_ppp": "PPP Brukere",
|
||||
"sensor_filter": "Filter brytere",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Mikrotik Router options (2\/2)",
|
||||
"description": "Skru på sensorer og brytere"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"description": "Skonfiguruj integrację z routerem Mikrotik.",
|
||||
"title": "Skonfiguruj Mikrotik Router",
|
||||
"description": "Skonfiguruj integrację z Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Nazwa integracji",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"username": "Nazwa użytkownika",
|
||||
"password": "Hasło",
|
||||
"ssl": "Użyj SSL"
|
||||
"ssl": "Użyj SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Nie można połączyć się z Mikrotik.",
|
||||
"ssl_handshake_failure": "Błąd uzgadniania SSL",
|
||||
"connection_timeout": "Limit czasu dla połączenia Mikrotik został przekroczony.",
|
||||
"wrong_login": "Nieprawidłowa nazwa użytkownika lub hasło."
|
||||
"wrong_login": "Nieprawidłowa nazwa użytkownika lub hasło.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Pokaż w interfejsach adres MAC i IP klienta",
|
||||
"unit_of_measurement": "Jednostka miary",
|
||||
"track_network_hosts_timeout": "Śledź limity czasu dla urządzeń sieciowych (sekundy)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Strefa programu do śledzenia urządzeń"
|
||||
},
|
||||
"title": "Podstawowe opcje"
|
||||
"title": "Opcje Mikrotik Router (1\/2)",
|
||||
"description": "Skonfiguruj integrację"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Przełączniki NAT",
|
||||
"sensor_scripts": "Przełączniki skryptów",
|
||||
"sensor_environment": "Czujniki zmiennych środowiskowych",
|
||||
"sensor_pppusers": "Użytkownicy PPP",
|
||||
"sensor_kidcontrol": "Kontrola rodzicielska",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_mangle": "Przełączniki mangle",
|
||||
"sensor_ppp": "Użytkownicy PPP",
|
||||
"sensor_filter": "Przełączniki filtrów",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Wybierz czujniki"
|
||||
"title": "Opcje Mikrotik Router (2\/2)",
|
||||
"description": "Włącz czujniki i przełączniki"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Configurar o Mikrotik Router",
|
||||
"description": "Configurar a integração do Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Nome da integração",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Porta",
|
||||
"username": "Nome de utilizador",
|
||||
"password": "Palavra-passe",
|
||||
"ssl": "Usar SSL"
|
||||
"ssl": "Usar SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Não é possível ligar à Mikrotik.",
|
||||
"ssl_handshake_failure": "Falha no reconhecimento SSL",
|
||||
"connection_timeout": "Tempo limite da ligação à Mikrotik.",
|
||||
"wrong_login": "Nome de utilizador ou palavra-passe inválidos."
|
||||
"wrong_login": "Nome de utilizador ou palavra-passe inválidos.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Mostrar MAC e IP do cliente nas interfaces",
|
||||
"unit_of_measurement": "Unidade de medida",
|
||||
"track_network_hosts_timeout": "Rastrear o tempo limite dos dispositivos de rede (segundos)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zona para o rastreador do dispositivo"
|
||||
},
|
||||
"title": "Opções básicas"
|
||||
"title": "Opções do Mikrotik Router (1\/2)",
|
||||
"description": "Configurar integração"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -43,15 +45,17 @@
|
|||
"sensor_client_traffic": "Sensores de tráfego do cliente",
|
||||
"sensor_simple_queues": "Comutadores simples de filas de espera",
|
||||
"sensor_nat": "Comutadores NAT",
|
||||
"sensor_scripts": "Comutadores de scripts",
|
||||
"sensor_scripts": "Comutadores de script",
|
||||
"sensor_environment": "Sensores de variáveis de ambiente",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Controlo de crianças",
|
||||
"sensor_mangle": "Interruptores mangle",
|
||||
"sensor_ppp": "Utilizadores PPP",
|
||||
"sensor_filter": "Interruptores de filtro",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Selecionar os sensores"
|
||||
"title": "Opções do Mikrotik Router (2\/2)",
|
||||
"description": "Ativar sensores e interruptores"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Configuração do Mikrotik Router",
|
||||
"description": "Configurando uma integração de roteador (Mikrotik).",
|
||||
"data": {
|
||||
"name": "Nome da mesclagem",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Porta",
|
||||
"username": "Nome de usuário",
|
||||
"password": "Senha",
|
||||
"ssl": "Usar SSL"
|
||||
"ssl": "Usar SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Não consigo conectar ao Mikrotik.",
|
||||
"ssl_handshake_failure": "Falha no handshake SSL",
|
||||
"connection_timeout": "Tempo limite de conexão do Mikrotik.",
|
||||
"wrong_login": "Nome de usuário ou senha inválidos."
|
||||
"wrong_login": "Nome de usuário ou senha inválidos.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -33,7 +34,8 @@
|
|||
"track_network_hosts_timeout": "Rastrear o tempo limite dos dispositivos de rede (segundos)",
|
||||
"zone": "Zona para rastreador de dispositivos"
|
||||
},
|
||||
"title": "Opções básicas"
|
||||
"title": "Opções de roteador Mikrotik (1\/2)",
|
||||
"description": "Inicialize a integração"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -43,15 +45,17 @@
|
|||
"sensor_client_traffic": "Sensores de tráfego do cliente",
|
||||
"sensor_simple_queues": "Interruptores de filas simples",
|
||||
"sensor_nat": "Interruptores NAT",
|
||||
"sensor_scripts": "Interruptores de scripts",
|
||||
"sensor_scripts": "Interruptores de script",
|
||||
"sensor_environment": "Sensores de variáveis de ambiente",
|
||||
"sensor_pppusers": "Usuários de PPP",
|
||||
"sensor_kidcontrol": "Controle infantil",
|
||||
"sensor_mangle": "Interruptores Mangle",
|
||||
"sensor_ppp": "Usuários de PPP",
|
||||
"sensor_filter": "Interruptores de filtro"
|
||||
"sensor_filter": "Interruptores de filtro",
|
||||
"sensor_client_captive": "sensores de cliente cativo",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Selecione os sensores"
|
||||
"title": "Opções de roteador Mikrotik (2\/2)",
|
||||
"description": "Ativar sensores e interruptores"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Роутер Mikrotik",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Роутер Mikrotik",
|
||||
"title": "Настроить Mikrotik Router",
|
||||
"description": "Настройка Home Assistant для интеграции с Mikrotik.",
|
||||
"data": {
|
||||
"name": "Название интеграции",
|
||||
"host": "Адрес маршрутизатора",
|
||||
"host": "Адрес хоста",
|
||||
"port": "Порт",
|
||||
"username": "Имя пользователя",
|
||||
"password": "Пароль",
|
||||
"ssl": "Использовать SSL"
|
||||
"ssl": "Использовать SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Не удалось подключиться.",
|
||||
"ssl_handshake_failure": "Ошибка SSL-соединения.",
|
||||
"connection_timeout": "Истекло время подключения к Mikrotik.",
|
||||
"wrong_login": "Неверное имя пользователя или пароль."
|
||||
"wrong_login": "Неверное имя пользователя или пароль.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -33,7 +34,8 @@
|
|||
"track_network_hosts_timeout": "Таймаут отслеживания сетевых устройств (в секундах)",
|
||||
"zone": "Зона для отслеживания устройств"
|
||||
},
|
||||
"title": "Базовые параметры"
|
||||
"title": "Параметры Mikrotik Router (1\/2)",
|
||||
"description": "Настроить интеграцию"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Переключатели NAT правил",
|
||||
"sensor_scripts": "Переключатели скриптов",
|
||||
"sensor_environment": "Сенсоры переменных окружения",
|
||||
"sensor_pppusers": "PPP пользователи",
|
||||
"sensor_kidcontrol": "Родительский контроль",
|
||||
"sensor_mangle": "Переключатели правил Mangle",
|
||||
"sensor_mangle": "Переключатели Mangle",
|
||||
"sensor_ppp": "PPP-пользователи",
|
||||
"sensor_filter": "Переключатели фильтров"
|
||||
"sensor_filter": "Переключатели фильтров",
|
||||
"sensor_client_captive": "Данные портала авторизации",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Выберите сенсоры"
|
||||
"title": "Параметры Mikrotik Router (2\/2)",
|
||||
"description": "Включить датчики и переключатели"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Nastaviť Mikrotik Router",
|
||||
"description": "Nastaviť integráciu Mikrotik Router.",
|
||||
"data": {
|
||||
"name": "Názov integrácie",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Užívateľ",
|
||||
"password": "Heslo",
|
||||
"ssl": "Použiť SSL"
|
||||
"ssl": "Použiť SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Nedá sa pripojiť k Mikrotiku.",
|
||||
"ssl_handshake_failure": "Zlyhanie nadviazania spojenia SSL.",
|
||||
"connection_timeout": "Časový limit pripojenia vypršal.",
|
||||
"wrong_login": "Nesprávne užívateľské meno alebo heslo."
|
||||
"wrong_login": "Nesprávne užívateľské meno alebo heslo.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Zobraziť klientske MAC a IP na rozhraniach",
|
||||
"unit_of_measurement": "Merná jednotka",
|
||||
"track_network_hosts_timeout": "Časový limit sledovania sieťových zariadení (sekundy)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Zóna pre sledované zariadenia"
|
||||
},
|
||||
"title": "Základné možnosti"
|
||||
"title": "Možnosti Mikrotik Router (1\/2)",
|
||||
"description": "Nakonfigurovať integráciu"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Prepínače NAT",
|
||||
"sensor_scripts": "Prepínače skriptov",
|
||||
"sensor_environment": "Snímače prostredia",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Detská kontrola",
|
||||
"sensor_mangle": "Mangle prepínače",
|
||||
"sensor_ppp": "PPP používatelia",
|
||||
"sensor_filter": "Prepínače filtrov",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Vybrať senzory"
|
||||
"title": "Možnosti Mikrotik Router (2\/2)",
|
||||
"description": "Povoliť senzory a spínače"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Mikrotik Router'ı Kur",
|
||||
"description": "Mikrotik yönlendirici entegrasyonunu kur",
|
||||
"data": {
|
||||
"name": "Entegrasyon adı",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Port",
|
||||
"username": "Kullanıcı adı",
|
||||
"password": "Şifre",
|
||||
"ssl": "SSL kullan"
|
||||
"ssl": "SSL kullan",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Mikrotik'e bağlanılamıyor.",
|
||||
"ssl_handshake_failure": "SSL uyuşma hatası",
|
||||
"connection_timeout": "Mikrotik bağlantısı zaman aşımı.",
|
||||
"wrong_login": "Geçersiz kullanıcı adı veya şifresi."
|
||||
"wrong_login": "Geçersiz kullanıcı adı veya şifresi.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Müşteri MAC'ini ve IP'sini arayüzlerde göster",
|
||||
"unit_of_measurement": "Ölçü birimi",
|
||||
"track_network_hosts_timeout": "Ağ cihazı izleme zaman aşımı (saniyeler)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Cihaz izleyici için alan"
|
||||
},
|
||||
"title": "Temel seçenekler"
|
||||
"title": "Mikrotik Router seçenekleri (1\/2)",
|
||||
"description": "Entegrasyonu yapılandır"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT switchler",
|
||||
"sensor_scripts": "Betik switchleri",
|
||||
"sensor_environment": "Çevre değişkeni sensörleri",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Çocuk denetimi",
|
||||
"sensor_mangle": "Mangle anahtarları",
|
||||
"sensor_ppp": "PPP kullanıcıları",
|
||||
"sensor_filter": "Filtre anahtarları",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Sensörleri seçin"
|
||||
"title": "Mikrotik Router seçenekleri (2\/2)",
|
||||
"description": "Sensör ve anahtarları etkinleştir"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
custom_components/mikrotik_router/translations/uk.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Налаштувати Mikrotik Router",
|
||||
"description": "Налаштувати Mikrotik Router інтеграцію",
|
||||
"data": {
|
||||
"name": "Назва інтеграції",
|
||||
"host": "Хост",
|
||||
"port": "Порт",
|
||||
"username": "Користувач",
|
||||
"password": "Пароль",
|
||||
"ssl": "Використовувати SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Назва вже існує.",
|
||||
"cannot_connect": "Не можу з'єднатися з Mikrotik.",
|
||||
"ssl_handshake_failure": "Помилка SSH протоколу",
|
||||
"connection_timeout": "Тайм-аут з'єднання з Mikrotik",
|
||||
"wrong_login": "Невірне ім'я або пароль",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"basic_options": {
|
||||
"data": {
|
||||
"scan_interval": "Інтервал сканування (вимагає перезавантаження HA)",
|
||||
"track_iface_clients": "Показувати MAC і IP адреси клієнта",
|
||||
"unit_of_measurement": "Одиниці вимірювання",
|
||||
"track_network_hosts_timeout": "Тайм-аут відстеження мережевих пристроїв (секунди)",
|
||||
"zone": "Зона для трекера пристроїв"
|
||||
},
|
||||
"title": "Налаштування Mikrotik роутера (1\/2)",
|
||||
"description": "Налаштувати інтеграцію"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
"track_network_hosts": "Відстежувати мережеві пристрої",
|
||||
"sensor_port_tracker": "Сенсори трекерів портів",
|
||||
"sensor_port_traffic": "Сенсори трафіку портів",
|
||||
"sensor_client_traffic": "Сенсори трафіку клієнтів",
|
||||
"sensor_simple_queues": "Прості черги і перемикачі",
|
||||
"sensor_nat": "Перемикачі NAT",
|
||||
"sensor_scripts": "Перемикачі скриптів",
|
||||
"sensor_environment": "Сенсори змінних оточення",
|
||||
"sensor_kidcontrol": "Дитячий контроль",
|
||||
"sensor_mangle": "Керувати перемикачами",
|
||||
"sensor_ppp": "Користувачі PPP",
|
||||
"sensor_filter": "Фільтрувати перемикачі",
|
||||
"sensor_client_captive": "Дані Captive portal",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Mikrotik Router options (2\/2)",
|
||||
"description": "Увімкнути сенсори і перемикачі"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "Thiết lập Mikrotik Router",
|
||||
"description": "Thiết lập tích hợp Bộ định tuyến Mikrotik.",
|
||||
"data": {
|
||||
"name": "Tên của tích hợp",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "Cổng",
|
||||
"username": "Tên người dùng",
|
||||
"password": "Mật khẩu",
|
||||
"ssl": "Sử dụng SSL"
|
||||
"ssl": "Sử dụng SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "Không thể kết nối với Mikrotik.",
|
||||
"ssl_handshake_failure": "Lỗi kết nối SSL",
|
||||
"connection_timeout": "Hết thời gian chờ kết nối Mikrotik.",
|
||||
"wrong_login": "Tên người dùng hoặc mật khẩu không hợp lệ."
|
||||
"wrong_login": "Tên người dùng hoặc mật khẩu không hợp lệ.",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "Hiển thị MAC và IP của máy khách trên giao diện",
|
||||
"unit_of_measurement": "Đơn vị đo",
|
||||
"track_network_hosts_timeout": "Theo dõi thời gian chờ thiết bị mạng (giây)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "Khu vực cho trình theo dõi thiết bị"
|
||||
},
|
||||
"title": "Tùy chọn cơ bản"
|
||||
"title": "Cài đặt Mikrotik Router (1\/2)",
|
||||
"description": "Đặt cấu hình tích hợp"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "Bộ chuyển đổi NAT",
|
||||
"sensor_scripts": "Bộ chuyển đổi mã lệnh",
|
||||
"sensor_environment": "Cảm biến biến số môi trường",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "Kiểm soát trẻ em",
|
||||
"sensor_mangle": "Công tắc Mangle",
|
||||
"sensor_ppp": "Người dùng PPP",
|
||||
"sensor_filter": "Công tắc bộ lọc",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "Chọn cảm biến"
|
||||
"title": "Cài đặt Mikrotik Router (2\/2)",
|
||||
"description": "Bật cảm biến và công tắc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Mikrotik Router",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Mikrotik Router",
|
||||
"title": "设置Mikrotik Router",
|
||||
"description": "设置 Mikrotik 路由器集成。",
|
||||
"data": {
|
||||
"name": "集成名称",
|
||||
|
@ -11,7 +10,8 @@
|
|||
"port": "端口",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"ssl": "使用 SSL"
|
||||
"ssl": "使用 SSL",
|
||||
"verify_ssl": "Verify SSL"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,8 @@
|
|||
"cannot_connect": "无法连接到 Mikrotik。",
|
||||
"ssl_handshake_failure": "SSL 交握失败",
|
||||
"connection_timeout": "Mikrotik 连接超时。",
|
||||
"wrong_login": "无效的用户名或密码。"
|
||||
"wrong_login": "无效的用户名或密码。",
|
||||
"ssl_verify_failure": "Certificate verify failed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -31,9 +32,10 @@
|
|||
"track_iface_clients": "显示接口上的客户端 MAC 和 IP",
|
||||
"unit_of_measurement": "测量单位",
|
||||
"track_network_hosts_timeout": "跟踪网络设备超时(秒)",
|
||||
"zone": "Zone for device tracker"
|
||||
"zone": "设备跟踪器的区域"
|
||||
},
|
||||
"title": "基本选项"
|
||||
"title": "Mikrotik Router 选项 (1\/2)",
|
||||
"description": "配置集成"
|
||||
},
|
||||
"sensor_select": {
|
||||
"data": {
|
||||
|
@ -45,13 +47,15 @@
|
|||
"sensor_nat": "NAT 交换机",
|
||||
"sensor_scripts": "脚本交换机",
|
||||
"sensor_environment": "环境变量传感器",
|
||||
"sensor_pppusers": "PPP users",
|
||||
"sensor_kidcontrol": "Kid control",
|
||||
"sensor_mangle": "Mangle switches",
|
||||
"sensor_ppp": "PPP users",
|
||||
"sensor_filter": "Filter switches"
|
||||
"sensor_kidcontrol": "儿童控制",
|
||||
"sensor_mangle": "标记开关",
|
||||
"sensor_ppp": "PPP 用户",
|
||||
"sensor_filter": "过滤器开关",
|
||||
"sensor_client_captive": "Captive portal data",
|
||||
"sensor_netwatch_tracker": "Netwatch tracker sensors"
|
||||
},
|
||||
"title": "选择传感器"
|
||||
"title": "Mikrotik Router 选项 (2\/2)",
|
||||
"description": "启动传感器和开关"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
209
custom_components/mikrotik_router/update.py
Normal file
|
@ -0,0 +1,209 @@
|
|||
"""Support for the Mikrotik Router update service."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from logging import getLogger
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from homeassistant.components.update import (
|
||||
UpdateEntity,
|
||||
UpdateDeviceClass,
|
||||
UpdateEntityFeature,
|
||||
)
|
||||
|
||||
from .coordinator import MikrotikCoordinator
|
||||
from .entity import MikrotikEntity, async_add_entities
|
||||
from .update_types import (
|
||||
SENSOR_TYPES,
|
||||
SENSOR_SERVICES,
|
||||
)
|
||||
from packaging.version import Version
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
DEVICE_UPDATE = "device_update"
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# async_setup_entry
|
||||
# ---------------------------
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
_async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry for component"""
|
||||
dispatcher = {
|
||||
"MikrotikRouterOSUpdate": MikrotikRouterOSUpdate,
|
||||
"MikrotikRouterBoardFWUpdate": MikrotikRouterBoardFWUpdate,
|
||||
}
|
||||
await async_add_entities(hass, config_entry, dispatcher)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikRouterOSUpdate
|
||||
# ---------------------------
|
||||
class MikrotikRouterOSUpdate(MikrotikEntity, UpdateEntity):
|
||||
"""Define an Mikrotik Controller Update entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: MikrotikCoordinator,
|
||||
entity_description,
|
||||
uid: str | None = None,
|
||||
):
|
||||
"""Set up device update entity."""
|
||||
super().__init__(coordinator, entity_description, uid)
|
||||
|
||||
self._attr_supported_features = UpdateEntityFeature.INSTALL
|
||||
self._attr_supported_features |= UpdateEntityFeature.BACKUP
|
||||
self._attr_supported_features |= UpdateEntityFeature.RELEASE_NOTES
|
||||
self._attr_title = self.entity_description.title
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return self._data[self.entity_description.data_attribute]
|
||||
|
||||
@property
|
||||
def installed_version(self) -> str:
|
||||
"""Version installed and in use."""
|
||||
return self._data["installed-version"]
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Latest version available for install."""
|
||||
return self._data["latest-version"]
|
||||
|
||||
async def options_updated(self) -> None:
|
||||
"""No action needed."""
|
||||
|
||||
async def async_install(self, version: str, backup: bool, **kwargs: Any) -> None:
|
||||
"""Install an update."""
|
||||
if backup:
|
||||
self.coordinator.execute("/system/backup", "save", None, None)
|
||||
|
||||
self.coordinator.execute("/system/package/update", "install", None, None)
|
||||
|
||||
async def async_release_notes(self) -> str:
|
||||
"""Return the release notes."""
|
||||
try:
|
||||
session = async_get_clientsession(self.hass)
|
||||
"""Get concatenated changelogs from installed_version to latest_version in reverse order."""
|
||||
versions_to_fetch = generate_version_list(
|
||||
self._data["installed-version"], self._data["latest-version"]
|
||||
)
|
||||
|
||||
tasks = [fetch_changelog(session, version) for version in versions_to_fetch]
|
||||
changelogs = await asyncio.gather(*tasks)
|
||||
|
||||
# Combine all non-empty changelogs, maintaining reverse order
|
||||
combined_changelogs = "\n\n".join(filter(None, changelogs))
|
||||
return combined_changelogs.replace("*) ", "- ")
|
||||
|
||||
except Exception as e:
|
||||
_LOGGER.warning("Failed to download release notes (%s)", e)
|
||||
|
||||
return "Error fetching release notes."
|
||||
|
||||
@property
|
||||
def release_url(self) -> str:
|
||||
"""URL to the full release notes of the latest version available."""
|
||||
return "https://mikrotik.com/download/changelogs"
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikRouterBoardFWUpdate
|
||||
# ---------------------------
|
||||
class MikrotikRouterBoardFWUpdate(MikrotikEntity, UpdateEntity):
|
||||
"""Define an Mikrotik Controller Update entity."""
|
||||
|
||||
TYPE = DEVICE_UPDATE
|
||||
_attr_device_class = UpdateDeviceClass.FIRMWARE
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: MikrotikCoordinator,
|
||||
entity_description,
|
||||
uid: str | None = None,
|
||||
):
|
||||
"""Set up device update entity."""
|
||||
super().__init__(coordinator, entity_description, uid)
|
||||
|
||||
self._attr_supported_features = UpdateEntityFeature.INSTALL
|
||||
self._attr_title = self.entity_description.title
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on."""
|
||||
return (
|
||||
self.data["routerboard"]["current-firmware"]
|
||||
!= self.data["routerboard"]["upgrade-firmware"]
|
||||
)
|
||||
|
||||
@property
|
||||
def installed_version(self) -> str:
|
||||
"""Version installed and in use."""
|
||||
return self._data["current-firmware"]
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Latest version available for install."""
|
||||
return self._data["upgrade-firmware"]
|
||||
|
||||
async def options_updated(self) -> None:
|
||||
"""No action needed."""
|
||||
|
||||
async def async_install(self, version: str, backup: bool, **kwargs: Any) -> None:
|
||||
"""Install an update."""
|
||||
self.coordinator.execute("/system/routerboard", "upgrade", None, None)
|
||||
self.coordinator.execute("/system", "reboot", None, None)
|
||||
|
||||
|
||||
async def fetch_changelog(session, version: str) -> str:
|
||||
"""Asynchronously fetch the changelog for a given version."""
|
||||
url = f"https://cdn.mikrotik.com/routeros/{version}/CHANGELOG"
|
||||
try:
|
||||
async with session.get(url) as response:
|
||||
if response.status == 200:
|
||||
text = await response.text()
|
||||
return text.replace("*) ", "- ")
|
||||
except Exception as e:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def generate_version_list(start_version: str, end_version: str) -> list:
|
||||
"""Generate a list of version strings from start_version to end_version in reverse order."""
|
||||
start = Version(start_version)
|
||||
end = Version(end_version)
|
||||
versions = []
|
||||
|
||||
current = end
|
||||
while current >= start:
|
||||
versions.append(str(current))
|
||||
current = decrement_version(current, start)
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
def decrement_version(version: Version, start_version: Version) -> Version:
|
||||
"""Decrement version by the smallest possible step without going below start_version."""
|
||||
if version.micro > 0:
|
||||
next_patch = version.micro - 1
|
||||
return Version(f"{version.major}.{version.minor}.{next_patch}")
|
||||
elif version.minor > 0:
|
||||
next_minor = version.minor - 1
|
||||
return Version(
|
||||
f"{version.major}.{next_minor}.999"
|
||||
) # Assuming .999 as max patch version
|
||||
else:
|
||||
next_major = version.major - 1
|
||||
return Version(
|
||||
f"{next_major}.999.999"
|
||||
) # Assuming .999 as max minor and patch version
|
56
custom_components/mikrotik_router/update_types.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
"""Definitions for Mikrotik Router update entities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from homeassistant.components.update import UpdateEntityDescription
|
||||
|
||||
|
||||
@dataclass
|
||||
class MikrotikUpdateEntityDescription(UpdateEntityDescription):
|
||||
"""Class describing mikrotik entities."""
|
||||
|
||||
ha_group: str | None = None
|
||||
ha_connection: str | None = None
|
||||
ha_connection_value: str | None = None
|
||||
title: str | None = None
|
||||
data_path: str | None = None
|
||||
data_attribute: str = "available"
|
||||
data_name: str | None = None
|
||||
data_name_comment: bool = False
|
||||
data_uid: str | None = None
|
||||
data_reference: str | None = None
|
||||
data_attributes_list: List = field(default_factory=lambda: [])
|
||||
func: str = "MikrotikRouterOSUpdate"
|
||||
|
||||
|
||||
SENSOR_TYPES: tuple[MikrotikUpdateEntityDescription, ...] = (
|
||||
MikrotikUpdateEntityDescription(
|
||||
key="system_rosupdate",
|
||||
name="RouterOS update",
|
||||
ha_group="System",
|
||||
title="Mikrotik RouterOS",
|
||||
data_path="fw-update",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
func="MikrotikRouterOSUpdate",
|
||||
),
|
||||
MikrotikUpdateEntityDescription(
|
||||
key="system_rbfwupdate",
|
||||
name="RouterBOARD firmware update",
|
||||
ha_group="System",
|
||||
title="Mikrotik RouterBOARD",
|
||||
data_path="routerboard",
|
||||
data_attribute="current-firmware",
|
||||
data_name="",
|
||||
data_uid="",
|
||||
data_reference="",
|
||||
func="MikrotikRouterBoardFWUpdate",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
SENSOR_SERVICES = {}
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
BIN
docs/assets/images/ui/diagnostics.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/assets/images/ui/firmware_update.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/assets/images/ui/gps.png
Normal file
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 41 KiB |
BIN
docs/assets/images/ui/kidcontrol_pause_switch.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 28 KiB |
BIN
docs/assets/images/ui/netwatch_tracker.png
Normal file
After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 38 KiB |
BIN
docs/assets/images/ui/routeros_update.png
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
BIN
docs/assets/images/ui/ups.png
Normal file
After Width: | Height: | Size: 34 KiB |
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"name": "Mikrotik Router",
|
||||
"homeassistant": "2022.2.0",
|
||||
"iot_class": "local_poll",
|
||||
"domains": ["device_tracker", "switch", "button", "sensor", "binary_sensor"],
|
||||
"homeassistant": "2024.3.0",
|
||||
"render_readme": false,
|
||||
"zip_release": true,
|
||||
"filename": "mikrotik_router.zip"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
librouteros>=3.2.0
|
||||
librouteros>=3.4.1
|
||||
mac-vendor-lookup>=0.1.12
|
|
@ -1,5 +1,5 @@
|
|||
[flake8]
|
||||
ignore = W293, # blank line contains whitespace
|
||||
ignore = W293
|
||||
|
||||
max-line-length = 220
|
||||
max-complexity = 10
|
||||
|
@ -7,7 +7,7 @@ exclude = ./.git,
|
|||
./tests,
|
||||
./.github,
|
||||
__pycache__,
|
||||
./docs
|
||||
./docs,
|
||||
./custom_components/mikrotik_router/librouteros_custom
|
||||
|
||||
# Run with: pylint --rcfile=setup.cfg --load-plugins=pylint.extensions.mccabe custom_components
|
||||
|
@ -19,4 +19,4 @@ disable = duplicate-code,
|
|||
too-many-arguments,
|
||||
too-many-instance-attributes,
|
||||
simplifiable-if-expression,
|
||||
#bare-except,
|
||||
bare-except
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
"""Tests for the Mikrotik Router component."""
|
||||
|
||||
from custom_components.mikrotik_router import config_flow
|
||||
|
||||
MOCK_DATA = {
|
||||
config_flow.CONF_NAME: config_flow.DEFAULT_DEVICE_NAME,
|
||||
config_flow.CONF_HOST: config_flow.DEFAULT_HOST,
|
||||
config_flow.CONF_USERNAME: config_flow.DEFAULT_USERNAME,
|
||||
config_flow.CONF_PASSWORD: config_flow.DEFAULT_PASSWORD,
|
||||
config_flow.CONF_PORT: config_flow.DEFAULT_PORT,
|
||||
config_flow.CONF_SSL: config_flow.DEFAULT_SSL,
|
||||
}
|
||||
|
||||
MOCK_OPTIONS = {
|
||||
config_flow.CONF_SCAN_INTERVAL: config_flow.DEFAULT_SCAN_INTERVAL,
|
||||
config_flow.CONF_UNIT_OF_MEASUREMENT: config_flow.DEFAULT_UNIT_OF_MEASUREMENT,
|
||||
config_flow.CONF_TRACK_IFACE_CLIENTS: config_flow.DEFAULT_TRACK_IFACE_CLIENTS,
|
||||
config_flow.CONF_TRACK_HOSTS: config_flow.DEFAULT_TRACK_HOSTS,
|
||||
config_flow.CONF_TRACK_HOSTS_TIMEOUT: config_flow.DEFAULT_TRACK_HOST_TIMEOUT,
|
||||
}
|
1148
tests/common.py
|
@ -1,253 +0,0 @@
|
|||
"""Set up some common test helper things."""
|
||||
import functools
|
||||
import logging
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import requests_mock as _requests_mock
|
||||
|
||||
from homeassistant import util
|
||||
from homeassistant.auth.const import GROUP_ID_ADMIN, GROUP_ID_READ_ONLY
|
||||
from homeassistant.auth.providers import homeassistant, legacy_api_password
|
||||
from homeassistant.components.websocket_api.auth import (
|
||||
TYPE_AUTH,
|
||||
TYPE_AUTH_OK,
|
||||
TYPE_AUTH_REQUIRED,
|
||||
)
|
||||
from homeassistant.components.websocket_api.http import URL
|
||||
from homeassistant.exceptions import ServiceNotFound
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import location
|
||||
|
||||
from tests.ignore_uncaught_exceptions import (
|
||||
IGNORE_UNCAUGHT_EXCEPTIONS,
|
||||
IGNORE_UNCAUGHT_JSON_EXCEPTIONS,
|
||||
)
|
||||
|
||||
pytest.register_assert_rewrite("tests.common")
|
||||
|
||||
from tests.common import ( # noqa: E402, isort:skip
|
||||
CLIENT_ID,
|
||||
INSTANCES,
|
||||
MockUser,
|
||||
async_test_home_assistant,
|
||||
mock_coro,
|
||||
mock_storage as mock_storage,
|
||||
)
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
|
||||
|
||||
|
||||
def check_real(func):
|
||||
"""Force a function to require a keyword _test_real to be passed in."""
|
||||
|
||||
@functools.wraps(func)
|
||||
async def guard_func(*args, **kwargs):
|
||||
real = kwargs.pop("_test_real", None)
|
||||
|
||||
if not real:
|
||||
raise Exception(
|
||||
'Forgot to mock or pass "_test_real=True" to %s', func.__name__
|
||||
)
|
||||
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
return guard_func
|
||||
|
||||
|
||||
# Guard a few functions that would make network connections
|
||||
location.async_detect_location_info = check_real(location.async_detect_location_info)
|
||||
util.get_local_ip = lambda: "127.0.0.1"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def verify_cleanup():
|
||||
"""Verify that the test has cleaned up resources correctly."""
|
||||
yield
|
||||
|
||||
if len(INSTANCES) >= 2:
|
||||
count = len(INSTANCES)
|
||||
for inst in INSTANCES:
|
||||
inst.stop()
|
||||
pytest.exit(f"Detected non stopped instances ({count}), aborting test run")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_storage():
|
||||
"""Fixture to mock storage."""
|
||||
with mock_storage() as stored_data:
|
||||
yield stored_data
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass(loop, hass_storage, request):
|
||||
"""Fixture to provide a test instance of Home Assistant."""
|
||||
|
||||
def exc_handle(loop, context):
|
||||
"""Handle exceptions by rethrowing them, which will fail the test."""
|
||||
exceptions.append(context["exception"])
|
||||
orig_exception_handler(loop, context)
|
||||
|
||||
exceptions = []
|
||||
hass = loop.run_until_complete(async_test_home_assistant(loop))
|
||||
orig_exception_handler = loop.get_exception_handler()
|
||||
loop.set_exception_handler(exc_handle)
|
||||
|
||||
yield hass
|
||||
|
||||
loop.run_until_complete(hass.async_stop(force=True))
|
||||
for ex in exceptions:
|
||||
if (
|
||||
request.module.__name__,
|
||||
request.function.__name__,
|
||||
) in IGNORE_UNCAUGHT_EXCEPTIONS:
|
||||
continue
|
||||
if isinstance(ex, ServiceNotFound):
|
||||
continue
|
||||
if (
|
||||
isinstance(ex, TypeError)
|
||||
and "is not JSON serializable" in str(ex)
|
||||
and (request.module.__name__, request.function.__name__)
|
||||
in IGNORE_UNCAUGHT_JSON_EXCEPTIONS
|
||||
):
|
||||
continue
|
||||
raise ex
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def requests_mock():
|
||||
"""Fixture to provide a requests mocker."""
|
||||
with _requests_mock.mock() as m:
|
||||
yield m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_device_tracker_conf():
|
||||
"""Prevent device tracker from reading/writing data."""
|
||||
devices = []
|
||||
|
||||
async def mock_update_config(path, id, entity):
|
||||
devices.append(entity)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.device_tracker.legacy"
|
||||
".DeviceTracker.async_update_config",
|
||||
side_effect=mock_update_config,
|
||||
), patch(
|
||||
"homeassistant.components.device_tracker.legacy.async_load_config",
|
||||
side_effect=lambda *args: mock_coro(devices),
|
||||
):
|
||||
yield devices
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_access_token(hass, hass_admin_user):
|
||||
"""Return an access token to access Home Assistant."""
|
||||
refresh_token = hass.loop.run_until_complete(
|
||||
hass.auth.async_create_refresh_token(hass_admin_user, CLIENT_ID)
|
||||
)
|
||||
return hass.auth.async_create_access_token(refresh_token)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_owner_user(hass, local_auth):
|
||||
"""Return a Home Assistant admin user."""
|
||||
return MockUser(is_owner=True).add_to_hass(hass)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_admin_user(hass, local_auth):
|
||||
"""Return a Home Assistant admin user."""
|
||||
admin_group = hass.loop.run_until_complete(
|
||||
hass.auth.async_get_group(GROUP_ID_ADMIN)
|
||||
)
|
||||
return MockUser(groups=[admin_group]).add_to_hass(hass)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_read_only_user(hass, local_auth):
|
||||
"""Return a Home Assistant read only user."""
|
||||
read_only_group = hass.loop.run_until_complete(
|
||||
hass.auth.async_get_group(GROUP_ID_READ_ONLY)
|
||||
)
|
||||
return MockUser(groups=[read_only_group]).add_to_hass(hass)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_read_only_access_token(hass, hass_read_only_user):
|
||||
"""Return a Home Assistant read only user."""
|
||||
refresh_token = hass.loop.run_until_complete(
|
||||
hass.auth.async_create_refresh_token(hass_read_only_user, CLIENT_ID)
|
||||
)
|
||||
return hass.auth.async_create_access_token(refresh_token)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def legacy_auth(hass):
|
||||
"""Load legacy API password provider."""
|
||||
prv = legacy_api_password.LegacyApiPasswordAuthProvider(
|
||||
hass,
|
||||
hass.auth._store,
|
||||
{"type": "legacy_api_password", "api_password": "test-password"},
|
||||
)
|
||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||
return prv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def local_auth(hass):
|
||||
"""Load local auth provider."""
|
||||
prv = homeassistant.HassAuthProvider(
|
||||
hass, hass.auth._store, {"type": "homeassistant"}
|
||||
)
|
||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||
return prv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_client(hass, aiohttp_client, hass_access_token):
|
||||
"""Return an authenticated HTTP client."""
|
||||
|
||||
async def auth_client():
|
||||
"""Return an authenticated client."""
|
||||
return await aiohttp_client(
|
||||
hass.http.app, headers={"Authorization": f"Bearer {hass_access_token}"}
|
||||
)
|
||||
|
||||
return auth_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_ws_client(aiohttp_client, hass_access_token, hass):
|
||||
"""Websocket client fixture connected to websocket server."""
|
||||
|
||||
async def create_client(hass=hass, access_token=hass_access_token):
|
||||
"""Create a websocket client."""
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
with patch("homeassistant.components.http.auth.setup_auth"):
|
||||
websocket = await client.ws_connect(URL)
|
||||
auth_resp = await websocket.receive_json()
|
||||
assert auth_resp["type"] == TYPE_AUTH_REQUIRED
|
||||
|
||||
if access_token is None:
|
||||
await websocket.send_json(
|
||||
{"type": TYPE_AUTH, "access_token": "incorrect"}
|
||||
)
|
||||
else:
|
||||
await websocket.send_json(
|
||||
{"type": TYPE_AUTH, "access_token": access_token}
|
||||
)
|
||||
|
||||
auth_ok = await websocket.receive_json()
|
||||
assert auth_ok["type"] == TYPE_AUTH_OK
|
||||
|
||||
# wrap in client
|
||||
websocket.client = client
|
||||
return websocket
|
||||
|
||||
return create_client
|
|
@ -1,38 +0,0 @@
|
|||
"""List of modules that have uncaught exceptions today. Will be shrunk over time."""
|
||||
IGNORE_UNCAUGHT_EXCEPTIONS = [
|
||||
("tests.components.dyson.test_air_quality", "test_purecool_aiq_attributes"),
|
||||
("tests.components.dyson.test_air_quality", "test_purecool_aiq_update_state"),
|
||||
(
|
||||
"tests.components.dyson.test_air_quality",
|
||||
"test_purecool_component_setup_only_once",
|
||||
),
|
||||
("tests.components.dyson.test_air_quality", "test_purecool_aiq_without_discovery"),
|
||||
(
|
||||
"tests.components.dyson.test_air_quality",
|
||||
"test_purecool_aiq_empty_environment_state",
|
||||
),
|
||||
(
|
||||
"tests.components.dyson.test_climate",
|
||||
"test_setup_component_with_parent_discovery",
|
||||
),
|
||||
("tests.components.dyson.test_fan", "test_purecoollink_attributes"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_turn_on"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_speed"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_turn_off"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_dyson_speed"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_oscillate"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_night_mode"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_auto_mode"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_angle"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_flow_direction_front"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_set_timer"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_update_state"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_update_state_filter_inv"),
|
||||
("tests.components.dyson.test_fan", "test_purecool_component_setup_only_once"),
|
||||
("tests.components.dyson.test_sensor", "test_purecool_component_setup_only_once"),
|
||||
("tests.components.ios.test_init", "test_creating_entry_sets_up_sensor"),
|
||||
("tests.components.ios.test_init", "test_not_configuring_ios_not_creates_entry"),
|
||||
("tests.components.local_file.test_camera", "test_file_not_readable"),
|
||||
]
|
||||
|
||||
IGNORE_UNCAUGHT_JSON_EXCEPTIONS = []
|
|
@ -1,186 +0,0 @@
|
|||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import librouteros
|
||||
import pytest
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from custom_components import mikrotik_router
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_USERNAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_SSL,
|
||||
)
|
||||
|
||||
from . import MOCK_DATA
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
DEMO_USER_INPUT = {
|
||||
CONF_NAME: "Home router",
|
||||
CONF_HOST: "0.0.0.0",
|
||||
CONF_USERNAME: "username",
|
||||
CONF_PASSWORD: "password",
|
||||
CONF_PORT: 8278,
|
||||
CONF_SSL: True,
|
||||
}
|
||||
|
||||
DEMO_CONFIG_ENTRY = {
|
||||
CONF_NAME: "Home router",
|
||||
CONF_HOST: "0.0.0.0",
|
||||
CONF_USERNAME: "username",
|
||||
CONF_PASSWORD: "password",
|
||||
CONF_PORT: 8278,
|
||||
CONF_SSL: True,
|
||||
mikrotik_router.mikrotik_controller.CONF_SCAN_INTERVAL: 60,
|
||||
mikrotik_router.mikrotik_controller.CONF_UNIT_OF_MEASUREMENT: "Mbps",
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_IFACE_CLIENTS: True,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS: True,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS_TIMEOUT: 180,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="api")
|
||||
def mock_mikrotik_api():
|
||||
"""Mock an api."""
|
||||
with patch("librouteros.connect"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="auth_error")
|
||||
def mock_api_authentication_error():
|
||||
"""Mock an api."""
|
||||
with patch(
|
||||
"librouteros.connect",
|
||||
side_effect=librouteros.exceptions.TrapError("invalid user name or password"),
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="conn_error")
|
||||
def mock_api_connection_error():
|
||||
"""Mock an api."""
|
||||
with patch(
|
||||
"librouteros.connect", side_effect=librouteros.exceptions.ConnectionClosed
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
async def test_import(hass, api):
|
||||
"""Test import step."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
mikrotik_router.DOMAIN, context={"source": "import"}, data=MOCK_DATA
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "Mikrotik"
|
||||
assert result["data"][CONF_NAME] == "Mikrotik"
|
||||
assert result["data"][CONF_HOST] == "10.0.0.1"
|
||||
assert result["data"][CONF_USERNAME] == "admin"
|
||||
assert result["data"][CONF_PASSWORD] == "admin"
|
||||
assert result["data"][CONF_PORT] == 0
|
||||
assert result["data"][CONF_SSL] is False
|
||||
|
||||
|
||||
async def test_flow_works(hass, api):
|
||||
"""Test config flow."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
mikrotik_router.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_USER_INPUT
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "Home router"
|
||||
assert result["data"][CONF_NAME] == "Home router"
|
||||
assert result["data"][CONF_HOST] == "0.0.0.0"
|
||||
assert result["data"][CONF_USERNAME] == "username"
|
||||
assert result["data"][CONF_PASSWORD] == "password"
|
||||
assert result["data"][CONF_PORT] == 8278
|
||||
assert result["data"][CONF_SSL] is True
|
||||
|
||||
|
||||
async def test_options(hass):
|
||||
"""Test updating options."""
|
||||
entry = MockConfigEntry(domain=mikrotik_router.DOMAIN, data=DEMO_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "device_tracker"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
mikrotik_router.mikrotik_controller.CONF_SCAN_INTERVAL: 30,
|
||||
mikrotik_router.mikrotik_controller.CONF_UNIT_OF_MEASUREMENT: "Kbps",
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_IFACE_CLIENTS: True,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS: False,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS_TIMEOUT: 180,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
mikrotik_router.mikrotik_controller.CONF_SCAN_INTERVAL: 30,
|
||||
mikrotik_router.mikrotik_controller.CONF_UNIT_OF_MEASUREMENT: "Kbps",
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_IFACE_CLIENTS: True,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS: False,
|
||||
mikrotik_router.mikrotik_controller.CONF_TRACK_HOSTS_TIMEOUT: 180,
|
||||
}
|
||||
|
||||
|
||||
async def test_name_exists(hass, api):
|
||||
"""Test name already configured."""
|
||||
|
||||
entry = MockConfigEntry(domain=mikrotik_router.DOMAIN, data=DEMO_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
user_input = DEMO_USER_INPUT.copy()
|
||||
user_input[CONF_HOST] = "0.0.0.1"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
mikrotik_router.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=user_input
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {"base": "name_exists"}
|
||||
|
||||
|
||||
async def test_connection_error(hass, conn_error):
|
||||
"""Test error when connection is unsuccessful."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
mikrotik_router.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_USER_INPUT
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"host": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_wrong_credentials(hass, auth_error):
|
||||
"""Test error when credentials are wrong."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
mikrotik_router.DOMAIN, context={"source": "user"}
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_USER_INPUT
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"host": "cannot_connect"}
|