Git Product home page Git Product logo

octolux's Introduction

LuxPower Inverter / Octopus Time-of-use Tariff Integration

This is a Ruby script to parse Octopus ToU tariff prices and control a LuxPower ACS inverter according to rules you specify.

The particular use-case of this is to charge your home batteries when prices are cheap, and use that power at peak times.

Note that I've superceded this project with a more generic "LXP to MQTT bridge" type project, which tries to be less opinionated and focuses more on just getting messages between your inverter and MQTT. You can find it at https://github.com/celsworth/lxp-bridge.

Installation

You'll need Ruby - at least 2.3 should be fine, which can be found in all good Linux distributions.

This apt-get command also installs the Ruby development headers and a compiler so Ruby can build extensions as part of installing dependencies:

sudo apt-get install ruby ruby-dev ruby-bundler git build-essential

Clone this repository to your machine:

git clone https://github.com/celsworth/octolux.git
cd octolux

Now install the gems. You may occasionally need to re-run this as I update the repository and bring in new dependencies or update existing ones. This will install gems to ./vendor/bundle, and so should not need root:

bundle update

Create a config.ini using the doc/config.ini.example as a template:

cp doc/config.ini.example config.ini

This script needs to know:

  • where to find your Lux inverter, host and port.
  • the serial numbers of your inverter and datalogger (the plug-in WiFi unit), which are normally printed on the sides.
  • how many batteries you have, which determines the maximum charge rate (used in agile_cheap_slots rules)
  • which Octopus tariff you're on, AGILE-18-02-21 is my current one for Octopus Agile.
  • if you're using MQTT, where to find your MQTT server.

Copy rules.rb from the example as a starting point:

cp doc/rules.example.5p.rb rules.rb

The idea behind keeping the rules separate is you can edit it and be unaffected by any changes to the main script in the git repository (hopefully).

Inverter Setup

Moved to a separate document, see INVERTER_SETUP.md.

Usage

There are two components.

server.rb

server.rb is a long-running process that we use for background work. In particular, it monitors the inverter for status packets (these include things like battery state-of-charge).

It starts a HTTP server which octolux.rb can then query to get realtime inverter data. It can also connect to MQTT and publish inverter information there. See MQ.md for more information about this.

It's split like this because there's no way to ask the inverter for the current battery SOC. You just have to wait (up to two minutes) for it to tell you. The server will return the latest SOC on-demand via HTTP.

You can use the provided systemd unit file to run the server. The instructions below will start it immediately, and then automatically on reboot. You may need to edit octolux_server.service before copying it into place, unless your installation is in /home/pi/octolux. You'll need to be root to do these steps:

cp systemd/octolux_server.service /etc/systemd/system
systemctl start octolux_server.service
systemctl enable octolux_server.service

The logs can then be seen with journalctl -u octolux_server.service.

octolux.rb

octolux.rb is intended to be from cron, and enables or disables AC charge depending on the logic written in rules.rb (you'll need to copy/edit an example from docs/).

There's also a wrapper script, octolux.sh, which will divert output to a logfile (octolux.log), and also re-runs octolux.rb if it fails the first time (usually due to transient failures like the inverter not responding, which can occasionally happen). You'll want something like this in cron:

0,30 * * * * /home/pi/octolux/octolux.sh

To complement the wrapper script, there's a log rotation script which you can use like this:

59 23 * * * /home/pi/octolux/rotate.sh

This will move the current octolux.log into logs/octolux.YYYYMMDD.log at 23:59 each night.

Development Notes

In your rules.rb, you have access to a few objects to do some heavy lifting.

octopus contains Octopus tariff price data. The most interesting method here is price:

  • octopus.price - the current tariff price, in pence
  • octopus.prices - a Hash of tariff prices, starting with the current price. Keys are the start time of the price, values are the prices in pence.

lc is a LuxController, which can do the following:

  • lc.charge(true) - enable AC charging
  • lc.charge(false) - disable AC charging
  • lc.charge_priority(true) - enable charging battery from solar before providing house load
  • lc.charge_priority(false) - disable charging battery from solar before providing house load
  • lc.discharge(true) - enable forced discharge
  • lc.discharge(false) - disable forced discharge
  • lc.charge_pct - get AC charge power rate, 0-100%
  • lc.charge_pct = 50 - set AC charge power rate to 50%
  • lc.discharge_pct - get discharge power rate, 0-100%
  • lc.discharge_pct = 50 - set discharge power rate to 50%
  • lc.charge_amount_pct - get AC Battery Charge Level, 0-100%
  • lc.charge_amount_pct = 50 - set AC Battery Charge Level to 50%

Forced discharge may be useful if you're paid for export and you have a surplus of stored power when the export rate is high.

Setting the power rates is probably a bit of a niche requirement. Note that discharge rate is all discharging, not just forced discharge. This can be used to cap the power being produced by the inverter. Setting it to 0 will disable discharging, even if not charging.

octolux's People

Contributors

celsworth avatar danfulton72 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

octolux's Issues

SSL certificate expired?

Hi there!

Seem to have a certificate issue since 3pm today? Any ideas?

'
from /octolux/octolux.rb:9:in <main>' /usr/local/lib/ruby/2.7.0/net/protocol.rb:44:in connect_nonblock': SSL_connect returned=1 errno=0 state=error: certificate verify failed (certificate has expired) (OpenSSL::SSL::SSLError)
from

Trying to set Charge Priority Enable and Disable

Hi,

I'm looking to add Charge Priority, to charge the battery exclusively, therefore reducing the amount of export while charging - over winter if I only produce 5kWh of solar a day, exporting 1kWh of that feels wrong!

Looking at the registers it is either
74 FORCED_CHG_POWER_CMD

or

21 FUNC_AC_CHARGE / FUNC_FORCED_DISCHG_EN / FUNC_FORCED_CHG_EN /
FUNC_EPS_EN / FUNC_FEED_IN_GRID_EN / FUNC_SET_TO_STANDBY ?

MOST SIGNIFICANT BYTE

bit 3 = charge priority (charge before supplying load)

Any thoughts on which it might be?

After adding AC_CHARGE_SOC_LIMIT my confidence was hi, it is now somewhat lower!

Thanks

(suggestion requested, not a defect!) - triggering charge manually

Hey Chris!

I had a query and was hoping to get your thoughts on it. I've recently moved to intelligent octopus, where they provide cheap power 1130pm - 530am each day, but can also grant additional half hour slots (like agile) outside of these hours if it makes sense to them. This is dynamic and not published in the API, so there's no way to query it, you just see it in an intelligent octopus app, and it's then reflected in your billing, but API will only ever show 1130-530.

What I now do is charge the battery fully (using ac charge enabled = true, start time 1130 end time 530am) in this window, and then have it dump into the house outside of this window of time.

What I'm looking for is a way to see if I can force charge the battery if for example I get 2 added slots, let's say at 930-10 and 10-10:30. This would be transient - i.e. it would apply for today only.

I'm trying to think of the art of the possible here, and am likely either overcomplicating it or missing something obvious. I'm seeing options as:

  1. Use the app. Log in, update the AC start/end time for the 2nd time window for this added time. Log in later and reset it back to 00:00 - 00:00 so it doesn't repeat this next day. Pro: easy. cons: fully manual and relies on me remembering to reset back to original time.

  2. Manipulate your script a bit, somehow. this is where i'm potentially overcomplicating. your script is for agile, so what i'd do is strip out all the agile stuff, and just have it control turning on charge at 11:30 (ac charge = enabled) and stopping charge (ac charge = disabled) at 530. what i could then do is have a telegram or url based trigger that starts off the charge process (ac charge = enabled). if i have this running every 30 mins, it'd automatically switch off the ac charge for the next half hour period as it'd revert to the schedule as it should be, and cancel my override. pro: bit more automated, no risk of me forgetting to reset and change config of the inverter, but con: potentially overcomplicated.

Before I go hack around, was hoping you may have some words of wisdom!

Thanks Chris

Lux API not updating

hi @celsworth ,

As of yesterday it seems the API is not updating?! I'm getting the data feed directly from http://192.168.1.17:4346/api/inputs which has worked wonders. I can still open that URL and see the JSON, but it's not updating.

Logging into the luxpower manager online I'm seeing current and updated info.

Any thoughts? I know this isn't a bug with octolux - octolux is in fact working fine- I can trigger controls as always and even query this API, but if the API itself is not providing updated data then what?!

Thanks!

Uncommented SOC line in basic rules.rb --- Error in log

Uncommented this line to log SOC.

#LOGGER.info "Battery SOC = #{ls.data['soc']}%" if ls.inputs['soc']

This was the result in the log:

rules.rb:15:in <main>': undefined method data' for #LuxStatus:0x016ab458 (NoMethodError)
from /home/pi/octolux/octolux.rb:30:in instance_eval' from /home/pi/octolux/octolux.rb:30:in

'
I, [2020-05-26T11:00:04.005927 #7152] INFO -- : Current Octopus Unit Price: 6.279p
rules.rb:15:in <main>': undefined method data' for #LuxStatus:0x01bbce20 (NoMethodError)
from /home/pi/octolux/octolux.rb:30:in instance_eval' from /home/pi/octolux/octolux.rb:30:in '
I, [2020-05-26T11:00:21.233267 #7161] INFO -- : Current Octopus Unit Price: 6.279p
rules.rb:15:in <main>': undefined method data' for #LuxStatus:0x025fe528 (NoMethodError)
from /home/pi/octolux/octolux.rb:30:in instance_eval' from /home/pi/octolux/octolux.rb:30:in '
I, [2020-05-26T11:00:38.435472 #7167] INFO -- : Current Octopus Unit Price: 6.279p

Issue: 'undefined method any?'

Hi there, me again :)

I can't figure this one out unfortunately...
I'm trying to make use of your smart octopus rules script. I have no idea if it's working based on the output in the log file:

rules.rb:73:in `<main>': **undefined method `any?' for nil:NilClass** (NoMethodError)
	from /octolux/octolux.rb:28:in `instance_eval'
	from /octolux/octolux.rb:28:in `<main>'
I, [2020-03-14T19:59:26.944449 #4404]  INFO -- : Current Price = 8.274p
I, [2020-03-14T19:59:26.948678 #4404]  INFO -- : SOC = 10% / 90%, charge_size = 3.84 kWh, hours = 1.54
rules.rb:73:in `<main>': undefined method `any?' for nil:NilClass (NoMethodError)
	from /octolux/octolux.rb:28:in `instance_eval'
	from /octolux/octolux.rb:28:in `<main>'
I, [2020-03-14T19:59:37.158243 #4409]  INFO -- : Current Price = 8.274p
I, [2020-03-14T19:59:37.170503 #4409]  INFO -- : SOC = 10% / 90%, charge_size = 3.84 kWh, hours = 1.54

Plus side is it is taking my SOC from the server, my charge size, hours correctly. Is there any way to 'see' what slots it has picked as cheapest times and when it would ultimately start grid charging? i.e. can I preempt / predict what it'll do or do I just need to keep fingers crossed and check the next day?

Lux Consumer Power

Is it possible to obtain current power use?

Within the web interface under the maintenance tab 'History data' it appears as ''pLoad'. Under the Overview tab it appears as 'Load'. So I assume it must be embedded somewhere in the 2 minute outputs from the inverter.

Any guidance much appreciated.

Not an issue - but a question: how often does the octolux server.rb 'refresh' data?

Hi there,

This is an amazing script - thank you!

Had a query - for the octolux server.rb - how often is it 'polling' data to output the array on the page from which the magic happens? I'd like to ideally pull this data for other uses but want to understand frequency.

Is there any way you're aware of to pull data directly from the device, maybe some HTTP post request? or even API? I'd love to be able to grab some of this data in more real time using PHP which I understand better.

Thanks!

Fine for a year+, sudden connection issues

Hi there!

I've had no issues at all with octolux for ages until a couple of days ago where it's stopped connecting to my inverter.

13: from /var/www/html/octolux/test/ac_charge_off.rb:11:in <main>' 12: from /var/www/html/octolux/lib/lux_controller.rb:41:in charge'
11: from /var/www/html/octolux/lib/lux_controller.rb:96:in update_register' 10: from /var/www/html/octolux/lib/lux_controller.rb:112:in read_register'
9: from /var/www/html/octolux/lib/lux_socket.rb:17:in write' 8: from /var/www/html/octolux/lib/lux_socket.rb:63:in socket'
7: from /usr/lib/ruby/2.5.0/socket.rb:631:in tcp' 6: from /usr/lib/ruby/2.5.0/socket.rb:227:in foreach'
5: from /usr/lib/ruby/2.5.0/socket.rb:227:in each' 4: from /usr/lib/ruby/2.5.0/socket.rb:641:in block in tcp'
3: from /usr/lib/ruby/2.5.0/socket.rb:137:in connect' 2: from /usr/lib/ruby/2.5.0/socket.rb:56:in connect_internal'
1: from /usr/lib/ruby/2.5.0/socket.rb:1213:in connect_nonblock' /usr/lib/ruby/2.5.0/socket.rb:1213:in __connect_nonblock': No route to host - connect(2) for 192.168.1.88:4346 (Errno::EHOSTUNREACH)

The IP address is correct, I'm able to log into the IP address and see network settings, no firewalls or anything, and I've tried rebooting the full system.

Any other ideas??

Thanks!

More of a question really

I'd like to be able to set AC Battery Charge Level so that if my solar forcast is above 15kWh for tomorrow I'd only charge up to 50% overnight (for example).

That setting isn't in registers.rb in lxp-packet, and I've tried network sniffing to figure it out, but haven't been able to!

Can you help!

Help requested: Can't get the webserver up

Hey Chris,

I've been trying to port my octolux onto a raspberry pi. Gone through all the instructions, even got the server up but the page is still unaccessible. Clearly I've missed something!!

From octolux_server.service log:

Sep 20 14:54:14 raspberrypi systemd[1]: Started Octolux Server.
Sep 20 14:59:20 raspberrypi server.rb[435]: [2021-09-20 14:59:20] INFO WEBrick 1.4.2
Sep 20 14:59:20 raspberrypi server.rb[435]: [2021-09-20 14:59:20] INFO ruby 2.5.5 (2019-03-15) [arm-linux-gnueabihf]
Sep 20 14:59:20 raspberrypi server.rb[435]: [2021-09-20 14:59:20] INFO WEBrick::HTTPServer#start: pid=435 port=4346
Sep 20 15:03:05 raspberrypi server.rb[435]: 192.168.1.176 - - [20/Sep/2021:15:03:05 BST] "GET / HTTP/1.1" 404 0
Sep 20 15:03:05 raspberrypi server.rb[435]: - -> /

For reference, I've tried to load the site using http://192.168.1.17:4346 which yields a 404 error - you can see this in the last 2 lines of the log.

Far as I can see no firewalls running that could be blocking it - and that would yield a different error.

Trying to run via sudo ruby server.rb yields the same error on the console as the two lines above - 404 error. Running the ruby scripts in the test folder (e.g. toggle ac charge) DOES work, so it is communicating with my inverter.

Any idea what I'm missing?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.