Devmel integration for Gladys Assistant

Control the shutters, switches, lights and sensors of a Devmel AirSend radio gateway.
This integration connects Gladys to a Devmel AirSend or AirSend Duo radio gateway, and through it to the 433 MHz / 868 MHz equipment it drives: roller shutters, switches, dimmable lights, gates, and the sensors that talk back (Somfy RTS, Chacon DiO, Nice, FAAC, Bubendorff…).
Before you start
Your devices are paired in the AirSend mobile app or on airsend.cloud, not in Gladys: this integration replays the radio orders they already know. Get them working there first.
Everything then goes through the local channel below: the integration drives your box on your own network, and never calls airsend.cloud to send an order.
Local
The local channel talks to the AirSend Web Service, the small HTTP server Devmel ships to drive the box over your LAN.
It is included. The integration runs it inside its own container, on
http://127.0.0.1:33863, and supervises it: nothing to install next to
Gladys, no URL to fill in. The Use the built-in AirSend service switch, on
by default, is what controls it.
So the only thing the local channel needs from you is the connection string exported by airsend.cloud, which looks like:
sp://password@fe80::xxxx:xxxx:xxxx:xxxx?gw=0&rhost=192.168.1.50
Paste it exactly as airsend.cloud gives it to you: add nothing, remove nothing, do not retype it.
Always keep the ?gw=0&rhost=<box IPv4> part: without it the box is reached
by IPv6 link-local only and the service answers unexpected errors.
Where that string is set on airsend.cloud
It is not meant to be typed by hand: it is produced by the Communication screen of app.airsend.cloud. Every field there writes a piece of the string, and two of them decide whether the integration will talk to your box over your own network or over the internet.
| Field on airsend.cloud | What it writes in the string | What to set it to for Gladys |
|---|---|---|
| Local IP | the part after @ | Nothing to fill in: the box link-local IPv6, the same as localip |
| Password | the part before @ | The password of the box |
| Internet gateway | gw=1 or gw=0 | Off, i.e. gw=0: the order stays on your network |
| Secondary address | rhost= | The box IPv4 on your network, not your router's |
| Connection mode / WebService Url | nothing | Concerns the airsend.cloud page only (see below) |
| Maximum delay (ms) | nothing | Leave it empty |
Connection mode and WebService Url say how the airsend.cloud page
reaches your box from your browser — typically an AirSend service running on
your own PC, hence the http://127.0.0.1:33863. Gladys never uses them: it
runs its own service inside its container. Those two fields can stay as they
are.
The two that matter are the others:
- Internet gateway turned on writes
gw=1, and the box becomes reachable through the internet. Handy from a phone on 4G, and a needless detour for an integration sitting on the same network as the box: every order makes a round trip through the cloud, which shows in the logs (a good second between an order and its echo, instead of two or three tenths). Turn it off and keepgw=0. - Secondary address writes
rhost=. That is the address the AirSend service dials from the Gladys container, so it has to be the IPv4 of the box, routable from the Gladys host. Read it off your router's DHCP leases and reserve it so it does not change; without it the box is reachable by IPv6 link-local only and the service answers unexpected errors.
After any change, click Save, then copy the connection string again into Gladys: it changed.
Never retype it
Copy and paste the whole string, from sp:// to the end, into an emptied field.
The box answers a damaged string exactly what it answers a wrong password:
[WARN] [airsend] Local transfer failed for "Chambre parents": Invalid connection string (HTTP 401)
[WARN] Could not listen through "AirSend box": Invalid connection string (HTTP 401)
What gets damaged, by retyping and by proofreading alike:
- the double colon. A link-local address is written
fe80::dcf6:e5ff:fe8f:89cd—fe80, then two colons. Retyped, it becomesfe80:dcf6:e5ff:fe8f:89cd: five groups of an eight-group address. Still address-shaped, no longer an address; - what looks like it needs correcting. A URL normally puts an IPv6 address between square brackets; airsend.cloud exports it without them, and boxes answer that form. Both spellings exist, neither is to be added: what airsend.cloud gives you is what to paste;
- what cannot be seen. The field is a secret and shows nothing but dots: the masking dots copied instead of the password, a thin space picked up from a web page, an apostrophe turned typographic by a note-taking app — all of it pastes in with nothing to show for it.
The integration now checks what can be checked without a box — the sp://, the
password, the address, characters that have no business being there — and writes
it to the logs when the configuration loads, on the Configuration screen, and on
the Local line of Test the connection.
Above all, that action shows the string itself, password removed:
Connection string: sp://<16 characters>@fe80::dcf6:e5ff:fe8f:89cd?gw=0&rhost=192.168.1.50
It is the only way to see what Gladys is really holding. Half the mistakes it
catches are ones no check can name: the address of the other box, a gw=1
nobody asked for, an rhost left on the old IP. The password itself is never
shown and never logged — only its length is, which is enough to spot a mask
pasted in its place.
Using a service you already run
If the AirSend Web Service is already running somewhere on your network — the
Home Assistant add-on, the Jeedom daemon, a container of your own — just fill
in the AirSend Web Service URL field (http://192.168.1.50:33863/). A URL
typed there wins: the integration uses it and starts nothing of its own.
Configuration
- Open the Configuration tab of the integration.
- Paste the connection string of your box. That is enough: the AirSend service is already running inside the integration.
- Paste your device list (see below).
- Save, then click Test the connection: it reports the built-in service, the local channel, and how many devices it understood.
- The devices appear in the Discovery tab, ready to be added to Gladys.
The AirSend Web Service URL is only needed in the case described above: a service of your own.
Each device shows as a badge the channel that carried its last order — local
when your box answered, unreachable when it could not be reached.
The device list
On airsend.cloud, open Import/Export and export your devices as JSON. That export is read as it comes, on a single line included, which is what the Devices field expects.
The JSON export
It looks like this — a list, with the radio channel written flat:
{
"devices": [
{
"name": "Living room shutter",
"localip": "fe80::dcf6:e5ff:fe8f:89cd",
"type": 4098,
"pid": 25455,
"addr": 8295
}
]
}
Paste it as is, one line included. pid and addr are the radio channel of
the device: pid is the channel id (the protocol, shared by every device
driven the same way) and addr its source (the address of the emitter).
localip is the address of the box the device belongs to; the box itself is
still reached through the sp:// connection string filled in above.
An entry without a radio channel is ignored: there is nothing the box could
send for it. The box itself (type: 0) is the exception — it always answers on
channel 1.
Writing the list by hand
The same list can be written by hand, keyed by name, with the channel nested
under channel instead of the flat pid / addr pair. It stays JSON:
{
"devices": {
"AirSend box": { "type": 0, "sensors": true, "refresh": 300 },
"Living room shutter": {
"type": 4098,
"invert": true,
"channel": { "id": 25455, "source": 94311 }
},
"Pergola light": { "type": 4100, "channel": { "id": 26848, "source": 1442421508 } },
"Outdoor sensor": {
"type": 1,
"features": ["temperature", "humidity"],
"channel": { "id": 1368, "source": 542 }
}
}
}
Device types
type | Device | What you get in Gladys |
|---|---|---|
0 | AirSend box | Its temperature and light sensors |
1 | Radio sensor / remote | The readings it emits (see features below) |
4096 | Button | A push button sending TOGGLE |
4097 | Switch | On/Off |
4098 | Shutter | Open / Stop / Close (+ position once timed) |
4099 | Shutter with position | Open / Stop / Close + a 0-100 % position |
4100 | Dimmable light | On/Off + brightness |
Device options
| Option | Meaning |
|---|---|
type | Device type (table above), required |
channel | Radio channel (id, source, mac, seed), required |
pid | The channel.id of the JSON export (use either form) |
addr | The channel.source of the JSON export |
remotes | Other emitters driving the device (see "The wall remote") |
spurl | Connection string of this device, if it differs from the global one |
wait | Wait for the radio confirmation before answering (false by default) |
repeat | Extra emissions of the orders sent to this device (see below) |
invert | Swap open and close, for shutters installed the other way round |
travel_up | On a shutter: seconds for a full opening (see below) |
travel_down | On a shutter: seconds for a full closing |
travel | Both at once, for a motor that behaves the same in either direction |
favorite_position | On a shutter: the position programmed in the motor, in % |
sensors | On a box (type: 0), expose its temperature and light sensors |
refresh | Read interval of those sensors, in seconds |
features | On a sensor (type: 1): temperature, humidity, illuminance, click |
A box declared without sensors: true creates no device in Gladys — it is
only there to carry the connection string.
Getting an order through
Nothing acknowledges a radio order. A 433 MHz shutter has no way back: the box transmits, and nobody will ever say whether the motor heard it. A frame lost in the noise — a microwave, the neighbour's remote, a garage door — is therefore not an error shown anywhere, it is a click that did nothing. That is exactly what "I have to press Open two or three times" is made of.
Three things answer it, with nothing to set:
- orders go out one at a time. A box has one radio: while it transmits it hears nothing, and a second order fired straight after reaches a box that is still busy. Orders therefore queue up, a quarter of a second apart, in the order you gave them;
- an order the box could not carry is sent again. A refusal is not: a rejected connection string and an unknown channel answer exactly the same way the second time;
- an order is repeated on the air, the way a real remote repeats it for as long as the button is held. By default every order goes out twice.
The Command repeats field sets that number of extra emissions: raise it to
2 or 3 if a device still ignores Gladys now and then, 0 sends each order
once. A stubborn device can have its own, without changing anything for the
others:
{ "devices": { "Patio door": { "type": 4098, "pid": 25455, "addr": 8295, "repeat": 3 } } }
Only orders that mean the same thing twice are repeated — Open, Stop, Close, a position. A push button TOGGLE goes out once: heard twice, it is back where it started.
Those repeats do not make the interface wait: Gladys is answered as soon as the order is on the air, and the repeats keep going behind it, in their place in the queue. They are a second chance for a frame lost in the noise, not part of the answer — and re-arming the listener, which needs the radio too, now waits for the queue to empty instead of slipping between two orders. That is what made a "Close" clicked right after an "Open" take a few seconds to go out.
The first order after a long pause
An order that leaves in a fraction of a second most of the time, and takes several after a quiet evening, is not the radio: it is the link to the box having gone cold. Nothing had spoken to it for hours, so the session had to be built again before anything could be sent, and that wait landed on whoever clicked.
The integration keeps that from happening: when the box has been left alone for
four minutes, it reads the box own sensors — a request that reaches the box and
never goes on the air. An installation being used stays warm on its own traffic
and nothing extra is sent. It costs one small request per idle box, and it is
what polling already did for a box declared with sensors: true; a box that only
carries the connection string had nothing.
Four minutes is where it starts, not a fact: how long a box tolerates being left alone is documented nowhere, and it is not the same over Wi-Fi as through the cloud gateway. So the box says. When waking it takes more than a second, the link had already gone cold, the integration halves the wait — down to one minute, never below — and writes the line:
Waking the link to "AirSend" took 2.6 s after 4 min of silence: it goes cold
faster than it was being kept warm… Touching it every 2 min from now on.
It only ever shortens: a fast wake proves the interval it was made at works, never that a longer one would.
When an order still takes more than a second and a half to leave, the logs say so, and say where the time went:
The order sent to "Kitchen shutter" took 4.2 s to reach the air: 0.1 s waiting
for the radio, 4.1 s in the box. The time went into the box, not into the
queue: …
- in the box — the box itself was slow to answer. Two usual reasons: the
connection string carries
gw=1, so every order travels to Devmel servers and back (turn Internet gateway off on airsend.cloud and give the string the box IPv4 withrhost=), or the box sits where its Wi-Fi is weak; - waiting for the radio — the orders in front of this one were still going out. Repeats are the usual reason there were several: lower Command repeats if you raised it.
Gladys does not mistake itself for the remote
Everything the integration transmits comes back to it: the box answers the order, and since it listens permanently, it hears itself transmitting. That echo carries the order just sent — and reading it as a fresh order undoes what the order was doing. A timed shutter sent to 40 % is the example: the echo of its "Open" retargeted it at 100 % and cancelled the Stop due half way, so the shutter ran to the top. The integration now recognizes its own orders and does not replay them.
What identifies an echo is the address it comes from. The channel Gladys transmits on is the box's own: nothing else in the house speaks with that voice, whatever the delay. A box that takes ten seconds to repeat itself is no longer mistaken for a hand on a remote — which was enough to send a shutter stopped at 40 % back to the top.
Two visible consequences:
- a wall remote emits from another address: it is never taken for that echo, and goes on driving the shutter in Gladys;
- when the box answers that it did not carry the order, the integration writes it in its logs, naming the device and what the box said. It is the only trace of an order that went nowhere, and it is worth a look if you often click twice.
When the box says no
Every order handed to the box is answered, and that answer carries a number.
Below 256 the order went out; from 256 up it did not, and the number says
where it died. That is what the line in the logs spells out:
The box did not carry the order sent to "Kitchen". SYNCHRONIZATION (event type
258): the exchange between the AirSend Web Service and the box lost its thread —
a link error, not a radio one. Whether anything went out on the air, nothing
says. Check nothing else drives the box at the same moment…
| Name (type) | What it means | What to check |
|---|---|---|
UNKNOWN (256) | refused without saying why | Restart the box, then the integration |
NETWORK (257) | the service never reached the box | The box is powered, on the network, and still at the address in the connection string |
SYNCHRONIZATION (258) | the link with the box lost the plot | Nothing else driving the box at that moment; a weak Wi-Fi link does it too |
SECURITY (259) | the connection string was refused | Export it again from airsend.cloud and paste it whole |
BUSY (260) | another client had the box locked | Close the AirSend app, or take the box out of the other installation |
TIMEOUT (261) | the box did not answer in time | A box busy for someone else, or standing where its Wi-Fi is weak |
UNSUPPORTED (262) | the box cannot send this | The type and the channel of the device, against the airsend.cloud export |
INCOMPLETE (263) | something was missing | Paste the channel whole, with everything it came with next to id |
FULL (264) | the request was too large | Report it: nothing sent from here should ever be that long |
Command repeats answers none of these. Repeats are for a frame lost in the noise — and a frame lost in the noise is precisely the failure nothing reports: nothing acknowledges a radio order, so an order the box says it carried is all the confirmation there will ever be. When the box itself reports the failure, the order never got as far as the noise. Raising the repeats then only sends orders out closer together, at a box already having trouble keeping up.
The position of a shutter
A 433 MHz shutter never says where it is: the radio carries orders, not positions. What it does have is a duration — a given motor always takes the same time to travel from one end stop to the other. Time that travel once and the position becomes computable.
Time your shutter with a stopwatch, from the moment it starts moving to the moment it stops by itself, and write the two durations down:
{
"devices": {
"Living room shutter": {
"type": 4098,
"travel_up": 22,
"travel_down": 20,
"channel": { "id": 25455, "source": 94311 }
}
}
}
From then on the integration follows the shutter second by second, whether the order came from Gladys or from a wall remote it heard on the radio (which needs the listening channel below). Three things follow:
- a
4098gets a position too — the feature appears as soon as the shutter is timed, and its slider drives the motor with a timed Open/Stop: the integration starts the shutter in the right direction and sends the Stop at the moment the travel says it has arrived; - Stop is honest: a shutter stopped half-way reports the position it actually reached, instead of keeping the 100 % the order had announced;
- the estimate repairs itself: a shutter allowed to reach an end stop is at exactly 0 % or 100 %, because the motor physically stops there. Every full open or close wipes the error accumulated by the previous partial travels.
Expect around ±5 % of accuracy: a motor slows down under load and in the cold. If a shutter drifts, a scenario opening it fully once a day is enough to keep it in step.
A shutter you did not time keeps its previous behaviour: Gladys shows the destination of the last order, which is all a one-way protocol can offer.
Two more things worth knowing:
- until its first full travel, a timed shutter's position is unknown, and the integration publishes nothing rather than inventing a value. Open or close it once and it is set. Asking for a position before that sends the shutter to the nearest end stop, which is exactly what establishes the reference;
- the position is remembered across restarts of the integration: it starts again from the value Gladys kept.
The favourite position
Many motors have a position of their own, programmed in the hardware (the
Somfy "my" button). The radio says "go to your position" without ever saying
what that position is. Measure it once and declare it with
"favorite_position": 40: pressing that button then reports 40 % in Gladys.
Without it, the shutter is reported as stopped somewhere in between — the
honest answer.
Listening to the radio
The AirSend box can forward every frame it hears — a wall remote pressed by hand, a weather sensor waking up — so Gladys follows what happens in the house instead of only what it ordered itself. It is what makes the position of a shutter move when it is opened from its own remote.
It is on by default, and there is nothing to set: the integration subscribes the box to the radio protocol of your devices and receives the frames itself. Nothing to install, nothing to link.
The subscription is re-armed after every order, and renewed every ten minutes. Transmitting takes the box out of reception, and a subscription that did not survive a command is a wall remote Gladys silently stops following — until the next renewal.
The listening channel
What the box listens to is a protocol, not a device: it has one radio, and
subscribing switches it to permanent reception of a single protocol at a
time. Channel 1 is generic 433 MHz listening, which covers the protocols
built to fit in it — but not the others, and a Somfy shutter listened to on
channel 1 stays as silent as a remote nobody presses.
So the integration does not guess: it asks the AirSend service which channel decodes which protocol, and subscribes the box to the one your devices use. Leave the Listening channel field empty.
Fill it in only to listen to something else: the pid of a protocol you have
not declared yet, or 0 to turn listening off.
1 and an empty field mean the same thing: "deduce it". That is the value
the field defaulted to before the deduction existed, and it is read as such — a
1 does not ask for generic 433 MHz listening. Generic listening is what the
deduction falls back to when no radio device is declared, and nothing else. The
field is a text field rather than a number for that very reason: a number
field cannot be left empty, it makes you type something, and that something
then reads as a choice nobody made.
One consequence: the generic 433 MHz decoder cannot be asked for by its pid,
since that pid is 1. It is asked for by name instead — type generic in
the field. That is a test worth running for a 433 MHz remote its own protocol
only half decodes: the generic decoder is another way of reading the same
frames.
Either way, the Listening line of Test the connection says which channel was actually chosen — that line is the authority, not what is typed in the field.
To check, click Test the connection: the Listening line says which protocol is listened to, which devices it covers, and where the frames are pushed — or why they are not.
When two protocols are declared equally — a shutter on one side, the wall remote
attached to it on the other — the one that emits wins. A shutter, a switch,
a lamp do not talk: they are talked to. A remote declared in remotes is there
precisely to be heard, so that is what the box is subscribed to.
A protocol nobody speaks on
If nothing declared on the channel being listened to ever emits by itself, the subscription is armed, the route works, and nothing will ever come through it: the box will only hear the echo of Gladys' own orders. That is the normal state of a house where only shutters are declared — and it looks exactly like a listener that never armed, so the integration says it out loud, in its logs at startup and in the Listening line of Test the connection:
Nothing declared on channel 25455 emits by itself: shutters, switches and lamps
are talked to, they do not talk. The box will hear the echo of Gladys own orders
and nothing else.
The way out is the same one as everywhere else on this page: attach the wall remote that drives the equipment (see below). It has its own address, it is a real emitter, and once declared it is what the deduction listens to.
868 MHz and rolling code
Channel 1, the generic listening, is 433 MHz. An 868 MHz protocol —
Profalux, Somfy io — is not heard on it at all. And as long as you have declared
nothing, there is nothing to deduce from, so the integration falls back to that
channel 1 by default: the box then listens to the wrong band, and the silence
that follows looks exactly like a remote nobody presses. The integration now
says so, in its logs and in Test the connection.
Two ways out: declare a device on that protocol, or fill in its pid as the
Listening channel.
"Listening on 868 MHz" is not a thing — here is what is
A box does not listen to a band, it listens to a protocol: there is no
"868 MHz" switch to flip, and channel 1 is not "the 433 one" as opposed to
some "868 one" living elsewhere — it is one decoder among a couple of hundred,
the one covering generic 433 MHz protocols. Listening on 868 MHz therefore means
naming an 868 MHz protocol by its pid. Three ways, simplest first:
- declare the device in the Devices list (the airsend.cloud export
carries its
pid): the deduction does the rest and the Listening channel field stays empty. This is what works nine times out of ten; - attach its remote (
remotes): a remote actually emits, so on a tie the deduction keeps its protocol rather than the shutter's, which never speaks; - force the
pidin Listening channel when the protocol is not declared yet. To find it, the Find a radio protocol action asks the AirSend service and searches by brand or bypid:
2 protocols for "somfy":
- pid 25455 — Somfy RTS (decodes itself, declared on "Patio door")
- pid 26848 — Somfy io (decodes itself)
Put the pid you want in the Listening channel field…
It also says how each protocol is decoded — by itself, by the generic 433 MHz
channel 1, or only partially — and which one is being listened to right now.
And then there is the dead end, which is worth knowing about: the box has
one radio, so hearing a remote whose protocol you do not know requires
already listening to that protocol. There is no scanning. If its pid is
written nowhere — not in the AirSend app, not in the export — the only way left
is trying the candidates of your brand one by one, pressing the remote after
each attempt and running Test the connection again.
The rolling code of those remotes is not what stops the frames from coming
in. It protects emission: to drive a Profalux shutter, the AirSend box has to
have been paired with the motor, like one more remote. On reception it gets in
the way of nothing: the counter and the mac / seed fields change on every
frame, and the integration ignores them on purpose — an emitter is identified by
its pid and its addr, and those do not move.
What the rolling code does change is the decoding: the AirSend service only
partially decodes those protocols. The frame arrives, the emitter is named, but
it carries no usable note. Such frames are now logged with their pid and
addr: proof that the radio works, and what you need to attach the emitter to a
device.
The wall remote
One shutter is driven by several emitters: the AirSend box, and the remote
screwed on the wall. They speak the same protocol from different addresses,
so the box hears them on different channels, and a frame coming from the wall
belongs to no declared device. It is logged by the integration, with its pid
and its addr.
Attach that address to the device it drives, and pressing the wall remote updates the shutter in Gladys just like Gladys itself would:
{
"devices": {
"Living room shutter": {
"type": 4098,
"travel_up": 30,
"travel_down": 26,
"channel": { "id": 25455, "source": 8295 },
"remotes": [94311]
}
}
}
A bare address is read on the protocol of the device itself; a remote on
another protocol is written in full: "remotes": [{ "pid": 1368, "addr": 542 }].
Let the integration write that line
Copying a pid/addr pair out of a log into one-line JSON, in the right
spelling, is exactly the kind of thing one gets wrong every other time. The
Attach a remote action does it for you:
- press the wall remote (the integration remembers the emitters it hears, including the ones nobody declares);
- in the Configuration screen, run Attach a remote and pick the device it drives;
- the action answers with your device list, unchanged, the remote attached as
{"pid": …, "addr": …}: paste it into the Devices field and save.
It attaches the last emitter heard that no device claims, and names the others without touching them. It also says what its frames decoded to, and warns when the remote speaks another protocol than the device: the box listens to one at a time.
A frame dropped, or one with no address
The box grades every frame it decodes, and the integration ignores the ones it grades badly — exactly as the official Jeedom plugin does. Two very different shapes in the logs:
Ignored a radio frame (unreliable, graded 0): pid 14177, carrying no note the service could decode.
Ignored a radio frame (unreliable, graded 2): pid 25455, addr 94311, carrying level 100 (up).
A frame with an address, graded too low (second line): the box decoded both the emitter and the order, it is simply not confident — a remote at the edge of its range, a crowded band. Turn on Accept unreliable frames: they are then used anyway, at the price of the occasional false trigger.
A frame with no address (first line, a pid and nothing else): the box
picked the protocol up without decoding it. There is no emitter to name and
no order to replay, and no setting makes usable what was never decoded. It is
the signature of a box listening to the wrong decoder: put that pid in the
Listening channel field so the box listens to that protocol on its own
decoder, then run Test the connection again. If the frames come back with an
address, attach it as usual.
If the protocol stays address-less even on its own channel, the AirSend service only decodes it partially (868 MHz rolling code) and no software setting will change that. As a last resort, a device can follow every unattributed frame of a protocol:
{
"devices": {
"Patio door": { "type": 4098, "pid": 25455, "addr": 8295, "remotes": [{ "pid": 14177 }] }
}
}
A remotes entry reduced to a pid no longer names a remote but a whole
protocol: a neighbour's remote on that protocol will drive your shutter. Use it
knowingly.
The remote is attached, and nothing moves
Once the emitter is declared the frame does reach the device — what remains is whether it carries an order. Start with Test the connection: its Heard line answers without going through the logs (see "What the box actually heard"). It tells apart three cases that look alike and are unrelated:
declared on Living room shutter, but its frames carry no order to replay— the frame arrived, the device has no use for it. That is the fate of rolling-code 868 MHz protocols: the service only decodes them partially, so the frame proves the radio works but carries nothing to replay. The position cannot follow, and attaching the remote cannot change that; on the first frame from that emitter the integration says so in the logs too, at info level (no note the service could decode);no device declares it— the emitter is heard, it is simply attached to nothing: run Attach a remote again;- the emitter is not listed at all — the frame no longer arrives. When the remote
was declared on another protocol than its device, the Listening line names it
as unheard, and putting its
pidin the Listening channel field listens to its side instead.
Nothing in the logs after a press? The line is written when a frame comes in: press the remote, then read the logs of the integration again. If there is still nothing, the frame never arrived, and there are only three reasons why:
- listening is not in place. Click Test the connection: the Listening line says which protocol is listened to and where the frames are pushed — or why they are not;
- the remote speaks another protocol. The box listens to a single one at a
time, the one of your declared devices (see "The listening channel"). A wall
remote normally uses the protocol of the shutter it drives; if yours is of
another make, put its
pidin the Listening channel field long enough to spot it; - the frame was heard, but graded too doubtful to be published. Radio is
noisy, and the box grades every frame it decodes. Turn on Detailed logs
(debug): those frames are traced there too, with their
pid, theiraddrand the reason they went no further.
What a press writes in the logs
Each of those lines is said once per emitter — a remote held down emits a frame every half second, and a log that scrolls is a log nobody reads. The counter is re-armed every time you save the configuration: you changed something because it did not work, and the very next press is the one you are watching for.
So after saving, one press writes exactly one line, and which line it is says where the frame stopped:
Heard pid 25455, addr 94311 -> "Baie vitrée" followed it: level 0 (down).
Heard a frame on a channel no device declares: pid 14177, addr 3359265281.
Heard pid 14177, addr 3359265281 for "Baie vitrée", but no note the service could decode
Ignored a radio frame (unreliable, graded 2): pid 25455, addr 94311, carrying level 100 (up).
The first one is the only one that says it works — and it is worth reading twice before blaming the integration for a shutter that did not move: the frame was heard, routed and followed, so what is left is between the order and the motor. No line at all means no frame arrived; see the three reasons above.
Is that really my remote?
An emitter heard is not proof it is the one in your hand: 433 MHz is a public band, and the neighbourhood is on it too. Press yours five times, then run Test the connection and read the counter of the emitter under Heard:
pid 14177, addr 3359265281 (5 frames, last one 3 s ago, no decoded note)
Five frames and "3 s ago" is your remote. A counter that does not move while you press is somebody else's, and attaching it declares a stranger's remote on your shutter.
What the box actually heard
Every check above answers "can the frames get in?". The Heard line of Test the connection answers the next question, the only one that matters once a remote is attached: did they, and did anything move?
The integration remembers the emitters it hears, most recent first, with how many frames each sent, how long ago, what they decoded to, and what the devices made of them:
Heard: 1 emitter heard: pid 14177 "Profalux", addr 3359265281 (3 frames, last one 4 s ago,
no decoded note) — declared on Living room shutter, but its frames carry no
order to replay (a protocol the service only partially decodes): the position
cannot follow.
The pid is named whenever the service answered with its protocol table:
that is the most reliable answer to "which protocol does my remote speak?". The
brand printed on the plastic does not always say — a shutter sold under one name
may carry another manufacturer's radio — while the name here is the decoder that
actually decoded the frame.
Three possible verdicts per emitter:
| Verdict | What it means |
|---|---|
followed by <device> | it works: those frames do drive the device |
no device declares it | the emitter is heard but attached to nothing — Attach a remote |
carry no order to replay | the frame reaches its device and carries nothing to replay (rolling code) |
The line quotes every distinct note an emitter has been heard saying, not
just its last frame — that is what tells a remote whose buttons the service
decodes (notes: level 100 (up); level 0 (down); state stop) from one whose
every button decodes to the same order. The second is a followed by that moves
nothing: a STOP replayed on a shutter that is not moving changes a state, not a
percentage. The report now says so and offers the check: press Open, then Close,
and run the action again. If the note does not change, the AirSend service does
not decode the buttons of that remote, and the position cannot follow.
The echoes of your own orders are not emitters: they are counted apart, at the
end of the line (Plus 3 echoes of your own orders). That is the proof the
frames have a route back in, even when nothing else is heard.
The registry is emptied when the integration restarts: "no radio frame since the integration started" means "nothing since then", not "never". Press the remote, then run the action again.
Three silences that look alike
"Nothing heard" covers three very different problems, and the Heard line now tells them apart:
| What it says | What is going on |
|---|---|
no radio frame since the integration started | nothing gets in at all: neither the frames of the house nor the echoes of your own orders |
no frame from any other emitter, but N echoes | the route works — the box simply hears nothing else on the protocol it listens to |
N frames arrived and were dropped | the radio works: it is the frames themselves that were not usable |
Hence the test to run when the line says "no radio frame": drive a device from Gladys, then run the action again. Everything the integration transmits comes back to it, so the echo of that order must show up in the counter.
- the echo comes back → the route is fine, and it is the protocol being
listened to that is not your remote's. Read the Listening line again: it
says which channel is bound and which devices it covers. An 868 MHz remote
(Profalux, Somfy io) is never heard on channel
1, which is 433 MHz; - the echo does not come back either → the problem is upstream of the radio: the AirSend service posts nothing to the integration. Check the Listening line (did the box accept the subscription?) and the Local line (does the service answer?), then the detailed logs.
Detailed logs
Radio is the one part of this integration nobody can watch: a remote that never shows up is either unheard, dropped as unreliable, or heard and undecodable — and only the debug logs tell those three apart.
The Detailed logs (debug) field of the Configuration screen turns them on. It takes effect at once, with no restart: tick it, press the remote, read the logs, untick it. It is verbose, and not meant to stay on.
The password of your sp:// connection string is never logged, at any
level: the rest of the string — the address, gw, rhost — is, at debug, with
the password replaced by its length alone (see "Never retype it").
If you would rather use the container environment variable, LOG_LEVEL still
works and still wins: the switch raises the level to debug while it is on, then
hands your LOG_LEVEL back when you turn it off.
If you run the AirSend service elsewhere
The AirSend Web Service pushes the frames from the machine it runs on. When the integration is the one running it (the default), it pushes them straight to the integration. A service running on another machine has no way to reach the integration, so the frames have to go through a public URL, which Gladys Plus provides: link your Gladys Plus account and paste your Open API key in the Webhooks block of the Configuration screen.
Actions
- Test the connection — checks the local channel, says which protocol is listened to, what the box actually heard, and which devices were parsed. The fastest way to spot a mistyped connection string, a device list that did not parse, or an attached remote whose frames carry no order.
- Attach a remote — press the remote, pick the device it drives: the action writes the device list to paste back, remote included (see "The wall remote").
- Find a radio protocol — searches the protocols the AirSend service knows,
by brand or by
pid, says how each one is decoded and which one is being listened to: this is where thepidfor Listening channel comes from (see "Listening on 868 MHz"). - Identify a device — pick a device and it is sent a PING. Not every piece of 433 MHz equipment reacts to it.
Good to know
- 433 MHz is a one-way protocol for most equipment: nothing confirms an order was received, and Gladys shows the value it sent. Listening (above) is what turns that assumption into a real state, and timing a shutter (above) is what gives it a position.
- Sensors are read from the box itself, over the local channel.
- The built-in service reaches your box from inside the integration's
container, over your LAN: the
rhost=<IPv4>of the connection string is the address it dials, and it has to be routable from the Gladys host. - Changing the
typeof a device in the list creates a new device in Gladys (the identifier changes) — except between4098and4099, which share it.
Troubleshooting
| Symptom | What to check |
|---|---|
Invalid connection string | Test the connection: it shows the sp:// string, password removed |
Invalid input | The channel of the device (id/pid and source/addr) |
no radio channel in the logs | The entry carries no channel: it needs channel.id, or the pid/addr pair |
no radio confirmation | Normal on equipment without feedback: set wait: false |
| No device in the Discovery tab | Test the connection: the device list probably did not parse |
| The box is unreachable | The ?gw=0&rhost=<IPv4> part of the connection string |
Built-in service unavailable | The integration logs: the service logs its own startup there |
| The box answers by hand, not here | The rhost= IPv4 must be reachable from the container, not just from your desktop |
| You have to click several times | Raise Command repeats to 2 or 3, or the device's own repeat |
| An order takes a second to leave | gw=1 in the connection string: turn Internet gateway off on airsend.cloud |
| An order is slow after a long pause | The logs say where the time went — see The first order after a long pause |
| A shutter shows no position | Time it: travel_up / travel_down, then open or close it fully once |
| The position drifts over time | Re-time the travel, and open the shutter fully once a day to resynchronize it |
| No frame from an 868 MHz remote | Test the connection: channel 1 is 433 MHz. Declare the device, or its pid |
| Remote attached, nothing moves | Test the connection, Heard line: it says whether the frames carry an order |
unreliable, graded N in the logs | A frame graded too low: Accept unreliable frames, or move the box closer |
did not carry the order in the logs | The name that line gives the failure — see When the box says no |
A frame with a pid and no addr | The protocol is not decoded: put that pid in Listening channel |
The integration logs everything it does: read the integration logs from the
Gladys UI (or docker logs on the host), and tick Detailed logs (debug) in
its configuration for the full detail.
Configuration settings
These are the settings Devmel asks for in its configuration screen in Gladys.
| Setting | Type | Required | Description |
|---|---|---|---|
| Getting started | section | No | The AirSend Web Service is included: it runs inside this integration, on http://127.0.0.1:33863, and there is nothing to install on your network. All it needs is the sp:// connection string of your box. Devices are configured in the AirSend app, then exported from airsend.cloud (Import/Export, JSON): paste that export below, the integration reads it as is. |
| Use the built-in AirSend service | boolean | No | Run the AirSend Web Service inside the integration, on http://127.0.0.1:33863. Leave it on unless you already run that service elsewhere on your network. |
| AirSend Web Service URL | string | No | Only if you run the AirSend Web Service yourself (Home Assistant add-on, Jeedom daemon, your own container): its address on your network, e.g. http://192.168.1.50:33863/. Leave empty to use the built-in one. |
| Local connection string | secret | No | The sp:// URL exported by airsend.cloud, pasted exactly as it is given to you — add nothing, remove nothing. It reads sp://password@fe80::...?gw=0&rhost=192.168.1.50 |
| Devices | string | No | Your airsend.cloud JSON export, pasted as is: one entry per device, with its type and its radio channel (written as pid/addr). See the documentation. |
| Listening channel | string | No | Which radio protocol the box listens to, by its pid. Leave it empty — or 1, which means the same thing — and it is deduced from the devices you declared: that is what pushes to Gladys what the box hears (wall remotes, sensors). "Test the connection" says on its Listening line which protocol was chosen and what it covers, and "Find a radio protocol" searches the pids by brand. Fill in a pid to listen to another protocol, the word generic for generic 433 MHz listening (its pid, 1, is taken), 0 to turn listening off. |
| Command repeats | number | No | How many extra times an order is sent on the air, the way a real remote repeats it while the button is held. Nothing acknowledges a radio order, so a frame lost in the noise is a click that did nothing: raise this to 2 or 3 if a device sometimes ignores Gladys. 0 sends each order once. A device can override it with its own repeat value. It answers a frame lost in the noise, which nothing reports: an order the box itself says it did not carry is a different problem, and the logs name it. |
| Refresh interval (s) | number | No | How often the box sensors are read, in seconds. A device can override it with its own refresh value. |
| Accept unreliable frames | boolean | No | The box grades every frame it decodes, and the badly graded ones are normally ignored — as the official Jeedom plugin does. Turn this on when a remote is heard but never followed: its frames are then used anyway, at the risk of the occasional false trigger. It changes nothing for a protocol the box picks up without decoding it (no address in the logs): that one needs the listening channel set to its pid. |
| Detailed logs (debug) | boolean | No | Log every radio frame the box hears, including the ones dropped as unreliable or left undecoded. Turn it on to find out why a remote does not show up, and off again once you know: it is verbose. The password of your connection string is never logged; the rest of it is, at this level, so you can see what Gladys is really holding. |
How to install Devmel in Gladys
- In Gladys, open Integrations: Devmel appears in the catalog, next to the native integrations, with a community badge.
- Click Install. Gladys pulls the Docker image (
ghcr.io/prohand/gladys-devmel:1.0.18), starts it in a sandbox isolated from the core, and generates the integration's interface (devices, discovery and configuration). - Open the Configuration screen of the integration, fill in the settings, and save.
- You can also install it directly from its repository URL: https://github.com/prohand/gladys-devmel.
Devmel requires Gladys >=4.86.0. The catalog inside Gladys refreshes every hour, so a new version becomes available at most one hour after its release.
Not running Gladys yet? It is free and open source: follow the installation guide to get started.
About external integrations
Devmel is an external integration: a community integration packaged as a Docker container and published on GitHub, that Gladys installs in one click and runs in a sandbox isolated from its core. It is published and maintained by prohand, not by the Gladys core team.
- Browse all external integrations
- Discover the native integrations built into Gladys
- Build and publish your own external integration
- Source code on GitHub — source of this documentation