Skip to content

Commit

Permalink
Various Improvements and cleanup (#260)
Browse files Browse the repository at this point in the history
* remove location log info

* TEST: work with AC power instead of DC power on inverter, log smoothed smartmeter values to mqtt

* Use current AC power reported by inverter instead of predicted AC power (double prediction seems to conflict)

* use old steering interval to control rate limit of limit function

* new time based moving average on timebuffer (better smoothing)

* rapid change detection for new moving average

* better logging for rapid change and limit function trigger

* cp error

* rapid change detection improved, limit function rate limit override

* trigger limit function on rapid change forced

* #192 add support for Hub 2000

* aggressive avoid feed in

* adjust limitedRisefunction to provide better values at top end

* set hub limit timestamp only on actual execution of limit command

* candischarge function

* #198 reduce inverter limit faster wehn AC output is over AC Limit, increase slower when close to AC limit

* enforce limit function every 2min

* correct information if grid feed in happens from hub's battery

* correct determination of home input from battery (packInputPower)

* no need to ask for negative input from solarflow hub

* convenience accessors to get current limits from hub and inverter

* call trigger function if grid feed-in happens only when feed-in involves hub

* Support for Hub2000 in homeassistant (#193)

* Support for Hub2000 in homeassistant

* Added more entities to homeassistant

---------

Co-authored-by: Reinhard Weber (Brandstaedter) <[email protected]>

* Fix #200, move creation of repeated timer to constructor

* documentation update

* documentation

* Issue #199: negative demand leads to hub limit set to zero where it shouldn't

* logging remainder

* logging of remainder and source of feed in

* determining source of feed in

* reducing feed in from hub solarpower if not necessary

* reduction of feed in with proper per channel limit

* adjust hub output power in source determination for feed in

* ignore solarflow feed-in if in bypass mode

* reduce log output

* Convert DC Channel powers into AC power values

* simplify limit calculation

* remainder calculation

* restructure limit function for better readability

* hub ask calc

* correct log output

* correct logging

* fix #211

* trying to fix parts of #210

* Add offset to shift smartmeter zero #209

* fix #211

* fix unassigned variable on insignificant hub contribution ask

* allow hub to trigger limit function

* Log additional smartmeter parameters

* Homeassistant improvements & geoip simplification (#208)

* add DryRun and chargeThrough to Homeassistant

* simplify geolocation

* move HA updates to own timer (10min)

* fix typo

* fix hub power update

* prepare for bypass control

* configparser doesn't understand bool as type

* optional bypass control

* fix #220

* bug typos

* ensure bypass is left again after sunset

* rensure reevaluation of limit when switching bypass off

* ensure bypass is only enabled once when raching full battery

* ensure direct panel limit is increased when there could be still more gained

* Fix #225

* open direct panels a bit more depending on demad

* typo from lint

* bug #226: on AC output over limit we should decrease the limit properly

* #226 enforce over AC limit correction

* when in bypass allow hub to feed everything to home

* enforce bypass to off in the  morning if control_bypass is on

* set hub bypass mode on startup

* fix #229

* add more logging re bypass

* ensure BP also gets enabled when battery didn't fully discharge before.

* turn off bypass only after sunset w/o offset

* introduce bypass allowance

* ensure bypass is not enabled again

* additional bypass info logging

* Updated readme for bypass control and zero offset

* seems the hub resets bypass mode to auto when completely depleted, ensure it is set to manual when bp control is enabled

* Adjusting inverter limit more rigorously in case of overshooting legal limit

* more over limit adjustments/logging

* consider unconnected direct channels

* correct producing channel numnber

* add arm64 support

* Update Jenkinsfile

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline typo

* pipeline branch name

* pipeline branch

* finalize pipeline

* add gh cli tools in pipeline

* fix pipeline github push

* cleanup

* determine direct channel increase by channel limit

* increase direct channel limit tunnel

* consider non-producing channels in direct panel limit

* increase time difference for sunrise action

* no need to set any hub limit when in bypass, more lenient on legal limit hit

* avoid hub contribution that would cross the legal limit

* consider inverter efficiency in hub contribution close to limit; reduce hub change frequency

* improve logging

* additional condition for turning off bypass

* #245 inverter serials can also be alphanumeric

* Better logging on AC limit actions

* better logging on AC limit actions

* copy paste error

* correct bypass mode selector config for homeassistant

* reducing max limit over inverter power to 12.5% as OpenDTU/HA integration uses this maximum

* regular time sync (UTC) for hub

* Bug #244: hub2k doesn't report bypass state via 'pass' topic

* #244 typo fixec for getBypass

* for some reason paho-mqtt not found in apt?

* remove unneeded packages

* add jinja2 to requirements

* #252 power up inverter when leaving charge-through mode and inverter is not ready

* #252 wrong metadata accessor

* #244 workaround for hub2k not reporting bypass state properly.

* more bypass for hub2k handling

* use getter for bypass info in hub log

* race condition for #244

* bug in bypass mode selector in HA

* HA integration for passmode #254

* attempted fix for Hub2k/HA

* #244 hub2k reports pass only when in auto mode

* HA template, try to fix bp select

* HA template fix attempt

* add separate sensor for passMode, turn off autorecover

* feat: #255 show grid feed in if coming from bypass

* HA templates for ByPass

* correct sensor for select update in HA

* enforce bypass auto recover off in the morning
  • Loading branch information
reinhard-brandstaedter authored Jun 13, 2024
1 parent d24e0c5 commit 0da2925
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 66 deletions.
3 changes: 1 addition & 2 deletions src/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ ENV PYTHONUNBUFFERED 1
WORKDIR /solarflow/
COPY ./requirements.txt /solarflow/requirements.txt

RUN apk add py3-scikit-learn py3-requests py3-paho-mqtt && \
python -m venv --system-site-packages /opt/venv
RUN python -m venv --system-site-packages /opt/venv

ENV PATH="/opt/venv/bin:$PATH"
ENV PYTHONPATH="/usr/lib/python3.11/site-packages"
Expand Down
5 changes: 4 additions & 1 deletion src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
astral
astral
paho-mqtt==1.6.1
requests
jinja2
13 changes: 3 additions & 10 deletions src/solarflow/dtus.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def __init__(self, client: mqtt_client, base_topic:str, sf_inverter_channels:[]=
def __str__(self):
chPower = "|".join([f'{v:>3.1f}' for v in self.channelsDCPower][1:])
return ' '.join(f'{yellow}INV: \
AC:{self.getCurrentACPower():>3.1f}W, AC_Prediction: {self.getPredictedACPower():>3.1f}W, \
DC:{self.getCurrentDCPower():>3.1f}W, DC_prediction: {self.getPredictedDCPower():>3.1f}W ({chPower}), \
AC:{self.getCurrentACPower():>3.1f}W, \
DC:{self.getCurrentDCPower():>3.1f}W ({chPower}), \
L:{self.limitAbsolute:>3.0f}W ({self.getChannelLimit():.1f}W/channel) [{self.maxPower:>3.0f}W]{reset}'.split())

def subscribe(self, topics):
Expand All @@ -68,7 +68,6 @@ def updChannelPowerDC(self,channel:int, value:float):
self.acPower.add(value)
self.channelsDCPower[channel] = value

# TODO: experimental, trigger limit calculation only on significant changes of DC power prediction
previous = self.getPreviousACPower()

if abs(previous - self.getCurrentACPower()) >= TRIGGER_DIFF:
Expand Down Expand Up @@ -134,12 +133,6 @@ def getPreviousACPower(self):
def getCurrentDCPower(self):
return self.dcPower.last()

def getPredictedACPower(self):
return self.acPower.predict()[0]

def getPredictedDCPower(self):
return self.dcPower.predict()[0]

def getDirectDCPowerValues(self) -> []:
direct = []
for idx,v in enumerate(self.channelsDCPower):
Expand Down Expand Up @@ -220,7 +213,7 @@ def setLimit(self, limit:int):
#### inv_limit = int(math.ceil(self.limitAbsoluteBuffer.qwavg() / 2.) * 2)

# Avoid setting limit higher than 150% of inverter capacity
inv_limit = self.maxPower*1.5 if (inv_limit > self.maxPower*1.5 and self.maxPower > 0) else inv_limit
inv_limit = self.maxPower*1.125 if (inv_limit > self.maxPower*1.125 and self.maxPower > 0) else inv_limit

# it could be that maxPower has not yet been detected resulting in a zero limit
inv_limit = 10 if inv_limit < 10 else int(inv_limit)
Expand Down
17 changes: 17 additions & 0 deletions src/solarflow/homeassistant/select.passMode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "Bypass Mode",
"cmd_t": "iot/{{ product_id }}/{{ device_id }}/properties/write",
"cmd_tpl": "{\"properties\": {\"passMode\": {% raw %}{{ 0 if value=='Auto' else (1 if value=='Off' else (2 if value=='On')) }}{% endraw %} }}",
"stat_t": "~passMode",
"uniq_id": "{{ device_id }}-passMode",
"val_tpl": "{% raw %}{{ 'Auto' if is_state('sensor.solarflow_hub_bypass_mode', 'Auto') else ('Off' if is_state('sensor.solarflow_hub_bypass_mode', 'Manual off') else ('On' if is_state('sensor.solarflow_hub_bypass_mode', 'Manual on') else 'Unknown')) }}{% endraw %}",
"ops": ["Auto","Off","On","Unknown"],
"dev": {
"identifiers": ["{{ device_id }}"],
"manufacturer": "Zendure",
"model": "Solarflow",
"name": "Solarflow Hub",
"sw_version": "{{ fw_version }}"
},
"~": "solarflow-hub/{{ device_id }}/telemetry/"
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
{
"name": "Bypass",
"cmd_t": "iot/{{ product_id }}/{{ device_id }}/properties/write",
"name": "Bypass Mode",
"stat_t": "~passMode",
"uniq_id": "{{ device_id }}-passMode",
"dev_cla": "switch",
"payload_on": "{\"properties\": {\"passMode\": 2 }}",
"payload_off": "{\"properties\": {\"passMode\": 0 }}",
"state_on": 2,
"state_off": 0,
"value_template": "{% raw %}{{ 'Auto' if value=='0' else ('Manual off' if value=='1' else ('Manual on' if value=='2' else 'Unknown')) }}{% endraw %}",
"dev": {
"identifiers": ["{{ device_id }}"],
"manufacturer": "Zendure",
Expand Down
6 changes: 1 addition & 5 deletions src/solarflow/smartmeters.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self, client: mqtt_client, base_topic:str, cur_accessor:str = "Powe
def __str__(self):
return ' '.join(f'{green}SMT: \
T:{self.__class__.__name__} \
P:{sum(self.phase_values.values()):>3.1f}W {self.power} Predict: {self.getPredictedPower():>3.1f}W{reset}'.split())
P:{sum(self.phase_values.values()):>3.1f}W {self.power}{reset}'.split())

def subscribe(self):
topics = [f'{self.base_topic}']
Expand Down Expand Up @@ -72,7 +72,6 @@ def updPower(self):
self.power.add(phase_sum)
self.client.publish("solarflow-hub/smartmeter/homeUsage",int(round(phase_sum)))
self.client.publish("solarflow-hub/smartmeter/homeUsageSmoothened", int(round(self.power.last())))
self.client.publish("solarflow-hub/smartmeter/homeUsagePredicted",int(round(self.getPredictedPower())))

# TODO: experimental, trigger limit calculation only on significant changes of smartmeter
previous = self.getPreviousPower()
Expand Down Expand Up @@ -107,9 +106,6 @@ def handleMsg(self, msg):
def getPower(self):
return self.power.last()

def getPredictedPower(self):
return self.power.predict()[0]

def getPreviousPower(self):
return self.power.previous()

Expand Down
17 changes: 12 additions & 5 deletions src/solarflow/solarflow-control.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def on_connect(client, userdata, flags, rc):
hub.setBuzzer(False)
if hub.control_bypass:
hub.setBypass(False)
hub.setAutorecover(False)
inv = client._userdata['dtu']
inv.subscribe()
smt = client._userdata['smartmeter']
Expand Down Expand Up @@ -206,7 +207,7 @@ def getSFPowerLimit(hub, demand) -> int:
sunset_off = timedelta(minutes = SUNSET_OFFSET)

# if the hub is currently in bypass mode we don't really worry about any limit
if hub.bypass:
if hub.getBypass():
path += "0."
# leave bypass after sunset/offset
if (now < (sunrise + sunrise_off) or now > sunset - sunset_off) and hub.control_bypass and demand > hub_solarpower:
Expand All @@ -217,7 +218,7 @@ def getSFPowerLimit(hub, demand) -> int:
path += "2."
limit = hub.getInverseMaxPower()

if not hub.bypass:
if not hub.getBypass():
if hub_solarpower - demand > MIN_CHARGE_POWER:
path += "1."
if hub_solarpower - MIN_CHARGE_POWER < MAX_DISCHARGE_POWER:
Expand All @@ -239,18 +240,23 @@ def getSFPowerLimit(hub, demand) -> int:
limit = 0

# get battery Soc at sunset/sunrise
td = timedelta(minutes = 5)
td = timedelta(minutes = 3)
if now > sunset and now < sunset + td:
hub.setSunsetSoC(hub_electricLevel)
if now > sunrise and now < sunrise + td:
hub.setSunriseSoC(hub_electricLevel)
log.info(f'Good morning! We have consumed {hub.getNightConsumption()}% of the battery tonight!')
ts = int(time.time())
log.info(f'Syncing time of solarflow hub (UTC): {datetime.fromtimestamp(ts).strftime("%Y-%m-%d, %H:%M:%S")}')
hub.timesync(ts)

# sometimes bypass resets to default (auto)
if hub.control_bypass:
hub.allowBypass(True)
hub.setBypass(False)
hub.setAutorecover(False)

log.info(f'Based on time, solarpower ({hub_solarpower:4.1f}W) minimum charge power ({MIN_CHARGE_POWER}W) and bypass state ({hub.bypass}), hub could contribute {limit:4.1f}W - Decision path: {path}')
log.info(f'Based on time, solarpower ({hub_solarpower:4.1f}W) minimum charge power ({MIN_CHARGE_POWER}W) and bypass state ({hub.getBypass()}), hub could contribute {limit:4.1f}W - Decision path: {path}')
return int(limit)


Expand Down Expand Up @@ -279,7 +285,6 @@ def limitHomeInput(client: mqtt_client):

hub_power = inv.getHubDCPower() * (inv.getEfficiency()/100)

#grid_power = smt.getPredictedPower()
grid_power = smt.getPower() - smt.zero_offset
inv_acpower = inv.getCurrentACPower()

Expand Down Expand Up @@ -360,6 +365,8 @@ def limitHomeInput(client: mqtt_client):
# since we usually set the inverter limit not to zero there is always a little bit drawn from the hub (10-15W)
if direct_panel_power == 0 and hub_power > 15 and hub.getDischargePower() == 0 and not hub.getBypass():
source = f'hub solarpower: {-grid_power:.1f}W'
if direct_panel_power > 0 and hub_power > 15 and hub.getDischargePower() == 0 and hub.getBypass():
source = f'hub bypass: {-grid_power:.1f}W'
if direct_panel_power > 0 and hub_power < 15:
source = f'panels connected directly to inverter: {-remainder:.1f}'

Expand Down
57 changes: 45 additions & 12 deletions src/solarflow/solarflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

TRIGGER_DIFF = 30

HUB1200 = "73bkTV"
HUB2000 = "A8yh63"


class Solarflow:
opts = {"product_id":str, "device_id":str ,"full_charge_interval":int, "control_bypass":bool}

Expand All @@ -23,7 +27,7 @@ def default_calllback(self):

def __init__(self, client: mqtt_client, product_id:str, device_id:str, full_charge_interval:int, control_bypass:bool = False, callback = default_calllback):
self.client = client
self.SF_PRODUCT_ID = product_id
self.productId = product_id
self.deviceId = device_id
self.fullChargeInterval= full_charge_interval
self.fwVersion = "unknown"
Expand All @@ -34,7 +38,7 @@ def __init__(self, client: mqtt_client, product_id:str, device_id:str, full_char
self.outputHomePower = -1 # power sent to home
self.bypass = False # Power Bypass Active/Inactive
self.control_bypass = control_bypass # wether we control the bypass switch or the hubs firmware
self.bypass_mode = -1 # bypassmode the hub is operating in 0=auto, 1=off, 2=manual
self.bypass_mode = -1 # bypassmode the hub is operating in 0=auto, 1=manual off, 2=manual on
self.allow_bypass = True # if bypass can be currently enabled or not
self.electricLevel = -1 # state of charge of battery pack
self.batteriesSoC = {"none":-1} # state of charge for individual batteries
Expand All @@ -47,7 +51,7 @@ def __init__(self, client: mqtt_client, product_id:str, device_id:str, full_char
self.lastSolarInputTS = None # time of the last received solar input value
self.batteryTarget = None

self.property_topic = f'iot/{self.SF_PRODUCT_ID}/{self.deviceId}/properties/write'
self.property_topic = f'iot/{self.productId}/{self.deviceId}/properties/write'
self.chargeThrough = True
self.dryrun = False
self.sunriseSoC = None
Expand All @@ -70,19 +74,19 @@ def __str__(self):
B:{self.electricLevel:>3}% ({batteries_soc}), \
V:{(sum(self.batteriesVol.values()) / len(self.batteriesVol)):2.1f}V ({batteries_vol}), \
C:{self.outputPackPower-self.packInputPower:>4}W, \
P:{self.bypass} ({"auto" if self.bypass_mode == 0 else "manual"}, {"possible" if self.allow_bypass else "not possible"}), \
P:{self.getBypass()} ({"auto" if self.bypass_mode == 0 else "manual"}, {"possible" if self.allow_bypass else "not possible"}), \
F:{self.getLastFullBattery():3.1f}h, \
E:{self.getLastEmptyBattery():3.1f}h, \
H:{self.outputHomePower:>3}W, \
L:{self.outputLimit:>3}W{reset}'.split())

def update(self):
log.info(f'Triggering telemetry update: iot/{self.SF_PRODUCT_ID}/{self.deviceId}/properties/read')
self.client.publish(f'iot/{self.SF_PRODUCT_ID}/{self.deviceId}/properties/read','{"properties": ["getAll"]}')
log.info(f'Triggering telemetry update: iot/{self.productId}/{self.deviceId}/properties/read')
self.client.publish(f'iot/{self.productId}/{self.deviceId}/properties/read','{"properties": ["getAll"]}')

def subscribe(self):
topics = [
f'/{self.SF_PRODUCT_ID}/{self.deviceId}/properties/report',
f'/{self.productId}/{self.deviceId}/properties/report',
f'solarflow-hub/{self.deviceId}/telemetry/solarInputPower',
f'solarflow-hub/{self.deviceId}/telemetry/electricLevel',
f'solarflow-hub/{self.deviceId}/telemetry/outputPackPower',
Expand All @@ -104,14 +108,22 @@ def subscribe(self):
def ready(self):
return (self.electricLevel > -1 and self.solarInputPower > -1)

def timesync(self, ts):
payload = {
"zoneOffset": "+00:00",
"messageId": 123,
"timestamp": ts
}
self.client.publish(f'iot/{self.productId}/{self.deviceId}/time-sync/reply',json.dumps(payload))

def pushHomeassistantConfig(self):
log.info("Publishing Homeassistant templates...")
hatemplates = [f for f in pathlib.Path().glob("homeassistant/*.json")]
environment = Environment(loader=FileSystemLoader("homeassistant/"), undefined=DebugUndefined)

for hatemplate in hatemplates:
template = environment.get_template(hatemplate.name)
hacfg = template.render(product_id=self.SF_PRODUCT_ID, device_id=self.deviceId, fw_version=self.fwVersion)
hacfg = template.render(product_id=self.productId, device_id=self.deviceId, fw_version=self.fwVersion)
cfg_type = hatemplate.name.split(".")[0]
cfg_name = hatemplate.name.split(".")[1]
self.client.publish(f'homeassistant/{cfg_type}/solarflow-hub-{self.deviceId}-{cfg_name}/config',hacfg)
Expand Down Expand Up @@ -185,6 +197,10 @@ def updMasterSoftVersion(self, value:int):
# self.pushHomeassistantConfig() # why here? this is not needed every minute

def updByPass(self, value:int):
# Hub2000 doesn't report bypass via pass property only when in auto mode?
# see: https://github.com/reinhard-brandstaedter/solarflow-control/issues/244#issuecomment-2152861536
if self.productId == HUB2000 and self.bypass_mode != 0:
return
self.bypass = bool(value)

def updByPassMode(self, value: int):
Expand All @@ -193,6 +209,9 @@ def updByPassMode(self, value: int):
self.setBypass(False)
value = 1

if self.productId == HUB2000:
self.bypass = value==2

self.bypass_mode = value

def allowBypass(self, allow):
Expand All @@ -204,6 +223,11 @@ def setChargeThrough(self, value):
if type(value) == int:
self.chargeThrough = bool(value)
log.info(f'Set ChargeThrough: {self.chargeThrough}')
# in case of setups with no direct panels connected to inverter it is necessary to turn on the inverter as it is likely offline now
inv = self.client._userdata['dtu']
if (not inv.ready()) and self.getOutputHomePower() == 0:
# this will power on the inverter so that control can resume from an interrupted charge-through
self.setOutputLimit(30)

def setDryRun(self,value):
if type(value) == str:
Expand Down Expand Up @@ -238,7 +262,7 @@ def getNightConsumption(self):
# handle content of mqtt message and update properties accordingly
def handleMsg(self, msg):
# transform the original messages sent by the SF hub into a better readable format
if self.SF_PRODUCT_ID in msg.topic:
if self.productId in msg.topic:
device_id = msg.topic.split('/')[2]
payload = json.loads(msg.payload.decode())
if "properties" in payload:
Expand Down Expand Up @@ -325,7 +349,7 @@ def setOutputLimit(self, limit:int):
# Hence setting the output limit 0 if SoC 0%
if self.electricLevel == 0:
limit = 0
log.info(f'Battery is empty! Disabling solaraflow output, setting limit to {limit}')
log.info(f'Battery is empty! Disabling solarflow output, setting limit to {limit}')


# Charge-Through:
Expand Down Expand Up @@ -365,6 +389,12 @@ def setOutputLimit(self, limit:int):
def setBuzzer(self, state: bool):
buzzer = {"properties": { "buzzerSwitch": 0 if not state else 1 }}
self.client.publish(self.property_topic,json.dumps(buzzer))
log.info(f'Turning hub buzzer {"ON" if state else "OFF"}')

def setAutorecover(self, state: bool):
autorecover = {"properties": { "autoRecover": 0 if not state else 1 }}
self.client.publish(self.property_topic,json.dumps(autorecover))
log.info(f'Turning hub bypass autorecover {"ON" if state else "OFF"}')

def setBypass(self, state: bool):
passmode = {"properties": { "passMode": 2 if state else 1 }}
Expand Down Expand Up @@ -411,8 +441,11 @@ def getLimit(self):
return self.outputLimit

def getBypass(self):
return self.bypass

if self.productId == HUB2000:
return self.bypass_mode == 2 or self.bypass
else:
return self.bypass

def getCanDischarge(self):
fullage = self.getLastFullBattery()
can_discharge = (self.batteryTarget == "discharging") or (self.batteryTarget == "charging" and fullage < self.fullChargeInterval)
Expand Down
24 changes: 0 additions & 24 deletions src/solarflow/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
from threading import Timer
import logging
import sys
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

FORMAT = '%(asctime)s:%(levelname)s: %(message)s'
logging.basicConfig(stream=sys.stdout, level="INFO", format=FORMAT)
Expand Down Expand Up @@ -119,26 +115,6 @@ def populate(self, duration, value):
self.values = []
for s in range(duration,-1,-1):
self.values.append((now-timedelta(seconds=s),value))

def predict(self) -> []:
if len(self.aggregated_values) >= 5:
data = {'X': [i for i,v in enumerate(self.values)],
'y': [v[1] for i,v in enumerate(self.values)]}
df = pd.DataFrame(data)
X = df["X"]
X = np.array(X).reshape(-1,1)
y = df["y"]

model = LinearRegression()
model.fit(X,y)

y_pred = model.predict(np.array([[6]]))
log.debug(f'prediction of {self}: {y_pred}')

return list(map(lambda x: round(x,1), y_pred))
else:
return [self.aggregated_values[-1]] if len(self.aggregated_values) > 0 else [0]


def deep_get(dictionary, keys, default=None):
return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

0 comments on commit 0da2925

Please sign in to comment.