惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security Affairs
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
月光博客
月光博客
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
博客园 - Franky
博客园 - 司徒正美
The Cloudflare Blog
V
Visual Studio Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
About on SuperTechFans
爱范儿
爱范儿
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Apple Machine Learning Research
Apple Machine Learning Research
S
Secure Thoughts
B
Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
AWS News Blog
AWS News Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News: Ask HN
Hacker News: Ask HN
T
Tenable Blog
N
News and Events Feed by Topic
Stack Overflow Blog
Stack Overflow Blog
AI
AI
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Cloudbric
Cloudbric
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
T
Threatpost
S
Schneier on Security
K
Kaspersky official blog
O
OpenAI News
TaoSecurity Blog
TaoSecurity Blog
The Register - Security
The Register - Security
PCI Perspectives
PCI Perspectives
L
LINUX DO - 热门话题
Microsoft Security Blog
Microsoft Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
C
Cyber Attacks, Cyber Crime and Cyber Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Palo Alto Networks Blog
Project Zero
Project Zero
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
Schneier on Security
Schneier on Security
腾讯CDC

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Мониторинг сервиса Ivideon-server с помощью Zabbix
Максим · 2026-06-18 · via Все публикации подряд на Хабре

Доброго времени суток, в общем, в компании, в которой я работаю, используется Ivideon-server версии 3.9.0 либо 3.12.0. И появилась огромная потребность в мониторинге камер: их доступности, а также, пишется ли архив с этих камер. Поэтому я начал разработку шаблона Zabbix, который бы опрашивал сервера с Ivideon-server по API. Начал я с того, что с помощью WireShark я разобрал работу Ivideon Client.

Готовые шаблоны закреплю в конце статьи. А в статье постараюсь разобрать как они работают.
Первый запрос, что я смог вытащить и считаю его основным: http://IP-Адрес сервера/streams/info?server=1&sessionId=пароль от сервера, при отправке которого мы получаем JSON ответ:

[{
    "achannels" : 0,
    "acodec" : "none",
    "afreq" : 0,
    "archive" : {
        "edge" : false,
        "internal_speed_play" : false,
        "key_frame_seek" : true,
        "smooth_speed_play" : 4,
        "speeds" : [ 1, 2, 4, 8, 16, 32, 64 ],
        "writable" : true
    },
    "custom_device_info" : {
        "fw_version" : "2.800.0000000.12.R 2021-04-30",
        "hardware_id" : "1.00",
        "ipv4" : "19.44.0.27",
        "model" : "DH-IPC-HDW1230T1P-0280B",
        "name" : "401 ближний кабинет",
        "serial_number" : "",
        "serial_number_format" : "onvif",
        "vendor" : "Dahua"
    },
    "features" : {
        "analytics_info" : { }
    },
    "height" : 960,
    "id" : 1245184,
    "instance_id" : "",
    "is_forced_as_active" : false,
    "is_turned_off" : false,
    "name" : "401 ближний кабинет",
    "online" : true,
    "preview" : {
        "0" : false,
        "1" : false,
        "2" : false
    },
    "record" : {
        "scheduled" : true,
        "type" : "continuous"
    },
    "sources" : {
        "0" : null,
        "1" : "rtsp://admin:viewer121@0.0.0.0/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif",
        "2" : "rtsp://admin:viewer121@0.0.0.0/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif"
    },
    "status" : 1,
    "streams" : {
        "0" : false,
        "1" : true,
        "2" : true
    },
    "vcodec" : "h265",
    "width" : 1280
}]

Здесь уже есть большая часть необходимых нам данных, IP‑Адреса камер в элементе «ipv4» а также их статус «status» где 0 — не работает 1 — работает.

Далее в Zabbix в шаблоне создается элемент данных, который как раз и будет опрашивать предполагаемый узел:

Основной элемент данных в шаблоне Zabbix

Основной элемент данных в шаблоне Zabbix

После создания основного элемента данных создается правило обнаружения, где как раз и происходит парсинг JSON ответа.

Правило обнаружения

Правило обнаружения

Предобработка JSONPath

Предобработка JSONPath

Все полученные данные из JSON записываются в LLD Макросы.

LLD Макросы

LLD Макросы

Наименование макросов лаконичное, поэтому, думаю, не должно возникнуть проблем с пониманием, какой макрос за что отвечает.

Теперь у нас есть IP-адрес, имя и статус камеры, можно переходить в прототипы элементов данных этого правила обнаружения

Прототипы элементов данных

Прототипы элементов данных

Создано 2 прототипа элемента данных,

CAMERA [{#NAME}]: Get record status

  • Получение состояние записи камеры, ведет ли она архивную запись

Ivideon Get DataCAMERA [{#NAME}]: Get Status

  • Получение статуса камеры

Ivideon Get Data: CAMERA [{#NAME}]: Get Status

Ivideon Get Data: CAMERA [{#NAME}]: Get Status

Это зависимый элемент данных, он ссылается на созданный ранее основной элемент данных, где происходит получение JSON ответа.

Предобработка элемента данных

Предобработка элемента данных

Теперь разберем, как шаблон получает состояние записи архива камеры с помощью прототипа элемента данных CAMERA [{#NAME}]: Get record status

CAMERA [{#NAME}]: Get record status

CAMERA [{#NAME}]: Get record status

Этот элемент данных имеет тип скрипт, мною сделан небольшой Java скрипт который отправляет запрос на сервер по камере.

var obj = JSON.parse(value);

var host_ip = obj.hostip;
var camera_id = obj.cameraid;
var ivideon_pass = obj.ivideon_password
var request = new HttpRequest();

var nowUnix = Math.floor(Date.now() / 1000);
var timestamp_offset = nowUnix - 10800

var url = "http://" + host_ip + ":8080/archive/list"
        + "?startTime=" + timestamp_offset
        + "&endTime=" + nowUnix
        + "&server=1&sessionId=" + ivideon_pass
        + "&camera=" + camera_id;

var data = JSON.parse(request.get(url));

if (Array.isArray(data) && data.length === 0) {
    return 0;
} else {
    return 1;
}

В параметрах прототипа данных указывается все те макросы, что мы вытащили ранее.

Теперь когда у нас есть данные можно делать прототипы триггеров.

Прототипы триггеров

Прототипы триггеров

Camera {#NAME} archive not recording — ведется ли архив

Camera {#NAME} is unavailable — статус камеры

Ведется ли архив

Ведется ли архив

Скрипт созданный ранее имеет 2 варианта ответа, 0 — архив не ведется 1 — архив пишется, соответственно, в прототипе триггера для его срабатывания он должен получить 0, тогда триггер сработает.

Статус камеры

Статус камеры

В прототипе Camera {#NAME} is unavailable применяется тот же принцип 0 — не работает 1 — работает.

На этом шаблон готов. Далее создаем узел сети с применением этого шаблона.

Создание узла сети

Создание узла сети

Тип интерфейса SNMP. Также необходимо указать в макросах узла сети следующий макрос: {$IVIDEON_PASSWORD}, в значение записать пароль используемый для подключение к Ivideon-server.

Макрос узла сети

Макрос узла сети

После создания узла сети в элементах данных можно увидеть созданные правилом обнаружения элементы, по каждой камере создается отдельный элемент данных.

Элементы данных узла сети

Элементы данных узла сети

Сразу после появления элементов данных правило обнаружения создает триггеры по каждому элементу данных.

Триггеры

Триггеры

На этом всё, все работы и тестирование проводились с версиями: Zabbix 7.4, ivideon‑server 3.9 и ivideon‑server 3.12.0.

Готовый шаблон для версии Ivideon — server 3.9.0.

zabbix_export:
  version: '7.4'
  template_groups:
    - uuid: d37f71c7e3f7469bab645852a69a2018
      name: 'Templates/Video surveillance'
  templates:
    - uuid: d8d1bbd1d20346f7bb5153d23f024150
      template: 'ivideon-server 3.9 HTTP'
      name: 'ivideon-server 3.9 HTTP'
      groups:
        - name: 'Templates/Video surveillance'
      items:
        - uuid: a8047c121f154cf2a37ff3a50d2cf52c
          name: 'Ivideon Get Data'
          type: HTTP_AGENT
          key: ivideon.attributes
          delay: 3m
          value_type: TEXT
          url: 'http:/{HOST.IP}:8080/streams/info'
          query_fields:
            - name: server
              value: '1'
            - name: sessionId
              value: '{$IVIDEON_PASSWORD}'
          output_format: JSON
          triggers:
            - uuid: 6aebb2b119844e7c8223e90a9afbeba9
              expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
              name: 'Ivideon-server service is unavailable'
              priority: HIGH
      discovery_rules:
        - uuid: 9a759a9eb47049a2bbf8e9effea1456b
          name: 'Ivideon-server discovery cameras'
          type: DEPENDENT
          key: ivideon.cameras
          item_prototypes:
            - uuid: 9a900ef7424f4d00bd3200f7ecb161e3
              name: 'CAMERA [{#NAME}]: Get record status'
              type: SCRIPT
              key: 'ivideon.cameras.record.status[{#NAME}]'
              value_type: TEXT
              params: |
                var obj = JSON.parse(value);
                
                var host_ip = obj.hostip;
                var camera_id = obj.cameraid;
                var ivideon_pass = obj.ivideon_password
                var request = new HttpRequest();
                
                var nowUnix = Math.floor(Date.now() / 1000);
                var timestamp_offset = nowUnix - 10800
                
                var url = "http://" + host_ip + ":8080/archive/list"
                        + "?startTime=" + timestamp_offset
                        + "&endTime=" + nowUnix
                        + "&server=1&sessionId=" + ivideon_pass
                        + "&camera=" + camera_id;
                
                var data = JSON.parse(request.get(url));
                
                if (Array.isArray(data) && data.length === 0) {
                    return 0;
                } else {
                    return 1;
                }
              parameters:
                - name: cameraid
                  value: '{#CAMERA_ID}'
                - name: hostip
                  value: '{HOST.IP}'
                - name: ivideon_password
                  value: '{$IVIDEON_PASSWORD}'
              trigger_prototypes:
                - uuid: 2c4c9a05f32d43f38e6d0a58c6e1d63e
                  expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.record.status[{#NAME}])=0'
                  name: 'Camera {#NAME} archive not recording'
                  priority: WARNING
                  dependencies:
                    - name: 'Camera {#NAME} is unavailable'
                      expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.status[{#NAME}])=0'
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
            - uuid: f76677285bf74103acdc473a92aad686
              name: 'CAMERA [{#NAME}]: Get Status'
              type: DEPENDENT
              key: 'ivideon.cameras.status[{#NAME}]'
              trends: '0'
              preprocessing:
                - type: JSONPATH
                  parameters:
                    - '$.body[?(@.name=="{#NAME}")].status.first()'
              master_item:
                key: ivideon.attributes
              trigger_prototypes:
                - uuid: 4f0bd15b54b2442cba8805f361787d7f
                  expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.status[{#NAME}])=0'
                  name: 'Camera {#NAME} is unavailable'
                  priority: HIGH
                  dependencies:
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
          master_item:
            key: ivideon.attributes
          lld_macro_paths:
            - lld_macro: '{#CAMERA_ID}'
              path: $.id
            - lld_macro: '{#NAME}'
              path: $.name
            - lld_macro: '{#STATUS}'
              path: $.status
          preprocessing:
            - type: JSONPATH
              parameters:
                - '$.body[*]'

Готовый шаблон для версии Ivideon-Server 3.12.0

zabbix_export:
  version: '7.4'
  template_groups:
    - uuid: d37f71c7e3f7469bab645852a69a2018
      name: 'Templates/Video surveillance'
  templates:
    - uuid: 8799d6b58bf3483895babbd76026c758
      template: 'ivideon-server 3.12 HTTP'
      name: 'ivideon-server 3.12 HTTP'
      groups:
        - name: 'Templates/Video surveillance'
      items:
        - uuid: a724aaa44e6e4c0d8c5fbadcb3247fa6
          name: 'Ivideon Get Data'
          type: HTTP_AGENT
          key: ivideon.attributes
          delay: 3m
          value_type: TEXT
          url: 'http:/{HOST.IP}:8080/streams/info'
          query_fields:
            - name: server
              value: '1'
            - name: sessionId
              value: '{$IVIDEON_PASSWORD}'
          output_format: JSON
          triggers:
            - uuid: 13e35f3290e644f09bead7f9caf78ab1
              expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
              name: 'Ivideon-server service is unavailable'
              priority: HIGH
      discovery_rules:
        - uuid: 66bdeec591ac4239806f6c86c0179afd
          name: 'Ivideon-server discovery cameras'
          type: DEPENDENT
          key: ivideon.cameras
          item_prototypes:
            - uuid: 685ba95a1f364e1b958106b4d1eb76a0
              name: 'CAMERA [{#NAME}]: Get record status'
              type: SCRIPT
              key: 'ivideon.cameras.record.status[{#NAME}]'
              value_type: TEXT
              params: |
                var obj = JSON.parse(value);
                
                var host_ip = obj.hostip;
                var camera_id = obj.cameraid;
                var ivideon_pass = obj.ivideon_password
                var request = new HttpRequest();
                
                var nowUnix = Math.floor(Date.now() / 1000);
                var timestamp_offset = nowUnix - 10800
                
                var url = "http://" + host_ip + ":8080/archive/list"
                        + "?startTime=" + timestamp_offset
                        + "&endTime=" + nowUnix
                        + "&server=1&sessionId=" + ivideon_pass
                        + "&camera=" + camera_id;
                
                var data = JSON.parse(request.get(url));
                
                if (Array.isArray(data) && data.length === 0) {
                    return 0;
                } else {
                    return 1;
                }
              parameters:
                - name: cameraid
                  value: '{#CAMERA_ID}'
                - name: hostip
                  value: '{HOST.IP}'
                - name: ivideon_password
                  value: '{$IVIDEON_PASSWORD}'
              trigger_prototypes:
                - uuid: 787126b8f33d4a75b8a038013a4e273e
                  expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.record.status[{#NAME}])=0'
                  name: 'Camera {#NAME} archive not recording'
                  priority: WARNING
                  dependencies:
                    - name: 'Camera {#NAME} is unavailable'
                      expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.status[{#NAME}])=0'
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
            - uuid: 4e5d1fd4cb4c4f73b6cae860743c3cf3
              name: 'CAMERA [{#NAME}]: Get Status'
              type: DEPENDENT
              key: 'ivideon.cameras.status[{#NAME}]'
              trends: '0'
              preprocessing:
                - type: JSONPATH
                  parameters:
                    - '$.body[?(@.custom_device_info.name=="{#NAME}")].status.first()'
              master_item:
                key: ivideon.attributes
              trigger_prototypes:
                - uuid: cf2488d8a103465b983c039ae415415c
                  expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.status[{#NAME}])=0'
                  name: 'Camera {#NAME} is unavailable'
                  priority: HIGH
                  dependencies:
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
          master_item:
            key: ivideon.attributes
          lld_macro_paths:
            - lld_macro: '{#CAMERA_ID}'
              path: $.id
            - lld_macro: '{#NAME}'
              path: $.custom_device_info.name
            - lld_macro: '{#STATUS}'
              path: $.custom_device_info.status
          preprocessing:
            - type: JSONPATH
              parameters:
                - '$.body[*]'

Чтобы использовать шаблон, вставьте содержимое в текстовый файл и сохраните с расширением yaml, после импортируйте в Zabbix.