Open cbergmann opened 3 months ago
Hello @cbergmann, We're almost there I guess... The problem is this device exposes features (through its 'Abilities') which are not currently supported/understood by meross_lan so we'd need some reverse engineering of these features in order to implement these.
As a starter (and hopefully finisher) we'd need at least a 'Download diagnostics' from the device so that these features (called 'namespaces' in Meross jargon) are euristically queried and dumped. After that, if everything works as usual, we should be able to infer the meaning of the different data and add decoding/encoding capabilities to meross_lan in order to map these to useful entities.
Another option (in parallel), would be to enable the 'Create diagnostic entities' in meross_lan -> Configuration -> Diagnostic. This would try to decode the data exposed by these namespaces and map every single value to a plain sensor entity so that you 'd just have some sensors reporting the raw device state.
The final (more powerful) option would be to use the 'Start diagnostic trace' (always in meross_lan-> Conf -> Diagnostics) to grab the full protocol exchange over a longer period of time so that you could physically operate the device (if anything is 'operable' from that) and meross_lan would dump every single message occuring.
I don't know the device but it looks like being just an energy/power sensor with maybe some configuration options. I think we could get it to work at least in a basic scenario, maybe more...
Hi, Attach is the downloaded diagnostics information. I also activated diagnostic entities and now have around 20 entities with more or less usefull information. For each of the 6 sensors I get a total consumption (in Wh it seems), A capacity which is 16 (A I think) in all cases and "circuitfactor_factor" of 1. The one missing information that I can see in the original app that is not here up until now is the current usage in W. I also started the diagnostic trace. How can I download that trace information?
config_entry-meross_lan-98f787ca444fe061299056061a3fddc6.json
Awesome! The trace is saved under <HA config>custom_components/meross_lan/traces
The History
message looks like reporting the (instant?) power every 60 seconds over the last few minutes. At least, the timestamps of the reported values are spaced by 60 seconds ...
The ConsumptionH
reminds me of the similar ConsumptionX
usually reported by Meross metering plugs and is likely reporting the energy over last few hours together with total energy x channel (that should be easily implemented).
I think the last missing value (i.e. the actual power reading) is being reported by ElectricityX
message which isn't correctly queried by standard euristics..I'll better check what else might be done to correctly query this. At any rate, it could also be PUSHED (asynchronously) by the device itself from time to time so, if we're in luck, the trace could report the layout of the message.
Hi, attached are traces from yesterday and today. Unfortunately they stop after a few minutes. Is there something I am doing wrong? traces.tar.gz
I've released a preview with some (small) improvements on entities mapping for EM06.
It is still unknown how to decode current power/current readings but I have patched a bit of the 'discovery/diagnostics' code hoping this new preview could better trace these messages (the candidate for this feature is Appliance.Control.ElectricityX
namespace)
You could try install the preview and see if anything improves (the consumption readings should be there though). We'd then need to grab the 'Download diagnostics' again and see if we can extract better info with this new release.
Hi, I installed the preview version and got a new diagnostics file. At the first glance this has not helped but maybe you can see something in the file. When this does not help, maybe looking a little deeper into the vendors app might give some insight. config_entry-meross_lan-98f787ca444fe061299056061a3fddc6.json
Hello,
I've searched a bit the internet and found that Refoss has its own integration for HA. Nevertheless, it doesn't look very 'complete' and I don't really know how and if it works so I'm reverse engineering their python code to implement support for EM06 in meross_lan.
I have 1 question though, if you're up to help with a bit of 'manual querying' before proceeding:
In HA -> Developer Tools -> Services
look for service named meross_lan.request
and setup the parameters as follows:
Device identifier
or the Host address
(whichever is easier to type)Method
to GET
Namespace
to Appliance.Control.Electricity
(more on this later)Payload
to {"electricity": {}}
(more on this later)Now switch to YAML mode and correct the Namespace
field to Appliance.Control.ElectricityX
(this is needed because this namespace is not provided by the UI list...)
Now to the (verbose) details: I've found in the original Refoss code that the query payload should be {"electricity": {"channel":65535}}
but this is actually 'hard' to implement in meross_lan because of a lot of euristics which (actually) don't expect this query format so, in order to implement this, I would need to introduce new euristics/rules but I don't want to do that if they're not necessary.
So, if the {"electricity": {}}
payload works then we're all happy and I'd quickly proceed to a 'simple' coding to add the namespace feature, else, if we really need to send {"electricity": {"channel":65535}}
no problem, it will just take some more time (and add complexity to the code which I always try to avoid)
Having said that, you should try and see if any of these queries work and then post the service reply (I'm confident at least 1 should work) so that I have the correct payload response to inspect.
Thank you! (by the way, just curious: did you try to use the Refoss integration? if so did it work or not?)
Hi this is the result for "{\"electricity\": {}}"
:
request:
header:
messageId: fca51ded841b4746aeaa38435b74f56f
namespace: Appliance.Control.ElectricityX
method: GET
payloadVersion: 1
from: /appliance/meross_lan/publish
timestamp: 1721722213
timestampMs: 0
sign: 726fb500e6620bb07acf8c4f4ca50e6a
payload:
electricity: {}
response:
header:
messageId: fca51ded841b4746aeaa38435b74f56f
namespace: Appliance.Control.ElectricityX
method: GETACK
payloadVersion: 1
from: /appliance/2401066372070174070148e1e9e92d1d/publish
uuid: 2401066372070174070148e1e9e92d1d
timestamp: 1721722213
timestampMs: 384
sign: 726fb500e6620bb07acf8c4f4ca50e6a
payload:
electricity:
- channel: 1
current: 0
voltage: 233546
power: 0
mConsume: 1967
factor: 0
- channel: 2
current: 576
voltage: 232037
power: 115523
mConsume: 4878
factor: 0.8636192083358765
- channel: 3
current: 0
voltage: 232124
power: 0
mConsume: 59
factor: 0
- channel: 4
current: 308
voltage: 233777
power: 770
mConsume: 0
factor: 0.010665059089660645
- channel: 5
current: 0
voltage: 232290
power: 0
mConsume: 0
factor: 0
- channel: 6
current: 335
voltage: 232328
power: -750
mConsume: 0
factor: -0.009644150733947754
and this is the result for "{\"electricity\": {\"channel\":65535}}"
request:
header:
messageId: f235da9ebe8d4a52bc8288465e5cdd31
namespace: Appliance.Control.ElectricityX
method: GET
payloadVersion: 1
from: /appliance/meross_lan/publish
timestamp: 1721722301
timestampMs: 0
sign: 535cd26ec302c66ec9c27faaf190e5ad
payload:
electricity:
channel: 65535
response:
header:
messageId: f235da9ebe8d4a52bc8288465e5cdd31
namespace: Appliance.Control.ElectricityX
method: GETACK
payloadVersion: 1
from: /appliance/2401066372070174070148e1e9e92d1d/publish
uuid: 2401066372070174070148e1e9e92d1d
timestamp: 1721722301
timestampMs: 87
sign: 535cd26ec302c66ec9c27faaf190e5ad
payload:
electricity:
- channel: 1
current: 0
voltage: 233680
power: 0
mConsume: 1967
factor: 0
- channel: 2
current: 574
voltage: 233184
power: 115185
mConsume: 4881
factor: 0.8602570295333862
- channel: 3
current: 0
voltage: 232021
power: 0
mConsume: 59
factor: 0
- channel: 4
current: 311
voltage: 233748
power: 324
mConsume: 0
factor: 0.004454255104064941
- channel: 5
current: 0
voltage: 233313
power: 0
mConsume: 0
factor: 0
- channel: 6
current: 339
voltage: 232127
power: -10
mConsume: 0
factor: -0.0001285076141357422
That looks good.
On your question about the Refoss integration: I looked into this but at that time i did not figure out a way to join the device to the WIFI without using the official app and therefore also using the refoss cloud. That was not an option for me. Now that You mentioned that I tried adding it and after fiddeling a little bit with my firewall I got it working. The only problem was that my HA and the device are on differnt networks. The Integration has no option to specify the IP and relies on sending a broadcast to port 9988. That would not reach the device and therefore setup would fail. When configuring iptables to rewrite the broadcast adress to the device adress before sending the udp datagram out the setup completes and everything works. It seems that the Refoss integration communicates via UDP directly. It queries for the information every 15 seconds and gets a reply with the information. This seems to remove the need for a local MQTT Server. If you are interested in implementing this protocol I have captured a pacap file containing the initial setup and the first minutes of updates. As this contains internal IP adresses and potentialy other sensetive information I am more than happy to send it to you via a secure channel.
I didn't dig too much in the refoss-ha library supporting this but the traffic over UDP could just be the discovery part since I've found parts of code using 'standard' http traffic to query the device. At any rate, I would not go too far (at the moment) since the http stack should work correctly without many issues.
But the trace could become useful in the future...I've sent you a PM
I looked into the pcap files and it seems that your are right. The UDP datagrams are always the same and seem to only contain basic information. The main data seems to be transmitted via http.
Updated pre-release. Beside the naive naming of some sensors it should work. I've removed the decoding of consumption hourly history since the statistics should be better available through HA.
The only 'open question', provided everything else works as expected, is the interpretation of the energy reading.
HA has different configurations in order to manage the possible scenarios and these are more or less based off when (and if) the accumulated values resets from time to time (daily-monthly-yearly....) but I don't have a clear understanding of when the EM06 eventually resets these energy readings.
The refoss-ha
library uses the label/name "Monthly energy" so I would expect these energy values to reset every month but, even if this is a correct assumption, the reset could actually happen anytime: likely the start of the month but not surely.
We have to see what will happen on these.
At the moment I've just used state_class == total
for the entity since the energy might flow in any direction if I'm correct and the meter (and HA statistics too) could then track energy import/export. Just, the code never tells HA if the value has been reset (start of new cycle)
Hi thanks again for the fast reply. I will monitor this information and give feedback what happens. Unfortunately I will be on vacation next week and might not have time to see if the reading changes.
Regarding energy flow it might be interesting that the original integration also adds a sensorsensor.em06_xx_this_month_energy_returned entity. I would think that this one would count if energy is flowing back to the provider but I can not see this because I currently don't have that in my setup.
If it helps I here is a screenshot of all the entities for one of the sensors of the original integration. This is the same for all 6 sensors (a1,b1,c1,a2,b2,c2).
Also I would suggest naming the entities with a1,b1,c1,a2,b2,c2 because the sensors on the device are labeld like this. This would make it more easy to identify which reading is from wich sensor.
Hello, I'd like to read data from my em06 to publish it on EmonCMS. I've installed the integration on Home Assistant (temporary on a VM) and it works like a charm, so I tried inspecting the Python code, but I'm not so expert in this and after 3h of attempts decided to write here.
Would it be possible to have a simple sample code to gather infos via API?
I've tried this:
import requests
import json
request_data = {
"header": {
"messageId": "fca51ded841b4746aeaa38435b74f56f",
"namespace": "Appliance.Control.ElectricityX",
"method": "GET",
"payloadVersion": 1,
"from": "/appliance/meross_lan/publish",
"timestamp": 1721722213,
"timestampMs": 0,
"sign": "726fb500e6620bb07acf8c4f4ca50e6a"
},
"payload": {
"electricity": {}
}
}
url = 'http://192.168.11.195'
try:
# Sends the request
response = requests.post(url, json=request_data)
# Checks the answer
if response.status_code == 200:
print("Reply from the device:", response.json())
else:
print("Error in the request:", response.status_code, response.text)
except requests.exceptions.RequestException as e:
print("Connection error:", e)
but the device replies this:
404 Nothing matches the given URI
I guess that in /appliance/meross_lan/publish
I should probably replace meross_lan
with the specific UUID which I don't know yet.
I've just installed merossBLE but not yet tested on my device.
Could someone please help me? Thanks in advance!
Hello @gponzo,
in meross_lan, the low level interface to the device(s) is almost completely isolated inside the (sub)module custom_components/meross_lan/merossclient
.
This python module is totally 'isolated' from any HA api and so it could (or should) easily work as a standalone library to access the device. Specifically, the httpclient.py module contains the class responsible for managing the HTTP access.
There you'll find all the helpers to build messages, sign, send, get replies and so.
Going back to your code, the first thing to check by the way is the url: devices respond to the http://hostaddress/config
endpoint so you're missing a bit of the path in your example.
As for the 'from' field in the message header it should not be relevant when using the HTTP endpoint. Some devices just complain if it's empty or not set at all but, generally, they don't care (over HTTP) since it's purpose is to tell the device which MQTT topic to use when replying to the request.
Grazie @krahabb, I've just analyzed the code you suggested me, but unfortunately my knowledge on Python is not so deep. Anyway I'll try this evening to do some test.
Consider that I did git clone
of the whole meross_lan
repo. How should I invoke it?
something like
Python3.8 custom_components/meross_lan/merossclient(192.168.11.195)
?
Please forgive my ignorance and, considering that this stuff could be boring for many here, feel free to contact me privately if you wish and prefer it (even in Italian ;--)
Hey @gponzo,
I'm also not very expert with python running modules and stuff..I've just started learning python while developing meross_lan and, in the beginning, I was just copying over samples for HA components development so I'm a bit lost too when trying to use the code outside of HA. Anyway...
I suggest you a few steps (just tried and confirmed working):
__main__.py
in custom_components
folder and start with
import asyncio
from meross_lan.merossclient.httpclient import MerossHttpClient
async def async_main(): client = MerossHttpClient("192.168.1.70", key="")
response = await client.async_request("Appliance.System.All", "GET", {})
print(response)
response = await client.async_request("Appliance.Control.Electricity", "GET", {"electricity": {}})
print(response)
if name == "main": loop = asyncio.new_event_loop() loop.run_until_complete(async_main())
Once the file is in place you can either run it from vscode ('Run python file' from the top-right corner of the editor window) or, from the command line by just typing `python custom_components`
When initializing the MerossHttpClient, beside the host address, you have to provide the 'device key' (long story here if you don't know what I mean...but if you have installed and running in HA you can copy the device key from the configuration there)
The first request ("Appliance.System.All") works on every device and replies a general device status/config while the second one ("Appliance.Control.Electricity") is very specific for metering plugs (like mss310) and will not work on every device. For EM06 you should be good using "Appliance.Control.ElectricityX" instead (every other param is the same).
The `client.async_request` call will automatically create the 'header' part of the message based on 'namespace' and 'method' (will then use the current time and the configured key to fill all of the other header fields.
The 3rd parameter (dict) is the 'payload' part of the request
This could be a start for some experiments, feel free to ask more if any. As for the call we could arrange that on skype when you want.
Thank you so much!
I've just tried creating main.py with the code you wrote, but when I run it I receive this error:
File "meross_lan/custom_components/meross_lan/__init__.py", line 337
match ConfigEntryType.get_type_and_id(config_entry.unique_id):
^
SyntaxError: invalid syntax
I'm afraid that the previous line contains a referral to HomeAssistant:
336 for config_entry in hass.config_entries.async_entries(mlc.DOMAIN):
337 match ConfigEntryType.get_type_and_id(config_entry.unique_id):`
I didn't fill the key value: where can I find the device key in Home Assistant?
I'm afraid that the previous line contains a referral to HomeAssistant:
Yep...I was scared of that since the package itself as a whole is an HA custom component so it has to have all of the dependencies installed (using pip install -r requirements_test.txt
should automatically install all of these..but in the end it would be better to let vscode create the docker container for the environment..everything is then automatically configured)
In order to 'break' these dependencies you'd have to copy the merossclient folder to a different path and use it as if it were a standalone package.
Getting some help here in order to correctly import everything I've tried this:
import asyncio
from merossclient.httpclient import MerossHttpClient
async def async_main(): client = MerossHttpClient("192.168.1.70", key="")
response = await client.async_request("Appliance.System.All", "GET", {})
print(response)
response = await client.async_request("Appliance.Control.Electricity", "GET", {"electricity": {}})
print(response)
if name == "main": loop = asyncio.new_event_loop() loop.run_until_complete(async_main())
- now you should have in your project folder:
-- test.py
-- merossclient (folder)
- at the command line you could use `python -m test`
> I didn't fill the key value: where can I find the device key in Home Assistant?
If you succesfully configured your EM06 in HA (so that's working) then you're done: enter the meross_lan integrations page and hit 'CONFIGURE' for the device entry. On the menu choose 'Configure' and you should see the device key there. This value was likely automatically retrieved from your Meross cloud account when you configured that in HA too - if you did it)
No way :(
I've done what you wrote but I stil get an error:
File "merossclient/namespaces.py", line 97
match name.split("."):
^
About the key, I've been able to configure and I'm using the em06 in Home Assistant, but via Refoss integration. Trying with Meross_lan it asks me a Meross account, which I don't have (I ahave a Refoss one but it doesn't accept it) or the UUID of the device which I don't have too (yet?). I've also attempted to find the key in the Refoss integration but with no luck.
I'm so frustrated about not being able to simply ask the info to this device... I've been inspecting Refoss integration code too, but I didn't find anywhere an http request...
I don't understand the error tbh...what's the complete output (when you run the python -m ..
script it should be verbose enough) ?
My guess is the name
value is empty or more likely None
so the parser throws an error there. Maybe the namespaces.py
file was 'corrupted' ?
I'm pretty sure my suggested approach is working (i'm testing it on the 'dev' branch but the 'master' one should be safer in this regard)
regarding the key, I've digged a bit into the refoss_ha repo and it is using an empty key (i.e. a void string: "") so that my example should work as is. Only, the ...Electricity
namespace is likely not supported for the device and you should just plainly use ...ElectricityX
instead.
As for the uuid
it is normally not needed to communicate over http by Meross device and I guess it is not needed too for Refoss ones.
I'm curious about what happens when you try to configure the device in meross_lan. Which procedure are you following? the Meross account is only 'needed' to retrieve the device key in case you don't know it but in my understanding (so far...) you should be able to make it work by just providing the device ip/address and the key (empty as supposed by looking at the refoss_ha repo)
Hi, sorry for being silent for so long...
That's the complete output:
Traceback (most recent call last):
File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/home/gabriele/dev/refoss_lan/test.py", line 3, in <module>
from merossclient.httpclient import MerossHttpClient
File "/home/gabriele/dev/refoss_lan/merossclient/__init__.py", line 14, in <module>
from . import const as mc, namespaces as mn
File "/home/gabriele/dev/Python/refoss_lan/merossclient/namespaces.py", line 97
match name.split("."):
^
SyntaxError: invalid syntax
It tells about syntax... just like if the name object is not recognized...
About the code in namespace.py
My guess is the
name
value is empty or more likelyNone
I'd say that it enters that if
block explicitly because the payload_get is None
...
so the parser throws an error there
So the parser shouldn't rise an error, as if it is None
it is expected, am I wrong?
96 if payload_get is None:
97 match name.split("."):
98 case (_, "Hub", *_):
99 self.payload_get_inner = _LIST
100 self.payload_type = list
101 self.need_channel = False
102 case (_, "RollerShutter", *_):
103 self.payload_get_inner = _LIST
104 self.payload_type = list
105 self.need_channel = False
106 case (
107 (_, "Control", "Thermostat", *_)
108 | (_, "Control", "Screen", *_)
109 | (_, "Control", "Sensor", *_)
110 ):
111 self.payload_get_inner = [{mc.KEY_CHANNEL: 0}]
112 self.payload_type = list
113 self.need_channel = True
114 case _:
115 self.payload_get_inner = _DICT
116 self.payload_type = dict
117 self.need_channel = False
118 else:
119 self.payload_get_inner = payload_get
120 self.payload_type = type(payload_get)
121 self.need_channel = bool(payload_get)
122
123 self.key_channel = key_channel or (mc.KEY_ID if self.is_hub else mc.KEY_CHANNEL)
124 self.has_get = has_get
125 self.has_push = has_push
126 NAMESPACES[name] = self
Anyway I solved!!!
The solution is in this file: https://github.com/Refoss/refoss-homeassistant/blob/main/custom_components/refoss_lan/refoss_ha/device.py In particular in these lines:
if self.device_type == "r10":
path = f"http://{self.inner_ip}/config"
else:
path = f"http://{self.inner_ip}/public"
So, my final code is:
import requests
import json
import random
from hashlib import md5
import string
def randomstring():
# Hash it as md5
md5_hash = md5()
md5_hash.update("".join(
random.SystemRandom().choice(string.ascii_uppercase + string.digits)
for _ in range(16)
).encode("utf8"))
return md5_hash.hexdigest().lower()
# Dati della richiesta
request_data = {
"header": {
# "messageId": "fca51ded841b4746aeaa38435b74f56h",
"messageId": randomstring(),
"namespace": "Appliance.Control.ElectricityX",
"method": "GET",
"payloadVersion": 1,
"from": "",
"timestamp": 1721722213,
"timestampMs": 0,
"sign": "726fb500e6620bb07acf8c4f4ca50e6a"
},
"payload": {
"electricity": {}
}
}
# Indirizzo del tuo dispositivo
url = 'http://192.168.11.195/public'
try:
# Invia la richiesta
response = requests.post(url, json=request_data)
# Controlla la risposta
if response.status_code == 200:
print("Risposta dal dispositivo:", response.json())
else:
print("Errore nella richiesta:", response.status_code, response.text)
except requests.exceptions.RequestException as e:
print("Errore nella connessione:", e)
Awesome!
I didn't spotted that url 'patching' in the refoss-ha
library...I wonder if current meross_lan
code is working or not for those using the EM06. In fact, after initial issue raising I didn't have any more confirmation or reply...
Hi,
I have a Refoss Smart Energy Monitor EM06 to monitor the power usage of some of my appliances. It seems that Refoss and Meross are somehow connected/the same company therefore I tried this integration but only with limited success. Following is my writeup of what I did and what works.
When using the Refoss app I can pair the device with the Refoss cloud and see the collected data. On my HA setup I strive to not have any dependencies to external cloud services. Therefore I wanted to pair this device locally to my local mosquitto MQTT server. As my mosquitto Server normally does not allow anonymous connections I added another listener with the following config:
The DHCP server is configured to give the EM06 a fixed IP and the firewall is configured to only allow that IP to connect to that port.
/etc/mosquitto/meross.acl
reads as follows:Next I did a hardware-reset on the EM06 by pressing the button for around 5 seconds. The device confirmed that by a red LED and after restarting blinking green.
Thereafter I installed Version v5.3.0 of the meross_lan Integration from via HACS and restarted Home Assistant.
I then installed the MerossBLE app onto my phone. The device was found and correctly reported in the Discovered Device secion as followed.
I entered the hostname of my MQTT Server (redacted as mqtt.my.local) in the "Server" field and 8884 in the "Port" field. I kept 0 the UserId field and entered a short passphrase as "Key". Then I selected "Write". In the "Time Configuration" section I selected "Europe/Berlin" as my "Timezone" and also selected "Write". As a last step in the app I selecte "Scan" in the "WIFI Configuration" section. Then selecting my wifi and entering the password before also confirming with "Write". The LED of the device then turns to solid green.
In the mosquitto log I see the device connecting with username 48:e1:e9:e9:2d:1d. It subscribes to
/appliance/2401066372070174070148e1e9e92d1d/subscribe
and the following three messages are exchanged:This seems to repeat every few seconds.
Next I go to Home Assistant and it discovers the Meros Lan Integration (my HA is in german so some labels might be not correct here). I select "Configure" and enter the same passphrase as device key. It discovers the em06 device immediately and I can configure it.
While doing that the following MQTT messages are exchanged:
The device is added to Home Assistant with two entities. A disabled one for the sensor protocol and one for the signal strenght which reports 100%.
This is it. After that I don't get any more information in Home Assistant.
While testing this I also got the following messages but I was not able to reproduce them. They are not sent frequently. Maybe the are only sent after a certain power usage but I don't know that.
It seems that these contain the correct power consumtions of the devices in mW. The negative numbers seem to be related to the accuracy of the sensor.
I don't know if this is the correct integration or if I should try something different. If this seems interessting for this integration I am more then happy to provied more information or test a new version. I am sufficentily fluent in python to also write a PR if youre are able to give me a hint where to start.
with kind regards Clemens