Skip to content

Commit

Permalink
Enhance OWM
Browse files Browse the repository at this point in the history
  • Loading branch information
javalikescript committed Nov 30, 2024
1 parent f683a5f commit fb1f4ca
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 74 deletions.
101 changes: 101 additions & 0 deletions extensions/owm/adapter.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
local List = require('jls.util.List')
local tables = require('jls.util.tables')

local utils = require('lha.utils')

-- temp_min temp_max
-- sys.sunrise: 1485720272
-- sys.sunset: 1485766550
-- city.name: "Paris"

local FIELD_MAP = {
temperature = 'main/temp',
humidity = 'main/humidity',
pressure = 'main/pressure',
cloud = 'clouds/all',
windSpeed = 'wind/speed',
windDirection = 'wind/deg',
rain = 'rain/3h', -- Rain volume for last 3 hours
}

local CUMULATIVE_FIELD_MAP = {
rain = true,
}

local function adapt(w, d)
local a = {}
for k, p in pairs(FIELD_MAP) do
a[k] = tables.getPath(w, p, d)
end
a.date = utils.timeToString(w.dt)
return a
end

local function adaptNil(w)
return adapt(w)
end

local function sumFields(a, w)
for k, v in pairs(w) do
if type(v) == 'number' then
a[k] = (a[k] or 0) + v
else
a[k] = v
end
end
return a
end

local function range(days, from, to, time)
local t = time or os.time()
local d = os.date('*t', t + 86400 * (days or 0))
d.min = 15
d.sec = 0
d.hour = from or 0
local ft = os.time(d)
d.hour = to or 23
local tt = os.time(d)
return function(w)
return w.dt >= t and w.dt >= ft and w.dt < tt
end
end

local function ranges(from, to, time)
local t = time or os.time()
local r3 = range(2, from, to, t)
local r4 = range(3, from, to, t)
local r5 = range(4, from, to, t)
return function(w)
return r3(w) or r4(w) or r5(w)
end
end

local function aggregate(list)
local w = List.reduce(List.map(list, adaptNil), sumFields, adapt({}, 0))
local a, n = {}, #list
for k, v in pairs(w) do
if type(v) == 'number' and not CUMULATIVE_FIELD_MAP[k] then
a[k] = (v * 100 // n) / 100
else
a[k] = v
end
end
return a
end

-- 5 day forecast includes weather forecast data with 3-hour step

return {
computeCurrent = function(weather)
return adapt(weather)
end,
computeNextHours = function(forecast, time)
return aggregate(List.filter(forecast.list, range(0, 7, 19, time)))
end,
computeTomorrow = function(forecast, time)
return aggregate(List.filter(forecast.list, range(1, 7, 19, time)))
end,
computeNextDays = function(forecast, time)
return aggregate(List.filter(forecast.list, ranges(7, 19, time)))
end
}
24 changes: 20 additions & 4 deletions extensions/owm/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,20 @@
"writeOnly": true,
"required": true
},
"cityId": {
"title": "City ID",
"type": "string",
"latitude": {
"title": "Latitude",
"type": "number",
"default": 49.181,
"minimum": -90,
"maximum": 90,
"required": true
},
"longitude": {
"title": "Longitude",
"type": "number",
"default": -0.370,
"minimum": -180,
"maximum": 180,
"required": true
},
"units": {
Expand All @@ -33,7 +44,12 @@
"title": "Units of measurement",
"type": "string"
},
"maxPollingDelay": {
"lang": {
"title": "language",
"type": "string",
"pattern": "^%a%a_?%a*$"
},
"maxPollingDelay": {
"title": "Minimum Call Interval in seconds",
"type": "integer",
"default": 600,
Expand Down
99 changes: 99 additions & 0 deletions extensions/owm/owm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
define(['./owm.xml'], function(owmTemplate) {

var unitAlias = app.getUnitAliases();
var directionLabels = ['-', 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
var directions = ['\u21BB', '\u2191', '\u2197', '\u2192', '\u2198', '\u2193', '\u2199', '\u2190', '\u2196'];

function compare(a, b) {
return a === b ? 0 : (a > b ? 1 : -1);
}
function compareTimes(a, b) {
return compare(a.time, b.time);
}
function formatIcon(props) {
if (props.rain) {
if (props.rain > 10) {
return 'cloud-showers-heavy';
}
return props.cloud < 66 ? 'cloud-sun-rain' : 'cloud-rain';
} else if (props.cloud > 33) {
return props.cloud < 66 ? 'cloud-sun' : 'cloud';
}
return 'sun';
}
function formatDirection(direction, speed, label) {
var i = 0;
if (typeof speed !== 'number' || speed > 0) {
i = Math.round(direction / 45) % 8 + 1;
}
if (label) {
return directionLabels[i];
}
return directions[i];
}

var owmVue = new Vue({
template: owmTemplate,
data: {
empty: true,
unit: {},
times: []
},
methods: {
onDataChange: function() {
Promise.all([
app.getThings(),
app.getPropertiesByThingId()
]).then(apply(this, function(things, properties) {
this.refresh(things, properties);
}));
},
onShow: function() {
this.onDataChange();
},
formatDirection: function(d) {
return formatDirection(d);
},
extractUnits: function(thing) {
for (var name in thing.properties) {
var unit = thing.properties[name].unit;
if (unit) {
this.unit[name] = unitAlias[unit] || unit;
}
}
},
refresh: function(things, properties) {
var now = Date.now();
var times = [];
for (var i = 0; i < things.length; i++) {
var thing = things[i];
if (thing.extensionId === 'owm') {
if (this.empty) {
this.empty = false;
this.extractUnits(thing);
//console.info('units:', this.unit);
}
var props = properties[thing.thingId];
if (props) {
var t = props.date ? new Date(props.date).getTime() : 0;
var h = t > now ? Math.floor((t - now) / 3600000) : 0;
var item = Object.assign({
title: thing.title,
label: h < 24 ? (h + 'h'): (Math.floor(h / 24) + 'd'),
time: t,
faIcon: 'fa-' + formatIcon(props)
}, props);
times.push(item);
}
}
}
times.sort(compareTimes);
//console.info('times:', times);
this.times = times;
}
}
});

addPageComponent(owmVue, 'fa-umbrella');

});
Loading

0 comments on commit fb1f4ca

Please sign in to comment.