This Python library aims to provide complete support for all Outback devices connected to a Mate3/Mate3s (or AXS port?) via Modbus. You can:
- Read values in Python - and then do whatever you want with them e.g. monitoring/alerting/dynamic power management etc.
- Write values - i.e. control your Outback system with Python.
- (Hopefully) avoid ever having to know about Modbus at all. Just use the Python API or the CLI.
- (Hopefully) get access to the full Outback spec in a 'user friendly' form. You don't need to know about the magic numbers in Enums or Bitfields (or the SunSpec), or how to interpret fault codes, and nor do you have to worry about twos-complements and other such things.
- Co-develop without giving access to your system. That is, you can 'dump' a snapshot of your system and others can then interact with it (within reason) as if it were a real Mate3 over Modbus - which is great for testing/debugging/etc.
Tested on Python 3.7. May work on 3.6.
The recommended installation is as follows:
pip install mate3
After this you should be able to run the mate3
command. To access your Mate it must be connected to your local network using its ethernet port.
Reading this will help you understand this libary and how to interact with your Mate.
Hopefully, you don't need to worry about Modbus at all - this library should abstract that away for you. The key thing to note is that Modbus is a communication protocol, and this library works by interacting with the Mate3 physical devices using synchronous messages. So:
- The information isn't 'live' - it's only the latest state since we last read the values. Generally, you should be calling
read
orwrite
before/after any operation you make. - Don't over-communicate! If you start doing too many
read
s orwrite
s you might brick the Modbus interface of your Mate (requiring a reboot to fix). As a rule of thumb, you probably don't want to be reading more frequently than once per second (and even then, preferably only specific fields, and not the whole lot). Since it's a communication protocol (and it's not actually clear what the latency is inherent in the Mate), there's not much point reading faster that this anyway. - Given the above, you might want to use the caching options in the
Mate3Client
, which can allow you to completely avoid interacting with/bricking your Mate while you're developing code etc. It's really tedious having to restart it every time your have a bug in your code. - Weird things happen when encoding stuff into Modbus. Hopefully you'll never notice this, but if you see things where your
-1
is appearing as65535
then yeh, that may be it.
You can check out the details of how Outback implements Modbus in ./mate3/sunspec/doc, but the key things to note are:
- SunSpec is a generic Modbus implementation for distributed energy systems include e.g. solar. There's a bunch of existing definitions for what e.g. charge controllers, inverters, etc. should be.
- Outback use these, but they have their own additional information they include - which they refer to as 'configuration' definitions (generally as that's where the writeable fields live i.e. things you can change). Generally, when you're using this library you might see e.g.
charge_controller.config.absorb_volts
. Here thecharge_controller
is the SunSpec block, and we add on a specialconfig
field which is actually a pointer to the Outback configuration block. This is to try to abstract away the implementation details so you don't have to worry about their being multiple charge controller things, etc.
Words are confusing. For now, take the below as a rough guide:
Field
- this is a definition of a field e.g.absorb_volts
isUint16
with units of"Volts"
etc.Model
- This is generally referring to a specific Modbus 'block' - which is really just a collection of fields that are generally aligned to a specific device e.g. an inverter model will have an output KWH field, which a charge controller model won't. (Again, it's confusing here as Outback generally have two models per device.) In the case abovecharge_controller
represents one (SunSpec) model, andcharge_controller.config
another (Outback) model.Device
- this is meant to represent a physical device and is basically our way of wrapping the Outback config model with the SunSpec one.FieldValue
- this is kind of like aField
but with data (read from Modbus) included i.e. "the value of the field". It includes some nice things too like auto-scaling variables ('cos floats aren't a thing) and simpleread
orwrite
APIs.
At this stage, it doesn't exist - the best documentation is the code and the examples, though this only works well for those who know Python. A few other quick tips:
- Turn intellisense on! There's a bunch of typing in this library, so it'll make your life much easier e.g. for finding all the fields accessible from your charge controller, etc.
- ./mate3/sunspec/models.py has all of the key definitions for every model, including all the fields (each of which has name/units/description/etc.). Error flags and enums are properly defined there too.
More documentation is needed (see above), but you can get a pretty code idea from ./examples/getting_started.py, copied (somewhat) below.
# Creating a client allows you to interface with the Mate. It also does a read of all devices connected to it (via the
# hub) on initialisation:
with Mate3Client("...") as client:
# What's the system name?
mate = client.devices.mate3
print(mate.system_name)
# >>> FieldValue[system_name] | Mode.RW | Implemented | Value: OutBack Power Technologies | Read @ 2021-01-01 17:50:54.373077
# Get the battery voltage. Note that it's auto-scaled appropriately.
fndc = client.devices.fndc
print(fndc.battery_voltage)
# >>> FieldValue[battery_voltage] | Mode.R | Implemented | Scale factor: -1 | Unscaled value: 506 | Value: 50.6 | ...
Read @ 2021-01-01 17:50:54.378941
# Get the (raw) values for the same device type on different ports.
inverters = client.devices.single_phase_radian_inverters
for port, inverter in inverters.items():
print(f"Output KW for inverter on port {port} is {inverter.output_kw.value}")
# >>> Output KW for inverter on port 1 is 0.7
# >>> Output KW for inverter on port 2 is 0.0
# Values aren't 'live' - they're only updated whenever you initialise the client, call client.update_all() or
# re-read a particular value. Here's how we re-read the battery voltage. Note the change in the last_read field
time.sleep(0.1)
fndc.battery_voltage.read()
print(fndc.battery_voltage)
# >>> FieldValue[battery_voltage] | Mode.R | Implemented | Scale factor: -1 | Unscaled value: 506 | Value: 50.6 | Read @ 2021-01-01 17:50:54.483401
# Nice. Modbus fields that aren't implemented are easy to identify:
print(mate.alarm_email_enable.implemented)
# >>> False
# We can write new values to the device too. Note that we don't need to worry about scaling etc.
# WARNING: this will actually write stuff to your mate - see the warning below!
mate.system_name.write("New system name")
print(mate.system_name)
# >>> FieldValue[system_name] | Mode.RW | Implemented | Value: New system name | Read @ 2021-01-01 17:50:54.483986
# All the fields and options are well defined so e.g. for enums you can see valid options e.g:
print(list(mate.ags_generator_type.field.options))
# >>> [<ags_generator_type.AC Gen: 0>, <ags_generator_type.DC Gen: 1>, <ags_generator_type.No Gen: 2>]
# In this case these are normal python Enums, so you can access them as expected, and assign them:
mate.ags_generator_type.write(mate.ags_generator_type.field.options["DC Gen"])
# >>> ags_generator_type.DC Gen
A simple CLI is available, with four main sub-commands:
read
- reads all of the values from the Mate3 and prints to stdout in a variety of formats.write
- writes values to the Mate3. (If you're doing anything serious you should use the python API.)devices
- shows the connected devices.dump
- dumps all of the raw modbus values to a (JSON) file in a format compatible withCachingModbusClient
which you can then share with others to help in debugging any problems you may have.
For each you can access the help (i.e. mate3 <cmd> -h
) for more information.
First, the big one:
WARNING! Please make sure you read the license before using any of the
write
functionality. You could easily damage your equipment by setting incorrect values (directly or indirectly).
In addition, there are other edges cases that may cause problems, mostly related to if a device is re-assigned a new port. For example, you have two inverters, read some values, then switch their ports over in the Hub before writing some values - which may now go to the 'wrong' one. For now, it's safest not to do that, unless you restart the Mate3Client
each time. On that note, the recommended approach if you need to poll over time is to recreate the Mate3Client
on every poll (as opposed to re-using one), as that'll help avoid these (or other) issues. There are exceptions to this rule, but you should know why you're breaking it before you do so.
Some ideas (which can be helpful for issues)
See mate3 -h
for the CLI, otherwise the following (or similar) for python code:
from loguru import logger
logger.remove()
logger.add(sys.stderr, level="DEBUG")
$ mate3 devices --host ...
name address port
---- ------- ----
Mate3 40069 None
ChargeController 40986 4
ChargeControllerConfiguration 41014 4
...
Are they all there?
See mate3 dump -h
. You can send the resulting JSON file to someone to help debug. (Just note that it includes all the data about the Mate, e.g. any passwords etc.)
See ./examples/postgres_monitor/README.md
This was originally a heavily refactored version of basrijn's Outback_Mate3 library, though has largely been completely rewritten since. Thanks anyway basrijn!