Skip to content

Commit

Permalink
Merge pull request #6 from PinoutLTD/rrs-0.1
Browse files Browse the repository at this point in the history
Robonomics Report Service v 0.1
  • Loading branch information
LoSk-p authored Jun 24, 2024
2 parents 6f4164f + 210cd86 commit 66de076
Show file tree
Hide file tree
Showing 19 changed files with 567 additions and 704 deletions.
132 changes: 19 additions & 113 deletions custom_components/robonomics_report_service/__init__.py
Original file line number Diff line number Diff line change
@@ -1,128 +1,36 @@
import asyncio
import logging
from datetime import timedelta

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import ServiceCall, HomeAssistant
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.event import async_track_time_interval

from .const import (
CONF_CONTROLLER_SEED,
CONF_EMAIL,
CONF_OWNER_ADDRESS,
CONF_SENDER_SEED,
DOMAIN,
ROOT_LOGGER,
LOGGER_HANDLER,
PROBLEM_REPORT_SERVICE,
CONF_OWNER_SEED,
STORAGE_PINATA_CREDS,
CONF_PINATA_PUBLIC,
CONF_PINATA_SECRET,
ROBONOMICS,
RWS_CHECK_UNSUB,
CHECK_ENTITIES_TRACK_TIME_UNSUB,
HANDLE_CHECK_ENTITIES_TIME_CHANGE,
CHECK_ENTITIES_TIMEOUT,
ERROR_SOURCES_MANAGER,
CONF_EMAIL
)
from .frontend import async_register_frontend, async_remove_frontend
# from .frontend import async_register_frontend, async_remove_frontend
from .rws_registration import RWSRegistrationManager
from .robonomics import Robonomics
from .utils import (
create_notification,
async_load_from_store,
async_remove_store,
ReportServiceStatus,
set_service_status,
create_link_for_notification,
)
from .service import send_problem_report
from .libp2p import get_pinata_creds
from .websocket import async_register_websocket_commands
from .ipfs import pinata_creds_exists
from .entities_check import EntitiesStatusChecker
from .logger_handler import LoggerHandler
from .message_formatter import MessageFormatter
from .error_sources.error_source_manager import ErrorSourcesManager
from .report_service import ReportService

_LOGGER = logging.getLogger(__name__)


async def wait_for_pinata_creds(hass: HomeAssistant, entry: ConfigEntry):
if not await pinata_creds_exists(hass):
_LOGGER.debug("Pinata credentials are not in storage")
while not await get_pinata_creds(
hass,
entry.data[CONF_CONTROLLER_SEED],
entry.data[CONF_EMAIL],
entry.data[CONF_OWNER_ADDRESS],
):
await asyncio.sleep(0.5)
else:
_LOGGER.debug("Pinata credentials are in storage")
await hass.data[DOMAIN][ROBONOMICS].async_init()


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})
set_service_status(hass, ReportServiceStatus.WaitPinataCreds)
hass.data[DOMAIN][ROBONOMICS] = Robonomics(
hass,
entry.data[CONF_CONTROLLER_SEED],
entry.data[CONF_OWNER_ADDRESS],
entry.data.get(CONF_OWNER_SEED),
)
LoggerHandler().setup(hass)
async_register_websocket_commands(hass)
async_register_frontend(hass)

async def check_rws_active(data=None):
if ROBONOMICS in hass.data[DOMAIN]:
if not await hass.data[DOMAIN][
ROBONOMICS
].get_rws_left_days() or not await pinata_creds_exists(hass):
# Delete pinata creds
await async_remove_store(hass, STORAGE_PINATA_CREDS)
entry.async_create_task(hass, wait_for_pinata_creds(hass, entry))

async def handle_problem_report(call: ServiceCall) -> None:
storage_data = await async_load_from_store(hass, STORAGE_PINATA_CREDS)
if (
CONF_PINATA_PUBLIC in storage_data
and CONF_PINATA_SECRET in storage_data
and ROBONOMICS in hass.data[DOMAIN]
):
await send_problem_report(
hass, call, hass.data[DOMAIN][ROBONOMICS], storage_data
)

await check_rws_active()
hass.services.async_register(DOMAIN, PROBLEM_REPORT_SERVICE, handle_problem_report)
# entry.async_create_task(hass, wait_for_pinata_creds(hass, entry))
hass.data[DOMAIN][RWS_CHECK_UNSUB] = async_track_time_interval(
hass,
check_rws_active,
timedelta(days=1),
)
async def check_states(_ = None):
await asyncio.sleep(15)
entities_checker = EntitiesStatusChecker(hass)
unavailables = entities_checker.get_unavailables()
not_updated = await entities_checker.get_not_updated()
unavailables_text = MessageFormatter.format_devices_list(unavailables, "Found some unavailable devices:")
not_updated_text = MessageFormatter.format_devices_list(not_updated, "Found some not updated for a long time devices:")
problem_text = MessageFormatter.concatinate_messages(unavailables_text, not_updated_text)
link = create_link_for_notification(problem_text)
service_data = {
"message": f"Found some unavaileble or not updated for a long time devices. [Click]({link})",
"title": "Send Report Service",
}
await create_notification(hass, service_data)
hass.data[DOMAIN][HANDLE_CHECK_ENTITIES_TIME_CHANGE] = check_states
asyncio.ensure_future(check_states())
hass.data[DOMAIN][CHECK_ENTITIES_TRACK_TIME_UNSUB] = async_track_time_interval(
hass.data[DOMAIN][CONF_EMAIL] = entry.data[CONF_EMAIL]
robonomics = Robonomics(
hass,
hass.data[DOMAIN][HANDLE_CHECK_ENTITIES_TIME_CHANGE],
timedelta(seconds=CHECK_ENTITIES_TIMEOUT),
entry.data[CONF_SENDER_SEED],
)
# async_register_frontend(hass)
await RWSRegistrationManager(hass, robonomics, entry.data[CONF_EMAIL]).register()
ReportService(hass, robonomics).register()
error_sources_manager = ErrorSourcesManager(hass)
error_sources_manager.setup_sources()
hass.data[DOMAIN][ERROR_SOURCES_MANAGER] = error_sources_manager

return True

Expand All @@ -140,8 +48,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
:return: True if all unload event were success
"""
hass.data[DOMAIN][ROOT_LOGGER].removeHandler(hass.data[DOMAIN][LOGGER_HANDLER])
hass.data[DOMAIN][CHECK_ENTITIES_TRACK_TIME_UNSUB]()
async_remove_frontend(hass)
hass.data[DOMAIN][RWS_CHECK_UNSUB]()
hass.data[DOMAIN][ERROR_SOURCES_MANAGER].remove_sources()
# async_remove_frontend(hass)
return True
61 changes: 19 additions & 42 deletions custom_components/robonomics_report_service/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,17 @@
from .const import (
DOMAIN,
CONF_EMAIL,
CONF_OWNER_SEED,
STORAGE_ACCOUNT_SEED,
CONF_OWNER_ADDRESS,
CONF_CONTROLLER_SEED,
CONF_SENDER_SEED,
CONF_PHONE_NUMBER,
)
from .utils import (
async_load_from_store,
async_save_to_store,
get_robonomics_accounts_if_exists,
)
from .robonomics import create_account, get_address_for_seed
from .robonomics import Robonomics

_LOGGER = logging.getLogger(__name__)

STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_EMAIL): str,
vol.Optional(CONF_PHONE_NUMBER): str,
}
)

Expand All @@ -35,7 +29,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1

def __init__(self):
self.paid = False
self.seed_saved = False

async def async_step_user(self, user_input: tp.Optional[dict] = None) -> FlowResult:
"""Handle the initial step of the configuration. Contains user's warnings.
Expand All @@ -50,37 +44,20 @@ async def async_step_user(self, user_input: tp.Optional[dict] = None) -> FlowRes
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
)
self.user_data = user_input
await self._get_or_create_accounts()
return self.async_create_entry(
title="Robonomics Report Service", data=self.user_data
)
sender_seed = Robonomics.generate_seed()
self.user_data[CONF_SENDER_SEED] = sender_seed
return await self.async_step_seed()

async def _get_or_create_accounts(self):
storage_data = await async_load_from_store(self.hass, STORAGE_ACCOUNT_SEED)
# If Robonomics integration was configured, there will be controller seed and owner address in the storage
if (
CONF_CONTROLLER_SEED in storage_data
and CONF_OWNER_ADDRESS in storage_data
):
controller_seed = storage_data[CONF_CONTROLLER_SEED]
owner_address = storage_data[CONF_OWNER_ADDRESS]
owner_seed = storage_data.get(CONF_OWNER_SEED)
async def async_step_seed(self, user_input: dict[str, tp.Any] | None = None):
if not self.seed_saved:
self.seed_saved = True
return self.async_show_form(
step_id="seed",
data_schema=vol.Schema({}),
description_placeholders={"seed": self.user_data[CONF_SENDER_SEED]},
)
else:
robonomics_data = await get_robonomics_accounts_if_exists(self.hass)
if robonomics_data is not None:
controller_seed = robonomics_data[CONF_CONTROLLER_SEED]
owner_address = robonomics_data[CONF_OWNER_ADDRESS]
owner_seed = None
else:
controller_seed, _ = create_account()
owner_seed, owner_account = create_account()
owner_address = owner_account.get_address()
storage_data[CONF_CONTROLLER_SEED] = controller_seed
storage_data[CONF_OWNER_ADDRESS] = owner_address
storage_data[CONF_OWNER_SEED] = owner_seed
await async_save_to_store(self.hass, STORAGE_ACCOUNT_SEED, storage_data)
self.user_data[CONF_CONTROLLER_SEED] = controller_seed
self.user_data[CONF_OWNER_ADDRESS] = owner_address
self.user_data[CONF_OWNER_SEED] = owner_seed
_LOGGER.debug(f"Finished configuration. Owner address: {owner_address}, controller_address: {get_address_for_seed(controller_seed)}")
return self.async_create_entry(
title="Robonomics Report Service", data=self.user_data
)

20 changes: 6 additions & 14 deletions custom_components/robonomics_report_service/const.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
DOMAIN = "robonomics_report_service"
ADDRESS = "address"

STORAGE_ACCOUNT_SEED = "account_seed"
STORAGE_PINATA_CREDS = "pinata_creds"

CONF_EMAIL = "email"
CONF_OWNER_SEED = "owner_seed"
CONF_CONTROLLER_SEED = "controller_seed"
CONF_OWNER_ADDRESS = "owner_address"
CONF_PINATA_SECRET = "pinata_secret"
CONF_PINATA_PUBLIC = "pinata_public"
CONF_SENDER_SEED = "sender_seed"
CONF_PHONE_NUMBER = "phone_number"

ROBONOMICS_WSS = [
"wss://kusama.rpc.robonomics.network/",
Expand All @@ -23,11 +22,6 @@
IPFS_PROBLEM_REPORT_FOLDER = "ha_problem_report"
LOGS_MAX_LEN = 9*1024*1024

ROOT_LOGGER = "root_logger"
LOGGER_HANDLER = "logger_handler"

WEBSOCKET = "websocket"
LIBP2P_UNSUB = "libp2p"
LIBP2P_WS_SERVER = "ws://127.0.0.1:8888"
LIBP2P_LISTEN_PROTOCOL = "/pinataCreds"
LIBP2P_SEND_PROTOCOL = "/initialization"
Expand All @@ -36,10 +30,8 @@

FRONTEND_URL_PUBLIC = "report-service"
FRONTEND_URL = "/rrs/frontend"
ROBONOMICS = "robonomics"
SERVICE_STATUS = "service_status"

RWS_CHECK_UNSUB = "rws_check_unsub"
CHECK_ENTITIES_TRACK_TIME_UNSUB = "check_entities_track_time_unsub"
HANDLE_CHECK_ENTITIES_TIME_CHANGE = "handle_check_entities_time_change"
CHECK_ENTITIES_TIMEOUT = 60 * 60 # Seconds
CHECK_ENTITIES_TIMEOUT = 24 # Hours

OWNER_ADDRESS = PROBLEM_SERVICE_ROBONOMICS_ADDRESS
ERROR_SOURCES_MANAGER = "error_sources_manages"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import typing as tp

from homeassistant.core import HomeAssistant, callback

from .sources.entities_checker import EntitiesStatusChecker
from .sources.error_source import ErrorSource
from .sources.logger_handler import LoggerHandler

class ErrorSourcesManager:
def __init__(self, hass: HomeAssistant):
self.error_sources: tp.List[ErrorSource] = [EntitiesStatusChecker(hass), LoggerHandler(hass)]

@callback
def setup_sources(self) -> None:
for source in self.error_sources:
source.setup()

@callback
def remove_sources(self) -> None:
for source in self.error_sources:
source.remove()
Loading

0 comments on commit 66de076

Please sign in to comment.