Okos Polip ~ My Device-to-Cloud Service



Okos Polip is the device-to-cloud service I built for my own hacked devices. You might be wondering why that odd name? Well okos is smart in Hungarian and polip is octopus. A smart octopus with a tentacle on every device in the house. I already had Táltos-oid, so I might as well name another thing in Hungarian. At least I am not competing in the English start-up name space (where my name is bound to collide with some B2B vaporware). My KOIOS air filter talks to it. So does the grow light hack. This is also the post where I admit I gave up on it (what a surprise).

Why Build One

In the 2022 home automation post I wanted two things. To hack various manual devices in the apartment so they could be controlled remotely. I also wanted to collect data from my plants so something could decide when to water and when to turn on the grow light. The first hack, the air filter, used fauxmoESP to pretend to be a Philips Hue bulb so Alexa could switch it. That works for on and off. It doesn’t work for “set the grow light to channel A at intensity two for six hours,” and it has nowhere to put a soil moisture reading.

What I wanted was a small, boring service of my own. A device checks in, says what it is, gets told what state it should be, reports what state it’s actually in, and can hand back sensor readings. Plus a dashboard on the other side to see and poke all of that. Oh and an API on top so Alexa or Google Home could eventually drive it.

I also wanted to learn how to build and deploy a real backend. Docker, reverse proxies, certificates, a VPS, the whole nine-yards. The robot and firmware side I know well enough but this web services side I had only done at course-project scale, and never alone.

A block diagram: Dashboard and Fake Device on the left feed a User Backend and a Schema; Schema connects to a Database and down to Device Ingest, which connects to Phy. Device below.
The architecture sketch.

The Device Side

The device side is a client library, polip-client for Arduino, with JavaScript and C ports for things that aren’t Arduinos. A device is identified by a serial like grow-light-0-0000 and a revocable key. Every request carries the serial, the hardware and firmware versions, a timestamp, a counter, and a tag.

The tag is an HMAC-SHA256 over the request using the revocable key, so the server can check the message came from a device holding that key and the device can check the reply came from the server. The counter goes up by one per request and rolls over at 2^32. If a request arrives with a stale counter the server rejects it, which is a cheap way to shut out replay attacks. Both checks can be switched off for debugging (which I had to add after since things never work the first try).

There are five kinds of message a device can send.

  • Poll. Ask the server for the state I should be in, plus, optionally, my metadata, my sensor definitions, and any pending RPCs.
  • Push state. Tell the server my state changed, usually because a human pressed a button.
  • Push sensors. Send up readings, telemetry, and what not.
  • Error. Log an error with a code the server knows the meaning of (or user codes).
  • RPC. Update the status of a remote procedure call I picked up on a poll.

The library wraps those in a workflow with hooks. Firmware clients fill in a hook to serialize its state, a hook to deserialize the server’s state, and an error hook, then calls one periodic update function from the main loop. Polling runs on a soft timer, pushes happen when the firmware flags a change. Check out the air filter post for a worked example.

RPCs were an afterthought. State handles values. Whereas an RPC like a timer is an action that starts, runs, and finishes. Both server and client need to keep track of the RPC phases asynchronously. An RPC is created on the server, picked up by the device on its next poll, acknowledged, ran, and then reported as succeeded, rejected, or cancelled, with a notification pushed at the end. Here is a log as an example. The binary switch test device picks up a one-hour timer.

Endpoint: /api/v1/device/poll?state=true&manufacturer=false&rpc=true
RX = { ..., "state":{"power":true,"timer":{...}},
       "rpc":[{"uuid":"c63c33bd-...","type":"timer","parameters":{"duration":1},"status":"pending"}] }
Started Timer RPC
Endpoint: /api/v1/device/rpc
TX = { "rpc":{"uuid":"c63c33bd-...","result":null,"status":"acknowledged"}, ... }

The Cloud Side

The whole thing ran on one small Vultr VPS running Linux. Every service is a Docker container, each in its own repo with its own Dockerfile, and a service_launch repo holds the compose files and the scripts to build, ship, and start them. There is no container registry. Images get built on my desktop, saved to a tarball, copied over via SFTP, and loaded on the box. Crude, but it worked.

The services:

  • api-reverse-proxy and dashboard-proxy. Two nginx containers. They terminate TLS with a Let’s Encrypt certificate from a certbot container, so everything behind them is plain HTTP.
  • device-v1-ingest-server. The device-to-cloud bridge. Every device request goes through here, gets its tag and counter checked, gets validated against the device’s schema, and updates the device record.
  • model-schema-server. Holds the schemas for every device type, state, sensor, RPC, and notification code, cached in front of MongoDB. Nothing gets written to the database that does not match a schema.
  • routes-schema-server. The same idea for the API routes themselves, served as static files.
  • auth-server. Login through Google or Amazon OAuth, sessions in Redis, and a test-user mode so I did not have to touch the real providers during development.
  • user-server. Accounts, permissions, notification preferences, API keys.
  • factory-server. Defining device types and provisioning devices, which is where a new serial and revocable key come from.
  • state-api. The external API for reading and setting device state. This is the piece an Alexa skill would talk to.
  • MongoDB and Redis underneath.

The nginx configs were mostly written by ChatGPT. This was early 2023 and my first toe in the water of having an AI write code for me.

The backend architecture diagram: devices with the client library and the fake device behind an API reverse proxy, the device ingest server, device database, state API server, and event queue across the top, the schema server and factory API server in the middle, the dashboard reverse proxy, user server, auth server, session cache, user database, and CRM below, with the user's cloud, payment processor, and OAuth provider on the right.
The backend, as drawn for a pitch deck. The event queue, the LLM box, the CRM, and the payment processor never got built.

The Dashboard and the Fake Device

Two React apps sit on top of the services.

The user dashboard is the account side. Log in, manage your settings and your API keys. The device view was stubbed for later (I never put much work into this dashboard; probably should have).

The second one is the fake device, which is a test tool. It is a web page that behaves like a physical device, speaking the same protocol through the JavaScript client library, so I could exercise the whole backend from a browser without flashing a board. It has a table of fake sensors with a random-values toggle and a state panel you can push from, and it can present as any device type the server knows about.

The interesting design in there is the semantic type system. Every state and sensor field in a device schema carries a type like okos-power-btn, okos-range-entry, okos-enum-select, or okos-polip-color, with modifiers like min, max, step, and read-only. The dashboard renders each field from its type instead of from a hand-written form per device. Add a device type on the server and the UI already knows how to draw it. That was the part that (I think) would have made Okos Polip a platform rather than a project.

I did not write all of the front end myself. In 2023 I hired a contractor on Upwork for the fake device refactor and the first dashboard pages, and wrote up a milestone document for each piece (as you could guess there was a lot of code being written, more than one person with limited attention span and even more limited hobby hours could muster). I write specs for other people at work, but this was the first time on a hobby project. It turned out to be good practice. These days I tell Claude what to build, and that is most of the job.

The Okos Polip user dashboard home page: a magenta header with a Login button, a Home heading, and a two-column footer with a site map and placeholder contact information.
The dashboard home page with the contractor's footer.

The Devices

The device list the fake device was told to expect:

SerialTypeStatus
air-purifier-0-0000air-purifier-0Built. The KOIOS air filter
grow-light-0-0000grow-light-0Built. The grow light
binary-switch-0-0000binary-switch-0Built as the reference firmware. An on/off outlet with an RPC timer
soil-sensor-0-0000soil-sensor-0Prototype. The plant data logger
robot-yam-0-0000robot-yam-0Planned. YAM was going to report in
fake-0-0000fake-0The browser fake device

Will It Okos?

Somewhere along the way this stopped being a home automation project and turned into a business plan. I made a pitch deck, sixty slides, with all the usual sections. Problem, solution, competition, pricing, financial projections, team, and roles.

The general architecture diagram: a user talks to a landing page on WordPress, the Okos dashboard React app, and the Okos fake device React app; a user device talks through the device client library in C, Python, or JavaScript; all of them meet the Okos backend, which connects out to a CRM, an OAuth provider, a payment processor, and an LLM.
The general architecture, one box for the backend and a ring of things it was supposed to talk to.

The pitch is that makers want to put a hacked or novel device on a cloud without signing up for one of the big platforms and their lock-in. Okos Polip would be a single-purpose device-to-cloud service where the client side is only a library, so the hardware, the OS, and the firmware stay your business. The one value from the deck I still believe is “vendor lock-ins are an anti-pattern.”

For the competition I sorted the field three ways. Big IoT like AWS IoT and MicroEJ. Maker platforms like Particle, Arduino Cloud, Blynk, and Golioth. Self-hosted like ESPHome, Home Assistant, and Tasmota. My own conclusion on the market slide was that Okos should be a lifestyle startup, not a unicorn, with a small set of dedicated fan-customers rather than a large audience.

The financial projections slide, in full: “Admittedly not great :(” The target customers all have free tiers elsewhere, and we cannot and should not compete on price.

The team slide has two bullets. Curt Henrichs, background in embedded systems, robotics, and HCI. Then ”???”.

The factory service was supposed to use GPT to infer a device schema from a description. A maker says what their device does and gets the state, sensor, and RPC definitions back. The slide calls this the most interesting part of the project, and I agree. It never got built, of course.

There were more devices planned.

  • A battery-powered environment sensor with soil moisture, temperature, humidity, and light, to pair with the grow light.
  • A wireless front panel for the grow light with an e-ink display and three capacitive touch buttons.
  • YAM through an Okos-ROS library, to get Okos used in the robotics space.

I also created a roles slide which lists (at the time) everything I was doing / going to do as a desperate plea to myself to find outside help.

  • An embedded engineer
  • A full-stack engineer
  • A devops engineer
  • A social media manager
  • An operations manager
  • A UX researcher

All of that for a team of one. Nobody was brought on. I tried to recruit my brother. He politely declined, which was wise. If I ever bring this back it will be with Claude’s help.

The Brand

The Okos Polip brand sheet: the neon octopus mark and wordmark on navy, the mark alone on white, and a business card mockup.
The brand sheet.

The logo is a neon line-art octopus, drawn as a vector, with a cyan to magenta gradient on navy. I was inspired by a retrowave palette on a whim. Now every slop product is retrowave or a riff off it, it seems. I generated a pile of reference images with prompts along the lines of “cyan, dark blue, and pink retrowave technology, geometric, home automation”. I contracted out the logo, wordmark, and brand sheet on Upwork as well. I was treating this as a proper SaaS project. Shame it never got done.

Three dark login pages stacked: Log In via OAuth provider with Google and Amazon buttons, Welcome with a Log Out button, and Logged Out with a Log In button, each with a cute low-poly cyan and magenta octopus on the right.
The auth service's login, welcome, and logged-out pages. The cute octopus is the one AI image that shipped.

Where It Stands

The service ran on a VPS where both hacked devices talked to it. Neither the dashboard’s device view nor the Alexa skill and Google Home integration on the state API ever got built, so controlling a device meant logging into the database. I never hooked up the soil sensor (though I did build a physical prototype).

In my 2025 home automation post I noted that I had put it on hold, it has since been shut down. During that time I let the certificate lapse (sorry). It’s now been migrated to Cloudflare with a temporary index page as a space saver. I’ll try to get back to it.

On the plus side, I learned Docker, nginx, OAuth, and Let’s Encrypt on a project I cared about. I also think some of the higher level decisions are right. And I am still very much against the anti-pattern of the vendor lock-in. The main lesson learned here is the tech stack, and again not taking on a moonshot scoped project. I was trying to one-shot a SaaS as a hobby, before agentic coding. People do it. I am too easily distracted to.

I should note, the landscape is different now. Agentic coding has sort of eaten the business layer. I could release it as open source after a polishing pass, but I doubt it would ever make enough to keep the lights (servers) on. I am not sure how many people would want to self-host this, though that would be a perfectly valid thing to do.

Message me if you are interested in this as an open source project, or for some reason want to get in business with me.

The closing slide of my pitchdeck asked one question, so I’ll end on it.

Will it Okos?
~ Curt
Posted in Home Automation