Skip to content

Commit 1563eef

Browse files
authored
Merge branch 'master' into issue-55-Avg_rate_improvements
2 parents c66ff62 + 9f6fb5e commit 1563eef

3 files changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
class AgileConsumptionCard extends HTMLElement {
2+
set hass(hass) {
3+
if (!this.content) {
4+
const card = document.createElement('ha-card');
5+
card.header = this.title;
6+
this.content = document.createElement('div');
7+
this.content.style.padding = '0 16px 16px';
8+
9+
const style = document.createElement('style');
10+
style.textContent = `
11+
table {
12+
width: 100%;
13+
padding: 0px;
14+
spacing: 0px;
15+
font-size: 12px;
16+
}
17+
table.sub_table {
18+
border-collapse: seperate;
19+
border-spacing: 0px 2px;
20+
}
21+
table.main {
22+
padding: 0px;
23+
}
24+
thead th {
25+
text-align: left;
26+
padding: 0px;
27+
}
28+
td {
29+
vertical-align: top;
30+
padding: 2px;
31+
spacing: 0px;
32+
}
33+
tr.rate_row{
34+
text-align:center;
35+
width:80px;
36+
}
37+
td.time {
38+
text-align:center;
39+
vertical-align: middle;
40+
}
41+
td.time_red{
42+
border-bottom: 1px solid Tomato;
43+
}
44+
td.time_orange{
45+
border-bottom: 1px solid orange;
46+
}
47+
td.time_green{
48+
border-bottom: 1px solid MediumSeaGreen;
49+
}
50+
td.time_blue{
51+
border-bottom: 1px solid #391CD9;
52+
}
53+
td.rate {
54+
color:white;
55+
text-align:center;
56+
vertical-align: middle;
57+
width:80px;
58+
}
59+
td.red {
60+
border: 2px solid Tomato;
61+
background-color: Tomato;
62+
}
63+
td.orange {
64+
border: 2px solid orange;
65+
background-color: orange;
66+
}
67+
td.green {
68+
border: 2px solid MediumSeaGreen;
69+
background-color: MediumSeaGreen;
70+
}
71+
td.blue {
72+
border: 2px solid #391CD9;
73+
background-color: #391CD9;
74+
}
75+
`;
76+
card.appendChild(style);
77+
card.appendChild(this.content);
78+
this.appendChild(card);
79+
}
80+
81+
const entityId = this.config.entity;
82+
const state = hass.states[entityId];
83+
const attributes = this.reverseObject(state.attributes);
84+
const stateStr = state ? state.state : 'unavailable';
85+
var tables = "";
86+
const rates_list_length = Object.keys(attributes).length;
87+
const rows_per_col = Math.ceil(rates_list_length / this.cols);
88+
tables = tables.concat("<td><table class='sub_table'><tbody>");
89+
var table = ""
90+
var x = 1;
91+
const mediumlimit = this.mediumlimit;
92+
const highlimit = this.highlimit;
93+
const unitstr = this.unitstr;
94+
const roundUnits = this.roundUnits
95+
96+
Object.keys(attributes).reverse().forEach(function (key) {
97+
var colour = "green";
98+
if(attributes[key] > highlimit) colour = "red";
99+
else if(attributes[key] > mediumlimit) colour = "orange";
100+
else if(attributes[key] <= 0 ) colour = "blue";
101+
table = table.concat("<tr class='rate_row'><td class='time time_"+colour+"'>" + key + "</td><td class='rate "+colour+"'>" + attributes[key].toFixed(roundUnits) + unitstr + "</td></tr>");
102+
if (x % rows_per_col == 0) {
103+
tables = tables.concat(table);
104+
table = "";
105+
if (rates_list_length != x) {
106+
tables = tables.concat("</tbody></table></td>");
107+
tables = tables.concat("<td><table class='sub_table'><tbody>");
108+
}
109+
};
110+
x++;
111+
112+
});
113+
tables = tables.concat(table);
114+
tables = tables.concat("</tbody></table></td>");
115+
116+
this.content.innerHTML = `
117+
<table class="main">
118+
<tr>
119+
${tables}
120+
</tr>
121+
</table>
122+
`;
123+
}
124+
125+
126+
reverseObject(object) {
127+
var newObject = {};
128+
var keys = [];
129+
130+
for (var key in object) {
131+
keys.push(key);
132+
}
133+
134+
for (var i = keys.length - 1; i >= 0; i--) {
135+
var value = object[keys[i]];
136+
newObject[keys[i]] = value;
137+
}
138+
139+
return newObject;
140+
}
141+
142+
setConfig(config) {
143+
if (!config.entity) {
144+
throw new Error('You need to define an entity');
145+
}
146+
147+
148+
this.config = config;
149+
if (!config.cols) {
150+
this.cols = 1;
151+
}
152+
else {
153+
this.cols = config.cols;
154+
}
155+
156+
if (!config.title) {
157+
this.title = 'Agile Consumption';
158+
}
159+
else {
160+
this.title = config.title;
161+
}
162+
163+
if (!config.mediumlimit) {
164+
this.mediumlimit = 10;
165+
}
166+
else {
167+
this.mediumlimit = config.mediumlimit;
168+
}
169+
170+
if (!config.highlimit) {
171+
this.highlimit = 15;
172+
}
173+
else {
174+
this.highlimit = config.highlimit;
175+
}
176+
177+
if (!config.roundUnits) {
178+
this.roundUnits = 2;
179+
}
180+
else {
181+
this.roundUnits = config.roundUnits;
182+
}
183+
184+
if(!config.showunits) {
185+
this.unitstr = "kWh";
186+
}
187+
else {
188+
if(config.showunits == "true") this.unitstr = "kWh";
189+
else this.unitstr = "";
190+
}
191+
}
192+
193+
// The height of your card. Home Assistant uses this to automatically
194+
// distribute all cards over the available columns.
195+
getCardSize() {
196+
return 3;
197+
}
198+
}
199+
200+
customElements.define('agile-consumption-card', AgileConsumptionCard);

custom_components/octopusagile/OctopusAgile/Agile.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,38 @@ def get_consumption(self, start, end):
335335
consumption = requests.get(url=consumption_url_str, auth=(self.auth, ''))
336336
return consumption.json()
337337

338+
def aggregate_consumption(self):
339+
week_days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
340+
341+
end = datetime.now().date()
342+
start = end - timedelta(days=30)
343+
344+
data = self.get_consumption(start, end)
345+
hourly = {}
346+
daily = {}
347+
hour_daily = {}
348+
349+
for record in data['results']:
350+
start = dateutil.parser.parse(record['interval_start'])
351+
if start.minute not in [0, 30]: continue
352+
353+
if f"{start.hour}:{start.minute:02}" not in hourly:
354+
hourly[f"{start.hour}:{start.minute:02}"] = []
355+
hourly[f"{start.hour}:{start.minute:02}"].append(record['consumption'])
356+
357+
if week_days[start.weekday()] not in daily:
358+
daily[week_days[start.weekday()]] = []
359+
daily[week_days[start.weekday()]].append(record['consumption'] * 48)
360+
361+
if f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}" not in hour_daily:
362+
hour_daily[f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}"] = []
363+
hour_daily[f"{week_days[start.weekday()]} {start.hour}:{start.minute:02}"].append(record['consumption'])
364+
365+
for d in [hourly, daily, hour_daily]:
366+
for key, value in d.items():
367+
d[key]=round(sum(value) / len(value), 3)
368+
return hourly, daily, hour_daily
369+
338370
def calculcate_cost(self, start, end):
339371
jconsumption = self.get_consumption(start, end)
340372
jcost = self.get_raw_rates_json(f"{start.isoformat()}T00:00:00Z", f"{end.isoformat()}T23:59:59Z")

custom_components/octopusagile/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,10 @@ def handle_update_consumption(call):
371371
round(monthlycost/100, 2),
372372
attributes={'unit_of_measurement': '£',
373373
'icon': 'mdi:cash'})
374+
hourly, daily, hour_daily = myrates.aggregate_consumption()
375+
hass.states.set(f"octopusagile.hourly_consumption", "", hourly)
376+
hass.states.set(f"octopusagile.daily_consumption", "", daily)
377+
hass.states.set(f"octopusagile.hour_daily_consumption", "", hour_daily)
374378

375379
def half_hour_timer(nowtime):
376380
roundedtime = myrates.round_time(nowtime)

0 commit comments

Comments
 (0)