Перейти до основного вмісту

Сигнал Повітряної Тривоги в Україні

Про проєкт

Додайте пристрій до вашого контролера, який представляє сигнал повітряної тривоги в обраних регіонах.

порада

Щоб використовувати цей плагін, вам потрібно отримати API-токен з UkraineAlarm

Встановлення

Після виконання інструкцій зі встановлення, вам потрібно встановити змінну API_TOKEN на ваш API-ключ.

Наприклад, якщо ваш ключ 12345678:123456789012345678, ви повинні замінити рядок

local API_TOKEN = ""

на рядок:

local API_TOKEN = "12345678:123456789012345678"

Вихідний код

Вихідний код плагіна
local os = require 'os'
local JSON = require 'json'
local http = require('http')
local https = require('https')
local timer = require('timer')

local plugin = {}
local API_VERSION = 2
local API_TOKEN = ""

plugin.name = "Повітряна тривога"
plugin.version = {1, 2, 0}

local ukrainealarm = {}
ukrainealarm.host = "https://api.ukrainealarm.com"
ukrainealarm.token = API_TOKEN
ukrainealarm.version = 3
ukrainealarm.api_url = ukrainealarm.host .. "/api/v" .. ukrainealarm.version
ukrainealarm.api_regions = ukrainealarm.api_url .. "/regions"
ukrainealarm.api_alerts = ukrainealarm.api_url .. "/alerts"

-- poll timer for every device
local timers = {}

ukrainealarm.get_regions = function(cb)
print("ukrainealarm.get_regions")
local options = http.parseUrl(ukrainealarm.api_regions)
options.headers = {{"Authorization", API_TOKEN}}
options.method = 'GET'
local req = https.get(options, function(res)
local regions = ""
res:on('data', function(chunk)
regions = regions .. chunk
end)
res:on("end", function()
if cb and type(cb) == "function" then
cb(regions)
end
end)
end)
end

ukrainealarm.get_alerts = function(region, cb)
print("ukrainealarm.get_alerts")
local options = http.parseUrl(ukrainealarm.api_alerts .. "/" .. region)
options.headers = {{"Authorization", API_TOKEN}}
options.method = 'GET'

local req = https.get(options, function(res)
local data = ""
res:on('data', function(chunk)
data = data .. chunk
end)
res:on("end", function()
if cb and type(cb) == "function" then
cb(true, data)
end
end)
end)
req:on("error", function(err)
print("ukrainealarm.get_alerts error", err)
if cb and type(cb) == "function" then
cb(false, err)
end
end)
req:setTimeout(25000)
end

-- new interfaces provided by the plugin
plugin.provided_interfaces = {
AirRaidAlert = {
actions = {
pollDevice = {
description = "Refresh all device data",
arguments = {},
handler = function(path)
print("sample plugin pollDevice", path)
end
}
},
parameters = {
identity = {
type = "string",
value = "0",
role = "edit",
locale = {
en = "Region code",
ru = "Код региона",
ua = "Код регіону"
}
},
version = {
type = "integer",
value = "1"
},
manufacturer = {
type = "string",
value = "ConnectHome",
role = "show",
locale = {
en = "Manufacturer",
ru = "Поставщик",
ua = "Постачальник"
}
},
polling_time = {
type = "integer",
value = 10,
role = "edit",
locale = {
en = "Polling time, s",
ru = "Время опроса, с",
ua = "Час опитування, с"
}
}
}
}
}
-- controller present interfaces overridden by the plugin
plugin.overridden_interfaces = {}

-- complete device information profiles
plugin.devices = {
AirRaidAlertSensor = {
type = "DevBinarySensor",
role = "BinaryGenericSensor",
icon = "ch-security",
interfaces = {"AirRaidAlert", "SensorBinary"},
parameters = {
polling_time = 10,
Armed = false,
Tripped = false
}
}
}

-- helper function
local function get_params_name_value(device, identity)
local params = {}
for k, v in pairs(device.parameters) do
params[k] = v.value
end
if identity then
params.identity = identity
end
return params
end

----------------- API HANDLERS --------------------------------
-- create device API handler
function plugin:device_create(topic, payload)
-- todo hide inside api?
print("plugin device_create")
local device_id = tonumber(self:get_topic_segment(topic, 5)) -- should be 1
local message_id = payload.message_id
local name = payload.body.params.name
local type = payload.body.params.type or ""
local identity = payload.body.params.identity
local room_id = payload.body.params.room_id or 0
local params = payload.body.params.params or {}
if not message_id or not name or not identity or not type or not room_id then
self:reply(topic, message_id, 400, false, {
message = "missing requred parameters"
})
return
end
local new_device = plugin.devices and plugin.devices[type]
if not new_device then
print("device_create: device type not found", type)
self:reply(topic, message_id, 404, false, {
message = "device type not found"
})
return
end
-- calling a controller to create the device
self:pub{
topic = "internal/plugins_hub/" .. self.uuid .. "/devices/create",
payload = JSON.stringify({
version = tostring(API_VERSION),
method = "POST",
message_id = self.mqtt:pending_message_add(new_device),
token = self.token,
body = {
plugin_type = type,
name = name,
type = new_device.type,
role = new_device.role,
alive = true,
hidden = false,
room_id = room_id,
interfaces = new_device.interfaces,
icon = new_device.icon,
params = get_params_name_value(new_device, identity)
}
}),
qos = 2
}
-- reply to the caller
self:reply(topic, message_id, 200, true)
end

-- discover devices API handler
function plugin:discover_devices(topic, payload)
print("plugin discover_devices")
-- Query all regions
ukrainealarm.get_regions(function(regions)
local json = JSON.parse(regions)
local short_regions = {}
-- Loop through each state
if json then
for i, state in ipairs(json.states) do
table.insert(short_regions, {
identity = state.regionId,
info = state.regionName,
type = "AirRaidAlertSensor",
icon = "ch-siren_on"
})
end
end
self:reply(topic, payload.message_id, 200, true, {
devices = short_regions
})
end)
end

-- convert UTC to local timestamp
local function convert_datetime(utc)
if not utc then
return nil
end
local pattern = "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z"
local year, month, day, hour, minute, second = utc:match(pattern)
local timestamp = os.time({
year = year,
month = month,
day = day,
hour = hour,
min = minute,
sec = second
})

local timezone = os.date('%z') -- "+0300"
local signum, hours, minutes = timezone:match '([+-])(%d%d)(%d%d)'

local dt = (tonumber(signum .. hours) * 3600 + tonumber(signum .. minutes) * 60)

return timestamp + dt
end

-- poll device
function plugin:poll_device(device_id)
-- get identity param
local region = self:db_get_param(device_id, "identity")
print("plugin poll_device", device_id, region)
if not region then
print("plugin poll_device: identity not found, terminating")
return
end
ukrainealarm.get_alerts(region, function(result, alerts)
p("alerts/" .. region, result, alerts)
if result then
local json = JSON.parse(alerts)
if json then
local active = json and json[1] and json[1].activeAlerts or nil
local tripped = active ~= nil and #active > 0
self:update_param_boolean(device_id, "Tripped", tripped)
if tripped then
local alert = active[1]
local timestamp = convert_datetime(alert.lastUpdate)
self:update_param_integer(device_id, "lasttrip", timestamp)
end
end
end
local time = self:db_get_param(device_id, "polling_time")
if type(time) ~= "number" then
time = 10
end
-- restart timer
if timers[device_id] then
timer.clearTimer(timers[device_id])
end
timers[device_id] = timer.setTimeout(time * 1000, function()
self:poll_device(device_id)
end)
end)
end

-- callback
function plugin:init(reason)
print("AirRaidAlert init")
end

-- callback
function plugin:device_created(topic, payload)
print("plugin device_created")
p(payload)
if payload.body.success ~= true then
print("plugin device_created error:", payload.status)
return
end
-- add device
self:db_add_device(payload.body.device_id, payload.body.plugin_type)
-- add params
self:db_add_default_params(payload.body.device_id, payload.body.interfaces, payload.body.params)
-- start poller
self:poll_device(payload.body.device_id)
end

-- callback
function plugin:device_registered(device_id, name, params)
print("device_registered")
self:notify("Registered device " .. device_id)
self:poll_device(device_id)
end

-- callback
function plugin:device_removed(topic, payload)
print("plugin device_removed")
p(payload)
if payload.body.success ~= true then
print("plugin device_removed error:", payload.status)
return
end
local device_id = payload.body.device_id
-- removing items from db
self:db_delete_params(device_id)
self:db_remove_device(device_id)
end

return plugin