How to pair Rostelecom Key with Home Assistant

Recently I came across a key for a Rostelecom intercom with an unusual rectangular shape. And soon after that, the Rostelecom Key application appeared on my phone, which allowed me to completely stop using the physical key. In this application, you can view video from various cameras around the house and in the entrance, create temporary codes for opening doors, and also open all doors remotely.

I immediately wanted to add all these functions to Home Assistant, which already controls a bunch of devices inside my apartment. Well, integration with Rostelecom services will allow you to expand the radius of control of Home Asstatant and, if desired, implement new interesting automations, for example, opening the entrance door by simply looking carefully into the intercom camera, and at that moment warn the children in the apartment that dad is in 5 will be home in a few minutes =)

Studying the Rostelecom Key application

Website offers to download the Android and iOS versions of the Rostelecom Key application, but the inconspicuous “Login” button allows you to use its web version.

We log in using the phone linked to the Rostelecom Key account, then go to https://key.rt.ru/main/pwa/dashboard and analyze the network requests that this application sends.

First to leave request to get a list of cameras. It contains an Authorization Header containing a Bearer Token. We decode the token using https://jwt.io/ and we see that the token’s lifespan is one year. This means that we can safely save this token and use it for subsequent requests to the API.

The response to the request contains a complete list of available cameras that have the following interesting attribute:

"screenshot_precise_url_template": "https://media‑vdk4.camera.rt.ru/image/precise/{size}/8f3a52fc‑aa67–48f6-aeaa‑fb1379e2c8f5/{timestamp}.jpg?token={cdn_token}"

Obviously, we can already get a screenshot from each camera, but how to get a video stream is still unclear.

Next, click on view video from the camera and see this request: wss://live‑vdk4.camera.rt.ru/stream/8f3a52fc‑aa67–48f6-aeaa‑fb1379e2c8f5/1 706 625 123.mp4?mp4-fragment‑length=0.5&mp4-use‑speed=0&mp4-afiller= 1

The video stream comes in portions via a websocket, which is not very convenient, and it is not yet clear how video in this format can be added to Home Assistant. I tried to look for information about standard protocols for transmitting video data over websockets, but did not find any mention of such data structures:

It seems that Rostelecom is using some kind of its own implementation.

And then an idea comes to mind – what if we change wss to https? We try, and this link works. In a new browser tab we see the video from our camera in real time!

And finally, we try to open some door from the application, and we see a request https://household.key.rt.ru/api/v2/app/devices/23616/open with the same Bearer Token. We try to repeat this request using the curl utility, and the door opens successfully!

This completes the research part – we learned how to receive video streams, screenshots and open doors. Now all that remains is to add the appropriate entities to Home Assistant.

Writing an integration for Home Assistant

Those who care about the result can immediately look at the resulting integration is here. Next, I will briefly describe the basic steps for creating an integration.

Creating integrations is well described in documentation Home Assistant. It is also convenient to use the sources of default integrations and their official ones as a source of inspiration. examples.

Let’s start creating the integration with the manifest.json file:

{
  "domain": "rtkey",
  "name": "rtkey",
  "documentation": "https://github.com/artgl/hass_rtkey",
  "iot_class": "cloud_polling",
  "version": "0.0.1",
  "requirements": ["transliterate", "pyjwt"],
  "config_flow": true
}

Pay attention to the following attributes:

iot_class: "cloud_polling" tells Home Assistant that our integration requires an active Internet connection, and entity updates may be delayed depending on the polling rate.

requirements — additional python packages that our integration uses.

config_flow: true — our integration is configured not using a configuration file, but using a configuration wizard that is launched from the Home Assistant settings.

Add the file config_flow.py. It contains a class with a single function, async_step_user, which is called when the user clicks the “Add Integration” button in the Home Assistant settings. You can read more about config_flow here.

Add the init.py file. At the very beginning, we will define a list of entity classes that our integration will create in the variable PLATFORMS. In the current version, only entities of the Image type are created for screenshots. Future versions will add Camera entities for video streams and Button entities for the “open door” action. You can read more about entity classes Here. For each platform you will need to add the appropriate file, in our case it will be the image.py file

Next in this file we will create two functions: async_setup_entry And async_unload_entry. The first is responsible for creating an instance of our integration when Home Assistant starts, the second is called when the user decides to delete our integration. Important point: some integrations only work in a single instance and add this check to this function. But in our case, the user can have access to several Rostelecom keys, and we will allow the user to have several instances of our integration.

async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
  await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
  return True

In our case, all initialization will happen inside the image.py file when the corresponding platform is initialized.

Let’s move on to the image.py file. A function is also needed here async_setup_entryin which we get a list of available cameras using the Rostelecom API calls from the first part and create the corresponding Home Assistant entities:

async def async_setup_entry(hass, config_entry, async_add_entities):
    cameras_api = RTKeyCamerasApi(hass, config_entry)

    cameras_info = await cameras_api.get_cameras_info()

    entities = []
    for camera_info in cameras_info["data"]["items"]:
        camera_id = camera_info["id"]
        entities.append(RTKeyCameraImageEntity(hass, config_entry, cameras_api, camera_id, camera_info))
    async_add_entities(entities)

Class RTKeyCameraImageEntity is a descendant of the Image class and in a minimal implementation should have only one function that returns an image in binary form:

    class RTKeyCameraImageEntity(ImageEntity):
    async def async_image(self) -> bytes | None:
        return self.cameras_api.get_camera_image(self.camera_id)

My github implementation contains a little more code that is responsible for caching and automatically updating the image if the user is currently looking at it.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *