From 10af4d3d28c1e533627c17a45f9d5997bb0316e5 Mon Sep 17 00:00:00 2001 From: Chris Veilleux Date: Thu, 18 Mar 2021 16:25:10 -0500 Subject: [PATCH] refactor weather skill --- __init__.py | 2155 ++++------------- dialog/en-us/RelativeDay.voc | 6 - dialog/en-us/and.dialog | 1 - ...time.forecast.affirmative.condition.dialog | 3 - .../at.time.forecast.cond.alternative.dialog | 3 - ...orecast.local.affirmative.condition.dialog | 3 - ...ime.forecast.local.cond.alternative.dialog | 3 - ...me.forecast.local.no.cond.predicted.dialog | 2 - .../at.time.forecast.no.cond.predicted.dialog | 3 - .../at.time.local.high.temperature.dialog | 2 - .../at.time.local.low.temperature.dialog | 2 - .../at.time.local.no.cond.predicted.dialog | 2 - dialog/en-us/at.time.local.temperature.dialog | 2 - dialog/en-us/at.time.local.weather.dialog | 4 - dialog/en-us/at.time.no.cond.predicted.dialog | 2 - dialog/en-us/condition.category.value | 15 - ...current.clear.alternative.location.dialog} | 0 ...> current.clear.not.expected.local.dialog} | 0 ...urrent.clear.not.expected.location.dialog} | 0 ...urrent.clouds.alternative.location.dialog} | 0 ... current.clouds.not.expected.local.dialog} | 0 ...rrent.clouds.not.expected.location.dialog} | 0 ...> current.condition.expected.local.dialog} | 0 ...urrent.condition.expected.location.dialog} | 0 ...g => current.fog.alternative.local.dialog} | 0 ...> current.fog.alternative.location.dialog} | 0 ... => current.fog.not.expected.local.dialog} | 0 ... current.fog.not.expected.location.dialog} | 0 dialog/en-us/current.high.temperature.dialog | 4 - dialog/en-us/current.hot.dialog | 2 - dialog/en-us/current.humidity.local.dialog | 1 + dialog/en-us/current.humidity.location.dialog | 1 + dialog/en-us/current.local.cold.dialog | 2 - .../current.local.high.temperature.dialog | 4 - dialog/en-us/current.local.hot.dialog | 2 - .../current.local.low.temperature.dialog | 4 - dialog/en-us/current.local.temperature.dialog | 4 - dialog/en-us/current.local.weather.dialog | 3 - dialog/en-us/current.low.temperature.dialog | 2 - ... => current.rain.alternative.local.dialog} | 0 ... current.rain.alternative.location.dialog} | 0 ...=> current.rain.not.expected.local.dialog} | 0 ...current.rain.not.expected.location.dialog} | 0 ... => current.snow.alternative.local.dialog} | 0 ... current.snow.alternative.location.dialog} | 0 ...=> current.snow.not.expected.local.dialog} | 0 ...current.snow.not.expected.location.dialog} | 0 .../en-us/current.sunrise.future.local.dialog | 2 + .../current.sunrise.future.location.dialog | 2 + ...alog => current.sunrise.past.local.dialog} | 0 .../current.sunrise.past.location.dialog | 2 + ...log => current.sunset.future.local.dialog} | 0 .../current.sunset.future.location.dialog | 3 + dialog/en-us/current.sunset.past.local.dialog | 3 + .../en-us/current.sunset.past.location.dialog | 3 + dialog/en-us/current.temperature.dialog | 4 - .../current.temperature.high.local.dialog | 4 + .../current.temperature.high.location.dialog | 4 + .../en-us/current.temperature.high.low.dialog | 1 + dialog/en-us/current.temperature.local.dialog | 4 + .../en-us/current.temperature.location.dialog | 4 + .../current.temperature.low.local.dialog | 4 + .../current.temperature.low.location.dialog | 2 + ...ent.thunderstorm.alternative.local.dialog} | 0 ....thunderstorm.alternative.location.dialog} | 0 ...nt.thunderstorm.not.expected.local.dialog} | 0 ...thunderstorm.not.expected.location.dialog} | 0 dialog/en-us/current.weather.dialog | 3 - dialog/en-us/current.weather.local.dialog | 3 + dialog/en-us/current.weather.location.dialog | 3 + dialog/en-us/current.wind.light.local.dialog | 2 + .../en-us/current.wind.light.location.dialog | 2 + .../en-us/current.wind.moderate.local.dialog | 2 + .../current.wind.moderate.location.dialog | 2 + dialog/en-us/current.wind.strong.local.dialog | 3 + .../en-us/current.wind.strong.location.dialog | 3 + ... currrent.clouds.alternative.local.dialog} | 0 ...g => daily.clear.alternative.local.dialog} | 0 ...> daily.clear.alternative.location.dialog} | 0 ... => daily.clear.not.expected.local.dialog} | 0 ... daily.clear.not.expected.location.dialog} | 0 ... => daily.clouds.alternative.local.dialog} | 0 ... daily.clouds.alternative.location.dialog} | 0 ...=> daily.clouds.not.expected.local.dialog} | 0 ...daily.clouds.not.expected.location.dialog} | 0 ... => daily.condition.expected.local.dialog} | 0 ... daily.condition.expected.location.dialog} | 0 ...log => daily.fog.alternative.local.dialog} | 0 ... => daily.fog.alternative.location.dialog} | 0 ...og => daily.fog.not.expected.local.dialog} | 0 ...=> daily.fog.not.expected.location.dialog} | 0 dialog/en-us/daily.humidity.local.dialog | 2 + dialog/en-us/daily.humidity.location.dialog | 2 + .../daily.precipitation.next.local.dialog | 2 + .../daily.precipitation.next.location.dialog | 2 + ...daily.precipitation.next.none.local.dialog | 2 + ...ly.precipitation.next.none.location.dialog | 2 + ...og => daily.rain.alternative.local.dialog} | 0 ...=> daily.rain.alternative.location.dialog} | 0 ...g => daily.rain.not.expected.local.dialog} | 0 ...> daily.rain.not.expected.location.dialog} | 0 ...=> daily.snow.alternative.location.dialog} | 0 ...g => daily.snow.not.expected.local.dialog} | 0 ...> daily.snow.not.expected.location.dialog} | 0 dialog/en-us/daily.sunrise.local.dialog | 3 + dialog/en-us/daily.sunrise.location.dialog | 3 + dialog/en-us/daily.sunset.local.dialog | 4 + dialog/en-us/daily.sunset.location.dialog | 3 + .../en-us/daily.temperature.high.local.dialog | 2 + .../daily.temperature.high.location.dialog | 2 + dialog/en-us/daily.temperature.local.dialog | 2 + .../en-us/daily.temperature.location.dialog | 2 + .../en-us/daily.temperature.low.local.dialog | 2 + .../daily.temperature.low.location.dialog | 2 + ...ily.thunderstorm.alternative.local.dialog} | 0 ....thunderstorm.alternative.location.dialog} | 0 ...ly.thunderstorm.not.expected.local.dialog} | 0 ...thunderstorm.not.expected.location.dialog} | 0 dialog/en-us/daily.weather.local.dialog | 5 + dialog/en-us/daily.weather.location.dialog | 3 + dialog/en-us/daily.wind.light.local.dialog | 2 + dialog/en-us/daily.wind.light.location.dialog | 2 + dialog/en-us/daily.wind.moderate.local.dialog | 3 + .../en-us/daily.wind.moderate.location.dialog | 3 + dialog/en-us/daily.wind.strong.local.dialog | 2 + .../en-us/daily.wind.strong.location.dialog | 2 + dialog/en-us/{E.dialog => east.dialog} | 0 dialog/en-us/forecast.hard.wind.dialog | 2 - dialog/en-us/forecast.high.temperature.dialog | 2 - dialog/en-us/forecast.hot.dialog | 2 - dialog/en-us/forecast.light.wind.dialog | 2 - dialog/en-us/forecast.local.hard.wind.dialog | 2 - .../forecast.local.high.temperature.dialog | 2 - dialog/en-us/forecast.local.hot.dialog | 1 - dialog/en-us/forecast.local.light.wind.dialog | 2 - .../forecast.local.low.temperature.dialog | 2 - .../en-us/forecast.local.medium.wind.dialog | 3 - .../en-us/forecast.local.temperature.dialog | 2 - dialog/en-us/forecast.local.weather.dialog | 5 - dialog/en-us/forecast.low.temperature.dialog | 2 - dialog/en-us/forecast.medium.wind.dialog | 3 - dialog/en-us/forecast.temperature.dialog | 2 - dialog/en-us/forecast.weather.dialog | 3 - dialog/en-us/hard.wind.dialog | 3 - dialog/en-us/hour.local.weather.dialog | 4 - dialog/en-us/hour.weather.dialog | 4 - ...hourly.condition.alternative.local.dialog} | 0 ...rly.condition.alternative.location.dialog} | 0 ...=> hourly.condition.expected.local.dialog} | 0 ...hourly.condition.expected.location.dialog} | 0 .../hourly.precipitation.next.local.dialog | 2 + .../hourly.precipitation.next.location.dialog | 2 + dialog/en-us/hourly.temperature.local.dialog | 2 + .../en-us/hourly.temperature.location.dialog | 2 + dialog/en-us/hourly.weather.local.dialog | 4 + dialog/en-us/hourly.weather.location.dialog | 4 + dialog/en-us/light.wind.dialog | 2 - dialog/en-us/local.hard.wind.dialog | 3 - dialog/en-us/local.light.wind.dialog | 2 - dialog/en-us/local.medium.wind.dialog | 2 - dialog/en-us/location.not.found.dialog | 5 +- dialog/en-us/medium.wind.dialog | 2 - dialog/en-us/min.max.dialog | 1 - dialog/en-us/no precipitation expected.dialog | 2 - dialog/en-us/{N.dialog => north.dialog} | 0 dialog/en-us/{NE.dialog => northeast.dialog} | 0 dialog/en-us/{NW.dialog => northwest.dialog} | 0 dialog/en-us/on.date.dialog | 1 - dialog/en-us/on.dialog | 1 - dialog/en-us/percentage.number.dialog | 2 +- dialog/en-us/precipitation expected.dialog | 2 - .../en-us/report.condition.at.location.dialog | 1 - dialog/en-us/report.condition.dialog | 1 - ...report.condition.future.at.location.dialog | 2 - dialog/en-us/report.condition.future.dialog | 2 - dialog/en-us/report.wind.dialog | 1 - dialog/en-us/seven.days.available.dialog | 2 + dialog/en-us/{S.dialog => south.dialog} | 0 dialog/en-us/{SE.dialog => southeast.dialog} | 0 dialog/en-us/{SW.dialog => southwest.dialog} | 0 dialog/en-us/tonight.local.weather.dialog | 4 - dialog/en-us/weekly.condition.on.day.dialog | 1 - .../en-us/weekly.conditions.mostly.one.dialog | 1 - .../en-us/weekly.conditions.seq.extra.dialog | 2 - .../en-us/weekly.conditions.seq.period.dialog | 1 - .../en-us/weekly.conditions.seq.start.dialog | 1 - .../en-us/weekly.conditions.some.days.dialog | 1 - dialog/en-us/weekly.temp.range.dialog | 1 - dialog/en-us/{W.dialog => west.dialog} | 0 dialog/en-us/wind.speed.dialog | 1 - dialog/en-us/wind.speed.dir.dialog | 2 - dialog/en-us/wind.strength.hard.dialog | 1 - dialog/en-us/wind.strength.light.dialog | 3 - dialog/en-us/wind.strength.medium.dialog | 2 - dialog/en-us/winds.dialog | 1 - source/__init__.py | 14 + source/api.py | 28 + source/config.py | 55 + source/dialog.py | 226 ++ source/intent.py | 64 + source/util.py | 93 + source/weather.py | 209 ++ test/behave/current-temperature-local.feature | 44 + .../current-temperature-location.feature | 47 + test/behave/current-weather-local.feature | 29 + test/behave/current-weather-location.feature | 40 + test/behave/daily-temperature-local.feature | 97 + test/behave/daily-weather-local.feature | 25 + test/behave/daily-weather-location.feature | 14 + test/behave/weather-local.feature | 239 -- test/behave/weather-location.feature | 115 - test/behave/weather-precipitation.feature | 172 +- .../this.week.dialog => test/unit/__init__.py | 0 vocab/en-us/{Cloudy.voc => Clouds.voc} | 0 ...lternatives.voc => CloudsAlternatives.voc} | 0 vocab/en-us/{Foggy.voc => Fog.voc} | 0 vocab/en-us/Like.voc | 1 + vocab/en-us/Location.voc | 3 +- vocab/en-us/Outside.voc | 2 + vocab/en-us/{Raining.voc => Rain.voc} | 0 vocab/en-us/RelativeDay.voc | 10 +- vocab/en-us/{Snowing.voc => Snow.voc} | 0 vocab/en-us/Temperature.voc | 1 + vocab/en-us/{Storm.voc => Thunderstorm.voc} | 0 ...tives.voc => ThunderstormAlternatives.voc} | 0 vocab/en-us/what.is.multi.day.forecast.intent | 6 +- vocab/en-us/what.is.three.day.forecast.intent | 2 - ...what.is.three.day.forecast.location.intent | 1 - vocab/en-us/what.is.two.day.forecast.intent | 3 - vocab/en-us/whats.weather.like.intent | 5 - 230 files changed, 1699 insertions(+), 2327 deletions(-) delete mode 100644 dialog/en-us/RelativeDay.voc delete mode 100644 dialog/en-us/and.dialog delete mode 100644 dialog/en-us/at.time.forecast.affirmative.condition.dialog delete mode 100644 dialog/en-us/at.time.forecast.cond.alternative.dialog delete mode 100644 dialog/en-us/at.time.forecast.local.affirmative.condition.dialog delete mode 100644 dialog/en-us/at.time.forecast.local.cond.alternative.dialog delete mode 100644 dialog/en-us/at.time.forecast.local.no.cond.predicted.dialog delete mode 100644 dialog/en-us/at.time.forecast.no.cond.predicted.dialog delete mode 100644 dialog/en-us/at.time.local.high.temperature.dialog delete mode 100644 dialog/en-us/at.time.local.low.temperature.dialog delete mode 100644 dialog/en-us/at.time.local.no.cond.predicted.dialog delete mode 100644 dialog/en-us/at.time.local.temperature.dialog delete mode 100644 dialog/en-us/at.time.local.weather.dialog delete mode 100644 dialog/en-us/at.time.no.cond.predicted.dialog delete mode 100644 dialog/en-us/condition.category.value rename dialog/en-us/{clear.alternative.dialog => current.clear.alternative.location.dialog} (100%) rename dialog/en-us/{local.clear.alternative.dialog => current.clear.not.expected.local.dialog} (100%) rename dialog/en-us/{no.clear.predicted.dialog => current.clear.not.expected.location.dialog} (100%) rename dialog/en-us/{cloudy.alternative.dialog => current.clouds.alternative.location.dialog} (100%) rename dialog/en-us/{local.no.cloudy.predicted.dialog => current.clouds.not.expected.local.dialog} (100%) rename dialog/en-us/{no.cloudy.predicted.dialog => current.clouds.not.expected.location.dialog} (100%) rename dialog/en-us/{local.affirmative.condition.dialog => current.condition.expected.local.dialog} (100%) rename dialog/en-us/{affirmative.condition.dialog => current.condition.expected.location.dialog} (100%) rename dialog/en-us/{local.foggy.alternative.dialog => current.fog.alternative.local.dialog} (100%) rename dialog/en-us/{fog.alternative.dialog => current.fog.alternative.location.dialog} (100%) rename dialog/en-us/{local.no.fog.predicted.dialog => current.fog.not.expected.local.dialog} (100%) rename dialog/en-us/{no.fog.predicted.dialog => current.fog.not.expected.location.dialog} (100%) delete mode 100644 dialog/en-us/current.high.temperature.dialog delete mode 100644 dialog/en-us/current.hot.dialog create mode 100644 dialog/en-us/current.humidity.local.dialog create mode 100644 dialog/en-us/current.humidity.location.dialog delete mode 100644 dialog/en-us/current.local.cold.dialog delete mode 100644 dialog/en-us/current.local.high.temperature.dialog delete mode 100644 dialog/en-us/current.local.hot.dialog delete mode 100644 dialog/en-us/current.local.low.temperature.dialog delete mode 100644 dialog/en-us/current.local.temperature.dialog delete mode 100644 dialog/en-us/current.local.weather.dialog delete mode 100644 dialog/en-us/current.low.temperature.dialog rename dialog/en-us/{local.raining.alternative.dialog => current.rain.alternative.local.dialog} (100%) rename dialog/en-us/{raining.alternative.dialog => current.rain.alternative.location.dialog} (100%) rename dialog/en-us/{local.no.rain.predicted.dialog => current.rain.not.expected.local.dialog} (100%) rename dialog/en-us/{no.rain.predicted.dialog => current.rain.not.expected.location.dialog} (100%) rename dialog/en-us/{local.snowing.alternative.dialog => current.snow.alternative.local.dialog} (100%) rename dialog/en-us/{snowing.alternative.dialog => current.snow.alternative.location.dialog} (100%) rename dialog/en-us/{local.no.snow.predicted.dialog => current.snow.not.expected.local.dialog} (100%) rename dialog/en-us/{no.snow.predicted.dialog => current.snow.not.expected.location.dialog} (100%) create mode 100644 dialog/en-us/current.sunrise.future.local.dialog create mode 100644 dialog/en-us/current.sunrise.future.location.dialog rename dialog/en-us/{sunrise.dialog => current.sunrise.past.local.dialog} (100%) create mode 100644 dialog/en-us/current.sunrise.past.location.dialog rename dialog/en-us/{sunset.dialog => current.sunset.future.local.dialog} (100%) create mode 100644 dialog/en-us/current.sunset.future.location.dialog create mode 100644 dialog/en-us/current.sunset.past.local.dialog create mode 100644 dialog/en-us/current.sunset.past.location.dialog delete mode 100644 dialog/en-us/current.temperature.dialog create mode 100644 dialog/en-us/current.temperature.high.local.dialog create mode 100644 dialog/en-us/current.temperature.high.location.dialog create mode 100644 dialog/en-us/current.temperature.high.low.dialog create mode 100644 dialog/en-us/current.temperature.local.dialog create mode 100644 dialog/en-us/current.temperature.location.dialog create mode 100644 dialog/en-us/current.temperature.low.local.dialog create mode 100644 dialog/en-us/current.temperature.low.location.dialog rename dialog/en-us/{local.storm.alternative.dialog => current.thunderstorm.alternative.local.dialog} (100%) rename dialog/en-us/{storm.alternative.dialog => current.thunderstorm.alternative.location.dialog} (100%) rename dialog/en-us/{local.no.storm.predicted.dialog => current.thunderstorm.not.expected.local.dialog} (100%) rename dialog/en-us/{no.storm.predicted.dialog => current.thunderstorm.not.expected.location.dialog} (100%) delete mode 100644 dialog/en-us/current.weather.dialog create mode 100644 dialog/en-us/current.weather.local.dialog create mode 100644 dialog/en-us/current.weather.location.dialog create mode 100644 dialog/en-us/current.wind.light.local.dialog create mode 100644 dialog/en-us/current.wind.light.location.dialog create mode 100644 dialog/en-us/current.wind.moderate.local.dialog create mode 100644 dialog/en-us/current.wind.moderate.location.dialog create mode 100644 dialog/en-us/current.wind.strong.local.dialog create mode 100644 dialog/en-us/current.wind.strong.location.dialog rename dialog/en-us/{local.cloudy.alternative.dialog => currrent.clouds.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.local.clear.alternative.dialog => daily.clear.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.clear.alternative.dialog => daily.clear.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.clear.predicted.dialog => daily.clear.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.clear.predicted.dialog => daily.clear.not.expected.location.dialog} (100%) rename dialog/en-us/{forecast.local.cloudy.alternative.dialog => daily.clouds.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.cloudy.alternative.dialog => daily.clouds.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.cloudy.predicted.dialog => daily.clouds.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.cloudy.predicted.dialog => daily.clouds.not.expected.location.dialog} (100%) rename dialog/en-us/{forecast.local.affirmative.condition.dialog => daily.condition.expected.local.dialog} (100%) rename dialog/en-us/{forecast.affirmative.condition.dialog => daily.condition.expected.location.dialog} (100%) rename dialog/en-us/{forecast.local.foggy.alternative.dialog => daily.fog.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.foggy.alternative.dialog => daily.fog.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.fog.predicted.dialog => daily.fog.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.fog.predicted.dialog => daily.fog.not.expected.location.dialog} (100%) create mode 100644 dialog/en-us/daily.humidity.local.dialog create mode 100644 dialog/en-us/daily.humidity.location.dialog create mode 100644 dialog/en-us/daily.precipitation.next.local.dialog create mode 100644 dialog/en-us/daily.precipitation.next.location.dialog create mode 100644 dialog/en-us/daily.precipitation.next.none.local.dialog create mode 100644 dialog/en-us/daily.precipitation.next.none.location.dialog rename dialog/en-us/{forecast.local.raining.alternative.dialog => daily.rain.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.raining.alternative.dialog => daily.rain.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.rain.predicted.dialog => daily.rain.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.rain.predicted.dialog => daily.rain.not.expected.location.dialog} (100%) rename dialog/en-us/{forecast.snowing.alternative.dialog => daily.snow.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.snow.predicted.dialog => daily.snow.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.snow.predicted.dialog => daily.snow.not.expected.location.dialog} (100%) create mode 100644 dialog/en-us/daily.sunrise.local.dialog create mode 100644 dialog/en-us/daily.sunrise.location.dialog create mode 100644 dialog/en-us/daily.sunset.local.dialog create mode 100644 dialog/en-us/daily.sunset.location.dialog create mode 100644 dialog/en-us/daily.temperature.high.local.dialog create mode 100644 dialog/en-us/daily.temperature.high.location.dialog create mode 100644 dialog/en-us/daily.temperature.local.dialog create mode 100644 dialog/en-us/daily.temperature.location.dialog create mode 100644 dialog/en-us/daily.temperature.low.local.dialog create mode 100644 dialog/en-us/daily.temperature.low.location.dialog rename dialog/en-us/{forecast.local.storm.alternative.dialog => daily.thunderstorm.alternative.local.dialog} (100%) rename dialog/en-us/{forecast.storm.alternative.dialog => daily.thunderstorm.alternative.location.dialog} (100%) rename dialog/en-us/{forecast.local.no.storm.predicted.dialog => daily.thunderstorm.not.expected.local.dialog} (100%) rename dialog/en-us/{forecast.no.storm.predicted.dialog => daily.thunderstorm.not.expected.location.dialog} (100%) create mode 100644 dialog/en-us/daily.weather.local.dialog create mode 100644 dialog/en-us/daily.weather.location.dialog create mode 100644 dialog/en-us/daily.wind.light.local.dialog create mode 100644 dialog/en-us/daily.wind.light.location.dialog create mode 100644 dialog/en-us/daily.wind.moderate.local.dialog create mode 100644 dialog/en-us/daily.wind.moderate.location.dialog create mode 100644 dialog/en-us/daily.wind.strong.local.dialog create mode 100644 dialog/en-us/daily.wind.strong.location.dialog rename dialog/en-us/{E.dialog => east.dialog} (100%) delete mode 100644 dialog/en-us/forecast.hard.wind.dialog delete mode 100644 dialog/en-us/forecast.high.temperature.dialog delete mode 100644 dialog/en-us/forecast.hot.dialog delete mode 100644 dialog/en-us/forecast.light.wind.dialog delete mode 100644 dialog/en-us/forecast.local.hard.wind.dialog delete mode 100644 dialog/en-us/forecast.local.high.temperature.dialog delete mode 100644 dialog/en-us/forecast.local.hot.dialog delete mode 100644 dialog/en-us/forecast.local.light.wind.dialog delete mode 100644 dialog/en-us/forecast.local.low.temperature.dialog delete mode 100644 dialog/en-us/forecast.local.medium.wind.dialog delete mode 100644 dialog/en-us/forecast.local.temperature.dialog delete mode 100644 dialog/en-us/forecast.local.weather.dialog delete mode 100644 dialog/en-us/forecast.low.temperature.dialog delete mode 100644 dialog/en-us/forecast.medium.wind.dialog delete mode 100644 dialog/en-us/forecast.temperature.dialog delete mode 100644 dialog/en-us/forecast.weather.dialog delete mode 100644 dialog/en-us/hard.wind.dialog delete mode 100644 dialog/en-us/hour.local.weather.dialog delete mode 100644 dialog/en-us/hour.weather.dialog rename dialog/en-us/{at.time.local.cond.alternative.dialog => hourly.condition.alternative.local.dialog} (100%) rename dialog/en-us/{at.time.cond.alternative.dialog => hourly.condition.alternative.location.dialog} (100%) rename dialog/en-us/{at.time.local.affirmative.condition.dialog => hourly.condition.expected.local.dialog} (100%) rename dialog/en-us/{at.time.affirmative.condition.dialog => hourly.condition.expected.location.dialog} (100%) create mode 100644 dialog/en-us/hourly.precipitation.next.local.dialog create mode 100644 dialog/en-us/hourly.precipitation.next.location.dialog create mode 100644 dialog/en-us/hourly.temperature.local.dialog create mode 100644 dialog/en-us/hourly.temperature.location.dialog create mode 100644 dialog/en-us/hourly.weather.local.dialog create mode 100644 dialog/en-us/hourly.weather.location.dialog delete mode 100644 dialog/en-us/light.wind.dialog delete mode 100644 dialog/en-us/local.hard.wind.dialog delete mode 100644 dialog/en-us/local.light.wind.dialog delete mode 100644 dialog/en-us/local.medium.wind.dialog delete mode 100644 dialog/en-us/medium.wind.dialog delete mode 100644 dialog/en-us/min.max.dialog delete mode 100644 dialog/en-us/no precipitation expected.dialog rename dialog/en-us/{N.dialog => north.dialog} (100%) rename dialog/en-us/{NE.dialog => northeast.dialog} (100%) rename dialog/en-us/{NW.dialog => northwest.dialog} (100%) delete mode 100644 dialog/en-us/on.date.dialog delete mode 100644 dialog/en-us/on.dialog delete mode 100644 dialog/en-us/precipitation expected.dialog delete mode 100644 dialog/en-us/report.condition.at.location.dialog delete mode 100644 dialog/en-us/report.condition.dialog delete mode 100644 dialog/en-us/report.condition.future.at.location.dialog delete mode 100644 dialog/en-us/report.condition.future.dialog delete mode 100644 dialog/en-us/report.wind.dialog create mode 100644 dialog/en-us/seven.days.available.dialog rename dialog/en-us/{S.dialog => south.dialog} (100%) rename dialog/en-us/{SE.dialog => southeast.dialog} (100%) rename dialog/en-us/{SW.dialog => southwest.dialog} (100%) delete mode 100644 dialog/en-us/tonight.local.weather.dialog delete mode 100644 dialog/en-us/weekly.condition.on.day.dialog delete mode 100644 dialog/en-us/weekly.conditions.mostly.one.dialog delete mode 100644 dialog/en-us/weekly.conditions.seq.extra.dialog delete mode 100644 dialog/en-us/weekly.conditions.seq.period.dialog delete mode 100644 dialog/en-us/weekly.conditions.seq.start.dialog delete mode 100644 dialog/en-us/weekly.conditions.some.days.dialog delete mode 100644 dialog/en-us/weekly.temp.range.dialog rename dialog/en-us/{W.dialog => west.dialog} (100%) delete mode 100644 dialog/en-us/wind.speed.dialog delete mode 100644 dialog/en-us/wind.speed.dir.dialog delete mode 100644 dialog/en-us/wind.strength.hard.dialog delete mode 100644 dialog/en-us/wind.strength.light.dialog delete mode 100644 dialog/en-us/wind.strength.medium.dialog delete mode 100644 dialog/en-us/winds.dialog create mode 100644 source/__init__.py create mode 100644 source/api.py create mode 100644 source/config.py create mode 100644 source/dialog.py create mode 100644 source/intent.py create mode 100644 source/util.py create mode 100644 source/weather.py create mode 100644 test/behave/current-temperature-local.feature create mode 100644 test/behave/current-temperature-location.feature create mode 100644 test/behave/current-weather-local.feature create mode 100644 test/behave/current-weather-location.feature create mode 100644 test/behave/daily-temperature-local.feature create mode 100644 test/behave/daily-weather-local.feature create mode 100644 test/behave/daily-weather-location.feature delete mode 100644 test/behave/weather-local.feature delete mode 100644 test/behave/weather-location.feature rename dialog/en-us/this.week.dialog => test/unit/__init__.py (100%) rename vocab/en-us/{Cloudy.voc => Clouds.voc} (100%) rename vocab/en-us/{CloudyAlternatives.voc => CloudsAlternatives.voc} (100%) rename vocab/en-us/{Foggy.voc => Fog.voc} (100%) create mode 100644 vocab/en-us/Like.voc create mode 100644 vocab/en-us/Outside.voc rename vocab/en-us/{Raining.voc => Rain.voc} (100%) rename vocab/en-us/{Snowing.voc => Snow.voc} (100%) rename vocab/en-us/{Storm.voc => Thunderstorm.voc} (100%) rename vocab/en-us/{StormAlternatives.voc => ThunderstormAlternatives.voc} (100%) delete mode 100644 vocab/en-us/what.is.three.day.forecast.intent delete mode 100644 vocab/en-us/what.is.three.day.forecast.location.intent delete mode 100644 vocab/en-us/what.is.two.day.forecast.intent delete mode 100644 vocab/en-us/whats.weather.like.intent diff --git a/__init__.py b/__init__.py index 3ee3e760..fc0f286c 100644 --- a/__init__.py +++ b/__init__.py @@ -26,253 +26,48 @@ https://media.readthedocs.org/pdf/pyowm/latest/pyowm.pdf """ -import json -import time -from copy import deepcopy +from collections import defaultdict from datetime import datetime, timedelta from multi_key_dict import multi_key_dict +from typing import List, Optional from adapt.intent import IntentBuilder -from pyowm.webapi25.forecaster import Forecaster -from pyowm.webapi25.forecastparser import ForecastParser -from pyowm.webapi25.observationparser import ObservationParser import pytz -from requests import HTTPError, Response +from requests import HTTPError import mycroft.audio from mycroft import MycroftSkill, intent_handler -from mycroft.api import Api from mycroft.messagebus.message import Message -from mycroft.util.format import ( - nice_date, nice_time, nice_number, pronounce_number, join_list -) -from mycroft.util.log import LOG +from mycroft.util.format import (nice_date, join_list) from mycroft.util.parse import extract_datetime, extract_number -from mycroft.util.time import now_local, to_utc, to_local +from mycroft.util.time import to_utc, to_local +from .source import ( + get_sequence_of_days, + LocationNotFoundError, + OpenWeatherMapApi, + OWMApi, + WeatherConfig, + WeatherDialog, + WeatherIntent, + WeatherReport +) +# TODO: VK Failures +# Locations: Washington, D.C. +# +# TODO: Intent failures +# Later weather: only the word "later" in the vocab file works all others +# invoke datetime skill MINUTES = 60 # Minutes to seconds multiplier -class LocationNotFoundError(ValueError): - pass - - -APIErrors = (LocationNotFoundError, HTTPError) - - - - -# Windstrength limits in miles per hour -WINDSTRENGTH_MPH = { - 'hard': 20, - 'medium': 11 -} - - -# Windstrenght limits in m/s -WINDSTRENGTH_MPS = { - 'hard': 9, - 'medium': 5 -} - - -class OWMApi(Api): - ''' Wrapper that defaults to the Mycroft cloud proxy so user's don't need - to get their own OWM API keys ''' - - def __init__(self): - super(OWMApi, self).__init__("owm") - self.owmlang = "en" - self.encoding = "utf8" - self.observation = ObservationParser() - self.forecast = ForecastParser() - self.query_cache = {} - self.location_translations = {} - - @staticmethod - def get_language(lang): - """ - OWM supports 31 languages, see https://openweathermap.org/current#multi - - Convert language code to owm language, if missing use 'en' - """ - - owmlang = 'en' - - # some special cases - if lang == 'zh-zn' or lang == 'zh_zn': - return 'zh_zn' - elif lang == 'zh-tw' or lang == 'zh_tw': - return 'zh_tw' - - # special cases cont'd - lang = lang.lower().split("-") - lookup = { - 'sv': 'se', - 'cs': 'cz', - 'ko': 'kr', - 'lv': 'la', - 'uk': 'ua' - } - if lang[0] in lookup: - return lookup[lang[0]] - - owmsupported = ['ar', 'bg', 'ca', 'cz', 'da', 'de', 'el', 'en', 'fa', 'fi', - 'fr', 'gl', 'hr', 'hu', 'it', 'ja', 'kr', 'la', 'lt', - 'mk', 'nl', 'pl', 'pt', 'ro', 'ru', 'se', 'sk', 'sl', - 'es', 'tr', 'ua', 'vi'] - - if lang[0] in owmsupported: - return lang[0] - - if (len(lang) == 2): - if lang[1] in owmsupported: - return lang[1] - return owmlang - - def build_query(self, params): - params.get("query").update({"lang": self.owmlang}) - return params.get("query") - - def request(self, data): - """ Caching the responses """ - req_hash = hash(json.dumps(data, sort_keys=True)) - cache = self.query_cache.get(req_hash, (0, None)) - # check for caches with more days data than requested - if data['query'].get('cnt') and cache == (0, None): - test_req_data = deepcopy(data) - while test_req_data['query']['cnt'] < 16 and cache == (0, None): - test_req_data['query']['cnt'] += 1 - test_hash = hash(json.dumps(test_req_data, sort_keys=True)) - test_cache = self.query_cache.get(test_hash, (0, None)) - if test_cache != (0, None): - cache = test_cache - # Use cached response if value exists and was fetched within 15 min - now = time.monotonic() - if now > (cache[0] + 15 * MINUTES) or cache[1] is None: - resp = super().request(data) - # 404 returned as JSON-like string in some instances - if isinstance(resp, str) and '{"cod":"404"' in resp: - r = Response() - r.status_code = 404 - raise HTTPError(resp, response=r) - self.query_cache[req_hash] = (now, resp) - else: - LOG.debug('Using cached OWM Response from {}'.format(cache[0])) - resp = cache[1] - return resp - - def get_data(self, response): - return response.text - - def weather_at_location(self, name): - if name == '': - raise LocationNotFoundError('The location couldn\'t be found') - - q = {"q": name} - try: - data = self.request({ - "path": "/weather", - "query": q - }) - return self.observation.parse_JSON(data), name - except HTTPError as e: - if e.response.status_code == 404: - name = ' '.join(name.split()[:-1]) - return self.weather_at_location(name) - raise - - def weather_at_place(self, name, lat, lon): - if lat and lon: - q = {"lat": lat, "lon": lon} - else: - if name in self.location_translations: - name = self.location_translations[name] - response, trans_name = self.weather_at_location(name) - self.location_translations[name] = trans_name - return response - - data = self.request({ - "path": "/weather", - "query": q - }) - return self.observation.parse_JSON(data) - - def three_hours_forecast(self, name, lat, lon): - if lat and lon: - q = {"lat": lat, "lon": lon} - else: - if name in self.location_translations: - name = self.location_translations[name] - q = {"q": name} - - data = self.request({ - "path": "/forecast", - "query": q - }) - return self.to_forecast(data, "3h") - - def _daily_forecast_at_location(self, name, limit): - if name in self.location_translations: - name = self.location_translations[name] - orig_name = name - while name != '': - try: - q = {"q": name} - if limit is not None: - q["cnt"] = limit - data = self.request({ - "path": "/forecast/daily", - "query": q - }) - forecast = self.to_forecast(data, 'daily') - self.location_translations[orig_name] = name - return forecast - except HTTPError as e: - if e.response.status_code == 404: - # Remove last word in name - name = ' '.join(name.split()[:-1]) - - raise LocationNotFoundError('The location couldn\'t be found') - - def daily_forecast(self, name, lat, lon, limit=None): - if lat and lon: - q = {"lat": lat, "lon": lon} - else: - return self._daily_forecast_at_location(name, limit) - - if limit is not None: - q["cnt"] = limit - data = self.request({ - "path": "/forecast/daily", - "query": q - }) - return self.to_forecast(data, "daily") - - def to_forecast(self, data, interval): - forecast = self.forecast.parse_JSON(data) - if forecast is not None: - forecast.set_interval(interval) - return Forecaster(forecast) - else: - return None - - def set_OWM_language(self, lang): - self.owmlang = lang - - # Certain OWM condition information is encoded using non-utf8 - # encodings. If another language needs similar solution add them to the - # encodings dictionary - encodings = { - 'se': 'latin1' - } - self.encoding = encodings.get(lang, 'utf8') - - class WeatherSkill(MycroftSkill): def __init__(self): super().__init__("WeatherSkill") + self.weather_api = OWMApi() + self.weather_api_new = OpenWeatherMapApi() + self.weather_config = WeatherConfig(self.config_core, self.settings) # Build a dictionary to translate OWM weather-conditions # codes into the Mycroft weather icon codes @@ -292,1510 +87,511 @@ def __init__(self): self.settings["use_proxy"] = True def initialize(self): - # TODO: Remove lat,lon parameters from the OWMApi() - # methods and implement _at_coords() versions - # instead to make the interfaces compatible - # again. - # - # if self.settings["api_key"] and not self.settings['use_proxy']): - # self.owm = OWM(self.settings["api_key"]) - # else: - # self.owm = OWMApi() - self.owm = OWMApi() - if self.owm: - self.owm.set_OWM_language(lang=OWMApi.get_language(self.lang)) - - self.schedule_for_daily_use() - try: - self.mark2_forecast(self.__initialize_report(None)) - except Exception as e: - self.log.warning('Could not prepare forecasts. ' - '({})'.format(repr(e))) - - # self.test_screen() # DEBUG: Used during screen testing/debugging - - def test_screen(self): - self.gui["current"] = 72 - self.gui["min"] = 83 - self.gui["max"] = 5 - self.gui["location"] = "kansas city" - self.gui["condition"] = "sunny" - self.gui["icon"] = "sunny" - self.gui["weathercode"] = 0 - self.gui["humidity"] = "100%" - self.gui["wind"] = "--" - - self.gui.show_page('weather.qml') - - def prime_weather_cache(self): - # If not already cached, this will reach out for current conditions - report = self.__initialize_report(None) - try: - self.owm.weather_at_place( - report['full_location'], report['lat'], - report['lon']).get_weather() - self.owm.daily_forecast(report['full_location'], - report['lat'], report['lon'], limit=16) - except Exception as e: - self.log.error('Failed to prime weather cache ' - '({})'.format(repr(e))) - - def schedule_for_daily_use(self): - # Assume the user has a semi-regular schedule. Whenever this method - # is called, it will establish a 45 minute window of pre-cached - # weather info for the next day allowing for snappy responses to the - # daily query. - self.prime_weather_cache() - self.cancel_scheduled_event("precache1") - self.cancel_scheduled_event("precache2") - self.cancel_scheduled_event("precache3") - self.schedule_repeating_event(self.prime_weather_cache, None, - 60*60*24, # One day in seconds - name="precache1") - self.schedule_repeating_event(self.prime_weather_cache, None, - 60*60*24-60*15, # One day - 15 minutes - name="precache2") - self.schedule_repeating_event(self.prime_weather_cache, None, - 60*60*24+60*15, # One day + 15 minutes - name="precache3") - - def get_coming_days_forecast(self, forecast, unit, days=None): - """ - Get weather forcast for the coming days and returns them as a list - - Parameters: - forecast: OWM weather - unit: Temperature unit - dt: Reference time - days: number of days to get forecast for, defaults to 4 - - Returns: List of dicts containg weather info - """ - days = days or 4 - weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - forecast_list = [] - # Get tomorrow and 4 days forward - for weather in list(forecast.get_weathers())[1:5]: - result_temp = weather.get_temperature(unit) - day_num = datetime.weekday( - datetime.fromtimestamp(weather.get_reference_time())) - result_temp_day = weekdays[day_num] - forecast_list.append({ - "weathercode": self.CODES[weather.get_weather_icon_name()], - "max": round(result_temp['max']), - "min": round(result_temp['min']), - "date": result_temp_day - }) - return forecast_list - - def mark2_forecast(self, report): - """ Builds forecast for the upcoming days for the Mark-2 display.""" - future_weather = self.owm.daily_forecast(report['full_location'], - report['lat'], - report['lon'], limit=5) - if future_weather is None: - self.__report_no_data('weather') - return - - f = future_weather.get_forecast() - forecast_list = self.get_coming_days_forecast( - f, self.__get_temperature_unit()) - - if "gui" in dir(self): - forecast = {} - forecast['first'] = forecast_list[0:2] - forecast['second'] = forecast_list[2:4] - self.gui['forecast'] = forecast - - # DATETIME BASED QUERIES - # Handle: what is the weather like? - @intent_handler(IntentBuilder("").one_of("Weather", "Forecast") - .require("Query").optionally("Location") - .optionally("Today").build()) + if self.weather_api: + self.weather_api.set_OWM_language(lang=OWMApi.get_language(self.lang)) + self.weather_config.speed_unit = self.translate( + self.weather_config.speed_unit + ) + self.weather_config.temperature_unit = self.translate( + self.weather_config.temperature_unit + ) + + @intent_handler( + IntentBuilder("").one_of("Weather", "Forecast").optionally("Query") + .optionally("Location").optionally("Today") + ) def handle_current_weather(self, message): - try: - self.log.debug("Handler: handle_current_weather") - # Get a date from requests like "weather for next Tuesday" - today, _ = self.__extract_datetime("today") - when, _ = self.__extract_datetime(message.data.get('utterance'), - lang=self.lang) - if when and when != today: - self.log.debug("Doing a forecast {} {}".format(today, when)) - return self.handle_forecast(message) - - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - self.__report_weather( - "current", report, - separate_min_max='Location' not in message.data) - self.mark2_forecast(report) - - # Establish the daily cadence - self.schedule_for_daily_use() - except APIErrors as e: - self.log.exception(repr(e)) - self.__api_error(e) - except Exception as e: - self.log.exception("Error: {0}".format(e)) - - @intent_handler("whats.weather.like.intent") - def handle_current_weather_alt(self, message): - self.handle_current_weather(message) - - @intent_handler(IntentBuilder("").one_of("Weather", "Forecast") - .one_of("Now", "Today").optionally("Location").build()) - def handle_current_weather_simple(self, message): - self.handle_current_weather(message) - - @intent_handler("what.is.three.day.forecast.intent") - def handle_three_day_forecast(self, message): - """ Handler for three day forecast without specified location - - Examples: "What is the 3 day forecast?" - "What is the weather forecast?" - """ - report = self.__initialize_report(message) - - try: - self.report_multiday_forecast(report) - except APIErrors as e: - self.__api_error(e) - except Exception as e: - self.log.exception("Error: {0}".format(e)) - - @intent_handler("what.is.three.day.forecast.location.intent") - def handle_three_day_forecast_location(self, message): - """ Handler for three day forecast for a specific location + # Handle: what is the weather like? + self._report_current_weather(message) - Example: "What is the 3 day forecast for London?" - """ - # padatious lowercases everything including these keys - message.data['Location'] = message.data.pop('location') - return self.handle_three_day_forecast(message) - - @intent_handler("what.is.two.day.forecast.intent") - def handle_two_day_forecast(self, message): - """ Handler for two day forecast with no specified location - - Examples: "What's the weather like next Monday and Tuesday?" - "What's the weather gonna be like in the coming days?" - """ - # TODO consider merging in weekend intent - - report = self.__initialize_report(message) - if message.data.get('day_one'): - # report two or more specific days - days = [] - day_num = 1 - day = message.data['day_one'] - while day: - day_dt, _ = self.__extract_datetime(day) - days.append(day_dt) - day_num += 1 - next_day = 'day_{}'.format(pronounce_number(day_num)) - day = message.data.get(next_day) - - try: - if message.data.get('day_one'): - # report two or more specific days - self.report_multiday_forecast(report, set_days=days) - else: - # report next two days - self.report_multiday_forecast(report, num_days=2) - - except APIErrors as e: - self.__api_error(e) - except Exception as e: - self.log.exception("Error: {0}".format(e)) + @intent_handler( + IntentBuilder("").require("Query").require("Like").require("Outside") + .optionally("Location").optionally("Today") + ) + def handle_like_outside(self, message): + self._report_current_weather(message) @intent_handler("what.is.multi.day.forecast.intent") def handle_multi_day_forecast(self, message): - """ Handler for multiple day forecast with no specified location + """ Handler for three day forecast without specified location - Examples: "What's the weather like in the next 4 days?" + Examples: "What is the 3 day forecast?" + "What is the weather forecast?" """ - - report = self.__initialize_report(message) - # report x number of days - when, _ = self.__extract_datetime("tomorrow") - num_days = int(extract_number(message.data['num'])) - if self.voc_match(message.data['num'], 'Couple'): - self.report_multiday_forecast(report, num_days=2) - - self.report_multiday_forecast(report, when, - num_days=num_days) - - # Handle: What is the weather forecast tomorrow? - @intent_handler(IntentBuilder("").one_of("Weather", "Forecast") - .optionally("Query").require("RelativeDay") - .optionally("Location").build()) - def handle_forecast(self, message): - report = self.__initialize_report(message) - - # Get a date from spoken request - when, _ = self.__extract_datetime(message.data.get('utterance'), - lang=self.lang) - today, _ = self.__extract_datetime('today', lang='en-us') - - if today == when: - self.handle_current_weather(message) - return - - self.report_forecast(report, when) - - # Establish the daily cadence - self.schedule_for_daily_use() - - # Handle: What's the weather later? - @intent_handler(IntentBuilder("").require("Query").require( - "Weather").optionally("Location").require("Later").build()) + days = 2 + else: + days = int(extract_number(message.data['num'])) + if days > 7: + self.speak_dialog('seven.days.available') + days = 7 + self._report_multi_day_forecast(message, days) + + @intent_handler( + IntentBuilder("").one_of("Weather", "Forecast").optionally("Query") + .require("RelativeDay").optionally("Location") + ) + def handle_one_day_forecast(self, message): + # Handle: What is the weather forecast tomorrow? + self._report_one_day_forecast(message) + + @intent_handler( + IntentBuilder("").require("Query").require("Weather") + .require("Later").optionally("Location") + ) def handle_next_hour(self, message): - report = self.__initialize_report(message) - - # Get near-future forecast - forecastWeather = self.owm.three_hours_forecast( - report['full_location'], - report['lat'], - report['lon']).get_forecast().get_weathers()[0] - - if forecastWeather is None: - self.__report_no_data('weather') - return - - # NOTE: The 3-hour forecast uses different temperature labels, - # temp, temp_min and temp_max. - report['temp'] = self.__get_temperature(forecastWeather, 'temp') - report['temp_min'] = self.__get_temperature(forecastWeather, - 'temp_min') - report['temp_max'] = self.__get_temperature(forecastWeather, - 'temp_max') - report['condition'] = forecastWeather.get_detailed_status() - report['icon'] = forecastWeather.get_weather_icon_name() - self.__report_weather("hour", report) - - # Handle: What's the weather tonight / tomorrow morning? - @intent_handler(IntentBuilder("").require("RelativeTime") - .one_of("Weather", "Forecast").optionally("Query") - .optionally("RelativeDay").optionally("Location").build()) + # Handle: What's the weather later? + self._report_one_hour_weather(message) + + @intent_handler( + IntentBuilder("").require("RelativeTime").one_of("Weather", "Forecast") + .optionally("Query").optionally("RelativeDay").optionally("Location") + ) def handle_weather_at_time(self, message): - self.log.debug("Handler: handle_weather_at_time") - when, _ = self.__extract_datetime( - message.data.get('utterance'), lang=self.lang) - now = datetime.utcnow() - time_diff = (when - now) - mins_diff = (time_diff.days * 1440) + (time_diff.seconds / 60) - - if mins_diff < 120: - self.handle_current_weather(message) - else: - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - self.__report_weather("at.time", report) - - @intent_handler(IntentBuilder("").require("Query").one_of( - "Weather", "Forecast").require("Weekend").require( - "Next").optionally("Location").build()) - def handle_next_weekend_weather(self, message): - """ Handle next weekends weather """ - - report = self.__initialize_report(message) - when, _ = self.__extract_datetime('next saturday', lang='en-us') - self.report_forecast(report, when) - when, _ = self.__extract_datetime('next sunday', lang='en-us') - self.report_forecast(report, when) - - @intent_handler(IntentBuilder("").require("Query") - .one_of("Weather", "Forecast").require("Weekend") - .optionally("Location").build()) - def handle_weekend_weather(self, message): + # Handle: What's the weather tonight / tomorrow morning? + self._report_one_hour_weather(message) + + @intent_handler( + IntentBuilder("").require("Query").one_of("Weather", "Forecast") + .require("Weekend").optionally("Location") + ) + def handle_weekend_forecast(self, message): """ Handle weather for weekend. """ - report = self.__initialize_report(message) - - # Get a date from spoken request - when, _ = self.__extract_datetime('this saturday', lang='en-us') - self.report_forecast(report, when) - when, _ = self.__extract_datetime('this sunday', lang='en-us') - self.report_forecast(report, when) + self._report_weekend_forecast(message) - @intent_handler(IntentBuilder("").optionally("Query") - .one_of("Weather", "Forecast").require("Week") - .optionally("Location").build()) + @intent_handler( + IntentBuilder("").optionally("Query").one_of("Weather", "Forecast") + .require("Week").optionally("Location") + ) def handle_week_weather(self, message): """ Handle weather for week. Speaks overview of week, not daily forecasts """ - report = self.__initialize_report(message) - when, _ = self.__extract_datetime(message.data['utterance']) - today, _ = self.__extract_datetime("today") - if not when: - when = today - days = [when + timedelta(days=i) for i in range(7)] - # Fetch forecasts/reports for week - forecasts = [dict(self.__populate_forecast(report, day, - preface_day=False)) - if day != today - else dict(self.__populate_current(report, day)) - for day in days] - - if forecasts is None: - self.__report_no_data('weather') - return - - # collate forecasts - collated = {'condition': [], 'condition_cat': [], 'icon': [], - 'temp': [], 'temp_min': [], 'temp_max': []} - for fc in forecasts: - for attribute in collated.keys(): - collated[attribute].append(fc.get(attribute)) - - # analyse for commonality/difference - primary_category = max(collated['condition_cat'], - key=collated['condition_cat'].count) - days_with_primary_cat, conditions_in_primary_cat = [], [] - days_with_other_cat = {} - for i, item in enumerate(collated['condition_cat']): - if item == primary_category: - days_with_primary_cat.append(i) - conditions_in_primary_cat.append(collated['condition'][i]) - else: - if not days_with_other_cat.get(item): - days_with_other_cat[item] = [] - days_with_other_cat[item].append(i) - primary_condition = max(conditions_in_primary_cat, - key=conditions_in_primary_cat.count) - - # CONSTRUCT DIALOG - speak_category = self.translate_namedvalues('condition.category') - # 0. Report period starting day - if days[0] == today: - dialog = self.translate('this.week') - else: - speak_day = self.__to_day(days[0]) - dialog = self.translate('from.day', {'day': speak_day}) - - # 1. whichever is longest (has most days), report as primary - # if over half the days => "it will be mostly {cond}" - speak_primary = speak_category[primary_category] - seq_primary_days = self.__get_seqs_from_list(days_with_primary_cat) - if len(days_with_primary_cat) >= (len(days) / 2): - dialog = self.concat_dialog(dialog, - 'weekly.conditions.mostly.one', - {'condition': speak_primary}) - elif seq_primary_days: - # if condition occurs on sequential days, report date range - dialog = self.concat_dialog(dialog, - 'weekly.conditions.seq.start', - {'condition': speak_primary}) - for seq in seq_primary_days: - if seq is not seq_primary_days[0]: - dialog = self.concat_dialog(dialog, 'and') - day_from = self.__to_day(days[seq[0]]) - day_to = self.__to_day(days[seq[-1]]) - dialog = self.concat_dialog(dialog, - 'weekly.conditions.seq.period', - {'from': day_from, - 'to': day_to}) - else: - # condition occurs on random days - dialog = self.concat_dialog(dialog, - 'weekly.conditions.some.days', - {'condition': speak_primary}) - self.speak_dialog(dialog) - - # 2. Any other conditions present: - dialog = "" - dialog_list = [] - for cat in days_with_other_cat: - spoken_cat = speak_category[cat] - cat_days = days_with_other_cat[cat] - seq_days = self.__get_seqs_from_list(cat_days) - for seq in seq_days: - if seq is seq_days[0]: - seq_dialog = spoken_cat - else: - seq_dialog = self.translate('and') - day_from = self.__to_day(days[seq[0]]) - day_to = self.__to_day(days[seq[-1]]) - seq_dialog = self.concat_dialog( - seq_dialog, - self.translate('weekly.conditions.seq.period', - {'from': day_from, - 'to': day_to})) - dialog_list.append(seq_dialog) - if not seq_days: - for day in cat_days: - speak_day = self.__to_day(days[day]) - dialog_list.append(self.translate( - 'weekly.condition.on.day', - {'condition': collated['condition'][day], - 'day': speak_day})) - dialog = join_list(dialog_list, 'and') - self.speak_dialog(dialog) - - # 3. Report temps: - temp_ranges = { - 'low_min': min(collated['temp_min']), - 'low_max': max(collated['temp_min']), - 'high_min': min(collated['temp_max']), - 'high_max': max(collated['temp_max']) - } - self.speak_dialog('weekly.temp.range', temp_ranges) - - # CONDITION BASED QUERY HANDLERS #### - @intent_handler(IntentBuilder("").require("Temperature") - .require("Query").optionally("Location") - .optionally("Unit").optionally("Today") - .optionally("Now").build()) + self._report_multi_day_forecast(message, days=7) + + @intent_handler( + IntentBuilder("").require("Temperature").optionally("Query") + .optionally("Location").optionally("Unit").optionally("Today") + .optionally("Now") + ) def handle_current_temperature(self, message): - return self.__handle_typed(message, 'temperature') + self._report_temperature(message, temperature_type="current") - @intent_handler('simple.temperature.intent') + @intent_handler( + IntentBuilder("").optionally("Query").require("Temperature") + .optionally("Location").optionally("Unit").optionally("RelativeDay") + .optionally("Now") + ) def handle_simple_temperature(self, message): - return self.__handle_typed(message, 'temperature') - - @intent_handler(IntentBuilder("").require("Query").require("High") - .optionally("Temperature").optionally("Location") - .optionally("Unit").optionally("RelativeDay") - .optionally("Now").build()) + self._report_temperature(message, temperature_type="current") + + @intent_handler( + IntentBuilder("").require("RelativeTime").require("Temperature") + .optionally("Query").optionally("RelativeDay").optionally("Location") + ) + def handle_temperature_at_time(self, message): + self._report_temperature(message) + + @intent_handler( + IntentBuilder("").optionally("Query").require("High") + .optionally("Temperature").optionally("Location").optionally("Unit") + .optionally("RelativeDay") + ) def handle_high_temperature(self, message): - return self.__handle_typed(message, 'high.temperature') + self._report_temperature(message, temperature_type="high") - @intent_handler(IntentBuilder("").require("Query").require("Low") - .optionally("Temperature").optionally("Location") - .optionally("Unit").optionally("RelativeDay") - .optionally("Now").build()) + @intent_handler( + IntentBuilder("").optionally("Query").require("Low") + .optionally("Temperature").optionally("Location").optionally("Unit") + .optionally("RelativeDay") + ) def handle_low_temperature(self, message): - return self.__handle_typed(message, 'low.temperature') - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Windy").optionally("Location").build()) - def handle_isit_windy(self, message): - """ Handler for utterances similar to "is it windy today?" """ - report = self.__populate_report(message) + self._report_temperature(message, temperature_type="low") - if report is None: - self.__report_no_data('weather') - return - - if self.__get_speed_unit() == 'mph': - limits = WINDSTRENGTH_MPH - report['wind_unit'] = self.translate('miles per hour') - else: - limits = WINDSTRENGTH_MPS - report['wind_unit'] = self.translate('meters per second') - - dialog = [] - if 'day' in report: - dialog.append('forecast') - if "Location" not in message.data: - dialog.append('local') - if int(report['wind']) >= limits['hard']: - dialog.append('hard') - elif int(report['wind']) >= limits['medium']: - dialog.append('medium') - else: - dialog.append('light') - dialog.append('wind') - dialog = '.'.join(dialog) - self.speak_dialog(dialog, report) - - @intent_handler(IntentBuilder("").require("ConfirmQueryCurrent").one_of( - "Hot", "Cold").optionally("Location").optionally("Today").build()) - def handle_isit_hot(self, message): + @intent_handler( + IntentBuilder("").require("ConfirmQueryCurrent").one_of("Hot", "Cold") + .optionally("Location").optionally("Today") + ) + def handle_is_it_hot(self, message): """ Handler for utterances similar to is it hot today?, is it cold? etc """ - return self.__handle_typed(message, 'hot') + self._report_temperature(message, "current") - # TODO This seems to present current temp, or possibly just hottest temp - @intent_handler(IntentBuilder("").optionally("How").one_of("Hot", "Cold") - .one_of("ConfirmQueryFuture", "ConfirmQueryCurrent") - .optionally("Location").optionally("RelativeDay").build()) + @intent_handler( + IntentBuilder("").optionally("How").one_of("Hot", "Cold") + .one_of("ConfirmQueryFuture", "ConfirmQueryCurrent") + .optionally("Location").optionally("RelativeDay") + ) def handle_how_hot_or_cold(self, message): """ Handler for utterances similar to how hot will it be today?, how cold will it be? , etc """ - response_type = 'high.temperature' if message.data.get('Hot') \ - else 'low.temperature' - return self.__handle_typed(message, response_type) - - @intent_handler(IntentBuilder("").require("How").one_of("Hot", "Cold") - .one_of("ConfirmQueryFuture", "ConfirmQueryCurrent") - .optionally("Location").optionally("RelativeDay").build()) + temperature_type = "high" if message.data.get('Hot') else "low" + self._report_temperature(message, temperature_type) + + @intent_handler( + IntentBuilder("").require("How").one_of("Hot", "Cold") + .one_of("ConfirmQueryFuture", "ConfirmQueryCurrent") + .optionally("Location").optionally("RelativeDay") + ) def handle_how_hot_or_cold_alt(self, message): - self.handle_how_hot_or_cold(message) - - @intent_handler(IntentBuilder("").require("ConfirmQuery") - .require("Snowing").optionally("Location").build()) - def handle_isit_snowing(self, message): - """ Handler for utterances similar to "is it snowing today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - dialog = self.__select_condition_dialog(message, report, - "snow", "snowing") - self.speak_dialog(dialog, report) - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Clear").optionally("Location").build()) - def handle_isit_clear(self, message): - """ Handler for utterances similar to "is it clear skies today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - dialog = self.__select_condition_dialog(message, report, "clear") - self.speak_dialog(dialog, report) - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Cloudy").optionally("Location").optionally("RelativeTime").build()) - def handle_isit_cloudy(self, message): - """ Handler for utterances similar to "is it cloudy skies today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - dialog = self.__select_condition_dialog(message, report, "cloudy") - self.speak_dialog(dialog, report) - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Foggy").optionally("Location").build()) - def handle_isit_foggy(self, message): - """ Handler for utterances similar to "is it foggy today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - dialog = self.__select_condition_dialog(message, report, "fog", - "foggy") - self.speak_dialog(dialog, report) - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Raining").optionally("Location").build()) - def handle_isit_raining(self, message): - """ Handler for utterances similar to "is it raining today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return + temperature_type = "high" if message.data.get('Hot') else "low" + self._report_temperature(message, temperature_type) + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Windy") + .optionally("Location").optionally("RelativeDay") + ) + def handle_is_it_windy(self, message): + """ Handler for utterances similar to "is it windy today?" """ + self._report_wind(message) - dialog = self.__select_condition_dialog(message, report, "rain", - "raining") - self.speak_dialog(dialog, report) + @intent_handler( + IntentBuilder("").require("How").require("Windy") + .optionally("Location").optionally("ConfirmQuery") + .optionally("RelativeDay") + ) + def handle_windy(self, message): + # Handle: How windy is it? + self._report_wind(message) + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Snowing") + .optionally("Location") + ) + def handle_is_it_snowing(self, message): + """Handler for utterances similar to "is it snowing today?" """ + self._report_weather_condition(message, "Snow") + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Clear") + .optionally("Location") + ) + def handle_is_it_clear(self, message): + """Handler for utterances similar to "is it clear skies today?" """ + self._report_weather_condition(message, condition="Clear") + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Cloudy") + .optionally("Location").optionally("RelativeTime") + ) + def handle_is_it_cloudy(self, message): + """Handler for utterances similar to "is it cloudy skies today?" """ + self._report_weather_condition(message, "Clouds") + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Foggy") + .optionally("Location") + ) + def handle_is_it_foggy(self, message): + """Handler for utterances similar to "is it foggy today?" """ + self._report_weather_condition(message, "Fog") + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Raining") + .optionally("Location") + ) + def handle_is_it_raining(self, message): + """Handler for utterances similar to "is it raining today?" """ + self._report_weather_condition(message, "Rain") @intent_handler("do.i.need.an.umbrella.intent") def handle_need_umbrella(self, message): - self.handle_isit_raining(message) - - @intent_handler(IntentBuilder("").require("ConfirmQuery").require( - "Storm").optionally("Location").build()) - def handle_isit_storming(self, message): - """ Handler for utterances similar to "is it storming today?" - """ - report = self.__populate_report(message) - - if report is None: - self.__report_no_data('weather') - return - - dialog = self.__select_condition_dialog(message, report, "storm") - self.speak_dialog(dialog, report) - - # Handle: When will it rain again? - @intent_handler(IntentBuilder("").require("When").optionally( - "Next").require("Precipitation").optionally("Location").build()) + self._report_weather_condition(message, "Rain") + + @intent_handler( + IntentBuilder("").require("ConfirmQuery").require("Storm") + .optionally("Location") + ) + def handle_is_it_storming(self, message): + """Handler for utterances similar to "is it storming today?" """ + self._report_weather_condition(message, 'Thunderstorm') + + @intent_handler( + IntentBuilder("").require("When").optionally("Next") + .require("Precipitation").optionally("Location") + ) def handle_next_precipitation(self, message): - report = self.__initialize_report(message) - - # Get a date from spoken request - today, _ = self.__extract_datetime("today") - when, _ = self.__extract_datetime(message.data.get('utterance'), - lang=self.lang) - - # search the forecast for precipitation - weathers = self.owm.daily_forecast( - report['full_location'], - report['lat'], - report['lon'], 10).get_forecast() - - if weathers is None: - self.__report_no_data('weather') - return - - weathers = weathers.get_weathers() - for weather in weathers: - - forecastDate = datetime.fromtimestamp(weather.get_reference_time()) - - if when and when != today: - # User asked about a specific date, is this it? - if forecastDate.date() != when.date(): - continue - - rain = weather.get_rain() - if rain and rain["all"] > 0: - data = { - "modifier": "", - "precip": "rain", - "day": self.__to_day(forecastDate, preface=True) - } - if rain["all"] < 10: - data["modifier"] = self.__translate("light") - elif rain["all"] > 20: - data["modifier"] = self.__translate("heavy") - - self.speak_dialog("precipitation expected", data) - return - - snow = weather.get_snow() - if snow and snow["all"] > 0: - data = { - "modifier": "", - "precip": "snow", - "day": self.__to_day(forecastDate, preface=True) - } - if snow["all"] < 10: - data["modifier"] = self.__translate("light") - elif snow["all"] > 20: - data["modifier"] = self.__translate("heavy") - - self.speak_dialog("precipitation expected", data) - return - - self.speak_dialog("no precipitation expected", report) - - # Handle: How humid is it? - @intent_handler(IntentBuilder("").require("Query").require("Humidity") - .optionally("RelativeDay").optionally("Location").build()) + # Handle: When will it rain again? + intent_data = WeatherIntent(message, self.lang) + weather = self._get_weather(intent_data) + if weather is not None: + forecast, timeframe = weather.get_next_precipitation(intent_data) + intent_data.timeframe = timeframe + dialog = WeatherDialog(forecast, self.weather_config, intent_data) + dialog.build_next_precipitation_dialog() + self._speak_weather(dialog) + + @intent_handler( + IntentBuilder("").require("Query").require("Humidity") + .optionally("RelativeDay").optionally("Location") + ) def handle_humidity(self, message): - report = self.__initialize_report(message) - - when, _ = self.__extract_datetime(message.data.get('utterance'), - lang=self.lang) - today, _ = self.__extract_datetime("today") - if when is None or when == today: - weather = self.owm.weather_at_place( - report['full_location'], - report['lat'], - report['lon']).get_weather() - else: - # Get forecast for that day - weather = self.__get_forecast( - when, report['full_location'], report['lat'], report['lon']) - - if weather is None: - self.__report_no_data('weather') - return - - if weather.get_humidity() == 0: - self.speak_dialog("do not know") - return - - value = self.translate('percentage.number', - {'num': str(weather.get_humidity())}) - loc = message.data.get('Location') - self.__report_condition(self.__translate("humidity"), value, when, loc) - - # Handle: How windy is it? - @intent_handler(IntentBuilder("").require("Query").require("Windy") - .optionally("Location").optionally("ConfirmQuery") - .optionally("RelativeDay").build()) - def handle_windy(self, message): - report = self.__initialize_report(message) - - when, _ = self.__extract_datetime(message.data.get('utterance')) - today, _ = self.__extract_datetime("today") - if when is None or when == today: - weather = self.owm.weather_at_place( - report['full_location'], - report['lat'], - report['lon']).get_weather() - else: - # Get forecast for that day - weather = self.__get_forecast( - when, report['full_location'], report['lat'], report['lon']) - - if weather is None: - self.__report_no_data('weather') - return - - if not weather or weather.get_wind() == 0: - self.speak_dialog("do not know") - return - - speed, dir, unit, strength = self.get_wind_speed(weather) - if dir: - dir = self.__translate(dir) - value = self.__translate("wind.speed.dir", - data={"dir": dir, - "speed": nice_number(speed), - "unit": unit}) - else: - value = self.__translate("wind.speed", - data={"speed": nice_number(speed), - "unit": unit}) - loc = message.data.get('Location') - self.__report_condition(self.__translate("winds"), value, when, loc) - self.speak_dialog('wind.strength.' + strength) - - def get_wind_speed(self, weather): - wind = weather.get_wind() - - speed = wind["speed"] - # get speed - if self.__get_speed_unit() == "mph": - unit = self.__translate("miles per hour") - speed_multiplier = 2.23694 - speed *= speed_multiplier - else: - unit = self.__translate("meters per second") - speed_multiplier = 1 - speed = round(speed) - - if (speed / speed_multiplier) < 0: - self.log.error("Wind speed below zero") - if (speed / speed_multiplier) <= 2.2352: - strength = "light" - elif (speed / speed_multiplier) <= 6.7056: - strength = "medium" - else: - strength = "hard" - - # get direction, convert compass degrees to named direction - if "deg" in wind: - deg = wind["deg"] - if deg < 22.5: - dir = "N" - elif deg < 67.5: - dir = "NE" - elif deg < 112.5: - dir = "E" - elif deg < 157.5: - dir = "SE" - elif deg < 202.5: - dir = "S" - elif deg < 247.5: - dir = "SW" - elif deg < 292.5: - dir = "W" - elif deg < 337.5: - dir = "NW" - else: - dir = "N" - else: - dir = None - - return speed, dir, unit, strength - - # Handle: When is the sunrise? - @intent_handler(IntentBuilder("").one_of("Query", "When") - .optionally("Location").require("Sunrise").build()) + # Handle: How humid is it? + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + dialog = WeatherDialog( + intent_weather, self.weather_config, intent_data + ) + dialog.build_humidity_dialog() + dialog.data.update( + humidity=self.translate( + "percentage.number", data=dict(num=dialog.data.humidity)) + ) + self._speak_weather(dialog) + + @intent_handler( + IntentBuilder("").one_of("Query", "When").optionally("Location") + .require("Sunrise") + ) def handle_sunrise(self, message): - report = self.__initialize_report(message) - - when, _ = self.__extract_datetime(message.data.get('utterance')) - today, _ = self.__extract_datetime("today") - if when is None or when.date() == today.date(): - weather = self.owm.weather_at_place( - report['full_location'], - report['lat'], - report['lon']).get_weather() - - if weather is None: - self.__report_no_data('weather') - return - else: - # Get forecast for that day - # weather = self.__get_forecast(when, report['full_location'], - # report['lat'], report['lon']) - - # There appears to be a bug in OWM, it can't extract the sunrise/ - # sunset from forecast objects. As of March 2018 OWM said it was - # "in the roadmap". Just say "I don't know" for now - weather = None - if not weather or weather.get_humidity() == 0: - self.speak_dialog("do not know") - return - - # uses device tz so if not set (eg Mark 1) this is UTC. - dtSunrise = datetime.fromtimestamp(weather.get_sunrise_time()) - if time.tzname == ("UTC", "UTC"): - dtSunrise = self.__to_Local(dtSunrise.replace(tzinfo=pytz.utc)) - spoken_time = self.__nice_time(dtSunrise, use_ampm=True) - self.speak_dialog('sunrise', {'time': spoken_time}) - - # Handle: When is the sunset? - @intent_handler(IntentBuilder("").one_of("Query", "When") - .optionally("Location").require("Sunset").build()) + # Handle: When is the sunrise? + intent_data = WeatherIntent(message, self.lang) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + dialog = WeatherDialog( + intent_weather, self.weather_config, intent_data + ) + dialog.build_sunrise_dialog() + self._speak_weather(dialog) + + @intent_handler( + IntentBuilder("").one_of("Query", "When").optionally("Location") + .require("Sunset") + ) def handle_sunset(self, message): - report = self.__initialize_report(message) - - when, _ = self.__extract_datetime(message.data.get('utterance')) - today, _ = self.__extract_datetime("today") - if when is None or when.date() == today.date(): - weather = self.owm.weather_at_place( - report['full_location'], - report['lat'], - report['lon']).get_weather() - - if weather is None: - self.__report_no_data('weather') - return + # Handle: When is the sunset? + intent_data = WeatherIntent(message, self.lang) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + dialog = WeatherDialog( + intent_weather, self.weather_config, intent_data + ) + dialog.build_sunset_dialog() + self._speak_weather(dialog) + + def _report_current_weather(self, message): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + dialog = WeatherDialog( + weather.current, self.weather_config, intent_data + ) + dialog.build_current_weather_dialog() + self._display_current_weather(weather) + self._speak_weather(dialog) + dialog.build_high_low_temperature_dialog() + self._speak_weather(dialog) + + def _display_current_weather(self, report): + image_code = self.CODES[report.current.condition.icon] + if self.gui.connected: + self.gui["current"] = report.current.temperature + self.gui["min"] = report.current.low_temperature + self.gui["max"] = report.current.high_temperature + self.gui["condition"] = report.current.condition.description + self.gui["icon"] = report.current.condition.icon + self.gui["weathercode"] = image_code + self.gui.show_pages(["weather.qml", "highlow.qml"]) else: - # Get forecast for that day - # weather = self.__get_forecast(when, report['full_location'], - # report['lat'], report['lon']) - - # There appears to be a bug in OWM, it can't extract the sunrise/ - # sunset from forecast objects. As of March 2018 OWM said it was - # "in the roadmap". Just say "I don't know" for now - weather = None - if not weather or weather.get_humidity() == 0: - self.speak_dialog("do not know") - return - - # uses device tz so if not set (eg Mark 1) this is UTC. - dtSunset = datetime.fromtimestamp(weather.get_sunset_time()) - if time.tzname == ("UTC", "UTC"): - dtSunset = self.__to_Local(dtSunset.replace(tzinfo=pytz.utc)) - spoken_time = self.__nice_time(dtSunset, use_ampm=True) - self.speak_dialog('sunset', {'time': spoken_time}) - - def __get_location(self, message): - """ Attempt to extract a location from the spoken phrase. - - If none is found return the default location instead. + self.enclosure.deactivate_mouth_events() + self.enclosure.weather_display( + image_code, report.current.temperature + ) + + def _report_one_hour_weather(self, message): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + forecast = weather.get_forecast_for_hour(intent_data) + dialog = WeatherDialog( + forecast, self.weather_config, intent_data + ) + dialog.build_hourly_weather_dialog() + self._speak_weather(dialog) + + def _report_multi_day_forecast(self, message, days): + intent_data = WeatherIntent(message, self.lang) + weather = self._get_weather(intent_data) + if weather is not None: + forecast = weather.daily[:days] + dialogs = self._build_forecast_dialogs(forecast, intent_data) + self._display_forecast(forecast) + for dialog in dialogs: + self._speak_weather(dialog) + + def _report_one_day_forecast(self, message): + intent_data = WeatherIntent(message, self.lang) + weather = self._get_weather(intent_data) + if weather is not None: + forecast = [weather.get_forecast_for_date(intent_data)] + dialogs = self._build_forecast_dialogs(forecast, intent_data) + self._display_forecast(forecast) + for dialog in dialogs: + self._speak_weather(dialog) + + def _report_weekend_forecast(self, message): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + forecast = weather.get_weekend_forecast() + dialogs = self._build_forecast_dialogs(forecast, intent_data) + self._display_forecast(forecast) + for dialog in dialogs: + self._speak_weather(dialog) + + def _build_forecast_dialogs(self, forecast, intent_data): + dialogs = list() + for forecast_day in forecast: + dialog = WeatherDialog( + forecast_day, self.weather_config, intent_data + ) + dialog.build_daily_weather_dialog() + dialogs.append(dialog) + + return dialogs + + def _display_forecast(self, forecast: List): + """Builds forecast for the upcoming days for the Mark-2 display.""" + display_data = [] + for forecast_day in forecast: + display_data.append( + dict( + weathercode=self.CODES[forecast_day.condition.icon], + max=forecast_day.temperature.high, + min=forecast_day.temperature.low, + date=forecast_day.date_time.strftime('%a') + ) + ) + self.gui['forecast'] = dict( + first=display_data[:2], second=display_data[2:] + ) + + def _report_temperature(self, message, temperature_type=None): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + dialog = WeatherDialog( + intent_weather, self.weather_config, intent_data + ) + dialog.build_temperature_dialog(temperature_type) + self._speak_weather(dialog) + + def _report_weather_condition(self, message, condition): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + dialog = self._build_condition_dialog( + intent_weather, intent_data, condition + ) + self._speak_weather(dialog) + + def _build_condition_dialog(self, weather, intent_data, condition): + dialog = WeatherDialog( + weather, self.weather_config, intent_data + ) + intent_match = self.voc_match(weather.condition.category.lower(), condition) + alternative_vocab = condition + 'Alternatives' + alternative = self.voc_match( + weather.condition.category, alternative_vocab + ) + dialog.build_condition_dialog(condition, intent_match, alternative) + dialog.data.update(condition=self.translate(condition)) - Arguments: - message (Message): messagebus message - Returns: tuple (lat, long, location string, pretty location) - """ + return dialog + + def _report_wind(self, message): + intent_data = self._get_intent_data(message) + weather = self._get_weather(intent_data) + if weather is not None: + intent_weather = weather.get_weather_for_intent(intent_data) + intent_weather.wind_direction = self.translate( + intent_weather.wind_direction + ) + dialog = WeatherDialog( + intent_weather, self.weather_config, intent_data + ) + dialog.build_wind_dialog() + self._speak_weather(dialog) + + def _get_intent_data(self, message): + intent_data = None try: - location = message.data.get("Location", None) if message else None - if location: - return None, None, location, location - - location = self.location - - if isinstance(location, dict): - lat = location["coordinate"]["latitude"] - lon = location["coordinate"]["longitude"] - city = location["city"] - state = city["state"] - return lat, lon, city["name"] + ", " + state["name"] + \ - ", " + state["country"]["name"], self.location_pretty - - return None - except Exception: - self.speak_dialog("location.not.found") - raise LocationNotFoundError("Location not found") - - def __initialize_report(self, message): - """ Creates a report base with location, unit. """ - lat, lon, location, pretty_location = self.__get_location(message) - temp_unit = self.__get_requested_unit(message) - return { - 'lat': lat, - 'lon': lon, - 'location': pretty_location, - 'full_location': location, - 'scale': self.translate(temp_unit or self.__get_temperature_unit()) - } - - def __handle_typed(self, message, response_type): - # Get a date from requests like "weather for next Tuesday" - today, _ = self.__extract_datetime("today") - when, _ = self.__extract_datetime( - message.data.get('utterance'), lang=self.lang) - - report = self.__initialize_report(message) - if when and when.date() != today.date(): - self.log.debug("Doing a forecast {} {}".format(today, when)) - return self.report_forecast(report, when, - dialog=response_type) - report = self.__populate_report(message) - if report is None: - return self.__report_no_data('weather') - - if report.get('time'): - self.__report_weather("at.time", report, response_type) - else: - self.__report_weather('current', report, response_type) - self.mark2_forecast(report) - - def __populate_report(self, message): - unit = self.__get_requested_unit(message) - # Get a date from requests like "weather for next Tuesday" - today, _ = self.__extract_datetime('today', lang='en-us') - when, _ = self.__extract_datetime( - message.data.get('utterance'), lang=self.lang) - when = when or today # Get todays date if None was found - self.log.debug('extracted when: {}'.format(when)) - - report = self.__initialize_report(message) - - # Check if user is asking for a specific time today - if when.date() == today.date() and when.time() != today.time(): - self.log.info("Forecast for time: {}".format(when)) - return self.__populate_for_time(report, when, unit) - # Check if user is asking for a specific day - elif today.date() != when.date(): - # Doesn't seem to be hitable, safety? - self.log.info("Forecast for: {} {}".format(today, when)) - return self.__populate_forecast(report, when, unit, - preface_day=True) - # Otherwise user is asking for weather right now - else: - self.log.info("Forecast for now") - return self.__populate_current(report, unit) - - return None - - def __populate_for_time(self, report, when, unit=None): - # TODO localize time to report location - # Return None if report is None - if report is None: - return None - - three_hr_fcs = self.owm.three_hours_forecast( - report['full_location'], - report['lat'], - report['lon']) - - if three_hr_fcs is None: - return None - - if not three_hr_fcs: - return None - earliest_fc = three_hr_fcs.get_forecast().get_weathers()[0] - if when < earliest_fc.get_reference_time(timeformat='date'): - fc_weather = earliest_fc + intent_data = WeatherIntent(message, self.lang) + except ValueError: + self.speak_dialog("cant.get.forecast") else: + print(self.voc_match(intent_data.utterance, "RelativeDay")) + if self.voc_match(intent_data.utterance, "RelativeTime"): + intent_data.timeframe = "hourly" + elif self.voc_match(intent_data.utterance, "Later"): + intent_data.timeframe = "hourly" + elif self.voc_match(intent_data.utterance, "RelativeDay"): + if not self.voc_match(intent_data.utterance, "Today"): + intent_data.timeframe = "daily" + + return intent_data + + def _get_weather(self, intent_data): + weather = None + if intent_data is not None: try: - fc_weather = three_hr_fcs.get_weather_at(when) - except Exception as e: - # fc_weather = three_hr_fcs.get_forecast().get_weathers()[0] - self.log.error("Error: {0}".format(e)) - return None - - report['condition'] = fc_weather.get_detailed_status() - report['condition_cat'] = fc_weather.get_status() - report['icon'] = fc_weather.get_weather_icon_name() - report['temp'] = self.__get_temperature(fc_weather, 'temp') - # Min and Max temps not available in 3hr forecast - report['temp_min'] = None - report['temp_max'] = None - report['humidity'] = self.translate('percentage.number', - {'num': fc_weather.get_humidity()}) - report['wind'] = self.get_wind_speed(fc_weather)[0] - - fc_time = fc_weather.get_reference_time(timeformat='date') - report['time'] = self.__to_time_period(self.__to_Local(fc_time)) - report['day'] = self.__to_day(when, preface=True) - - return report - - def __populate_current(self, report, unit=None): - # Return None if report is None - if report is None: - return None - - # Get current conditions - currentWeather = self.owm.weather_at_place( - report['full_location'], report['lat'], - report['lon']).get_weather() - - if currentWeather is None: - return None - - today = currentWeather.get_reference_time(timeformat='date') - self.log.debug("Populating report for now: {}".format(today)) - - # Get forecast for the day - # can get 'min', 'max', 'eve', 'morn', 'night', 'day' - # Set time to 12 instead of 00 to accomodate for timezones - forecastWeather = self.__get_forecast( - self.__to_Local(today), - report['full_location'], - report['lat'], - report['lon']) - - if forecastWeather is None: - return None - - # Change encoding of the localized report to utf8 if needed - condition = currentWeather.get_detailed_status() - if self.owm.encoding != 'utf8': - condition.encode(self.owm.encoding).decode('utf8') - report['condition'] = self.__translate(condition) - report['condition_cat'] = currentWeather.get_status() - - report['icon'] = currentWeather.get_weather_icon_name() - report['temp'] = self.__get_temperature(currentWeather, 'temp', - unit) - report['temp_min'] = self.__get_temperature(forecastWeather, 'min', - unit) - report['temp_max'] = self.__get_temperature(forecastWeather, 'max', - unit) - report['humidity'] = self.translate( - 'percentage.number', {'num': forecastWeather.get_humidity()}) - - wind = self.get_wind_speed(forecastWeather) - report['wind'] = "{} {}".format(wind[0], wind[1] or "") - today, _ = self.__extract_datetime('today', lang='en-us') - report['day'] = self.__to_day(today, preface=True) - - return report - - def __populate_forecast(self, report, when, unit=None, preface_day=False): - """ Populate the report and return it. - - Arguments: - report (dict): report base - when : date for report - unit: Unit type to use when presenting - - Returns: None if no report available otherwise dict with weather info - """ - self.log.debug("Populating forecast report for: {}".format(when)) - - # Return None if report is None - if report is None: - return None - - forecast_weather = self.__get_forecast( - when, report['full_location'], report['lat'], report['lon']) - - if forecast_weather is None: - return None # No forecast available - - # This converts a status like "sky is clear" to new text and tense, - # because you don't want: "Friday it will be 82 and the sky is clear", - # it should be 'Friday it will be 82 and the sky will be clear' - # or just 'Friday it will be 82 and clear. - # TODO: Run off of status IDs instead of text `.get_weather_code()`? - report['condition'] = self.__translate( - forecast_weather.get_detailed_status(), True) - report['condition_cat'] = forecast_weather.get_status() - - report['icon'] = forecast_weather.get_weather_icon_name() - # Can get temps for 'min', 'max', 'eve', 'morn', 'night', 'day' - report['temp'] = self.__get_temperature(forecast_weather, 'day', unit) - report['temp_min'] = self.__get_temperature(forecast_weather, 'min', - unit) - report['temp_max'] = self.__get_temperature(forecast_weather, 'max', - unit) - report['humidity'] = self.translate( - 'percentage.number', {'num': forecast_weather.get_humidity()}) - report['wind'] = self.get_wind_speed(forecast_weather)[0] - report['day'] = self.__to_day(when, preface_day) - - return report - - def __report_no_data(self, report_type, data=None): - """ Do processes when Report Processes malfunction - Arguments: - report_type (str): Report type where the error was from - i.e. 'weather', 'location' - data (dict): Needed data for dialog on weather error processing - Returns: - None - """ - if report_type == 'weather': - if data is None: + latitude, longitude = self._determine_weather_location(intent_data) + weather = self.weather_api_new.get_weather_for_coordinates( + self.config_core.get('system_unit'), latitude, longitude + ) + except HTTPError as api_error: + self.log.exception("Weather API failure") + self._handle_api_error(api_error) + except LocationNotFoundError: + self.log.exception("City not found.") + self.speak_dialog( + "location.not.found", + data=dict(location=intent_data.location) + ) + except Exception: + self.log.exception("Unexpected error retrieving weather") self.speak_dialog("cant.get.forecast") - else: - self.speak_dialog("no.forecast", data) - elif report_type == 'location': - self.speak_dialog('location.not.found') - - def __select_condition_dialog(self, message, report, noun, exp=None): - """ Select the relevant dialog file for condition based reports. - A condition can for example be "snow" or "rain". + return weather - Arguments: - message (obj): message from user - report (dict): weather report data - noun (string): name of condition eg snow - exp (string): condition as verb or adjective eg Snowing - - Returns: - dialog (string): name of dialog file - """ - if report is None: - # Empty report most likely caused by location not found - return 'do not know' - - if exp is None: - exp = noun - alternative_voc = '{}Alternatives'.format(noun.capitalize()) - if self.voc_match(report['condition'], exp.capitalize()): - dialog = 'affirmative.condition' - elif report.get('time'): - # Standard response for time based dialog eg 'evening' - if self.voc_match(report['condition'], alternative_voc): - dialog = 'cond.alternative' - else: - dialog = 'no.cond.predicted' - elif self.voc_match(report['condition'], alternative_voc): - dialog = '{}.alternative'.format(exp.lower()) + def _handle_api_error(self, exception): + if exception.response.status_code == 401: + self.bus.emit(Message("mycroft.not.paired")) else: - dialog = 'no.{}.predicted'.format(noun.lower()) - - if "Location" not in message.data: - dialog = 'local.' + dialog - if report.get('day'): - dialog = 'forecast.' + dialog - if (report.get('time') and - ('at.time.' + dialog) in self.dialog_renderer.templates): - dialog = 'at.time.' + dialog - return dialog - - def report_forecast(self, report, when, dialog='weather', unit=None, - preface_day=True): - """ Speak forecast for specific day. - - Arguments: - report (dict): report base - when : date for report - dialog (str): dialog type, defaults to 'weather' - unit: Unit type to use when presenting - preface_day (bool): if appropriate day preface should be added - eg "on Tuesday" but NOT "on tomorrow" - """ - report = self.__populate_forecast(report, when, unit, preface_day) - if report is None: - data = {'day': self.__to_day(when, preface_day)} - self.__report_no_data('weather', data) - return - - self.__report_weather('forecast', report, rtype=dialog) - - def report_multiday_forecast(self, report, when=None, - num_days=3, set_days=None, dialog='weather', - unit=None, preface_day=True): - """ Speak forecast for multiple sequential days. + self.speak_dialog("cant.get.forecast") - Arguments: - report (dict): report base - when (datetime): date of first day for report, defaults to today - num_days (int): number of days to report, defaults to 3 - set_days (list(datetime)): list of specific days to report - dialog (str): dialog type, defaults to 'weather' - unit: Unit type to use when presenting, defaults to user preference - preface_day (bool): if appropriate day preface should be added - eg "on Tuesday" but NOT "on tomorrow" - """ - - today, _ = self.__extract_datetime('today') - if when is None: - when = today - - if set_days: - days = set_days + def _determine_weather_location(self, intent_data): + if intent_data.location is None: + latitude = self.weather_config.latitude + longitude = self.weather_config.longitude else: - days = [when + timedelta(days=i) for i in range(num_days)] - - no_report = list() - for day in days: - if day == today: - self.__populate_current(report, day) - report['day'] = self.__to_day(day, preface_day) - self.__report_weather('forecast', report, rtype=dialog) - else: - report = self.__populate_forecast(report, day, unit, - preface_day) - if report is None: - no_report.append(self.__to_day(day, False)) - continue - self.__report_weather('forecast', report, rtype=dialog) - - if no_report: - dates = join_list(no_report, 'and') - dates = self.translate('on') + ' ' + dates - data = {'day': dates} - self.__report_no_data('weather', data) - - def __report_weather(self, timeframe, report, rtype='weather', - separate_min_max=False): - """ Report the weather verbally and visually. - - Produces an utterance based on the timeframe and rtype parameters. - The report also provides location context. The dialog file used will - be: - "timeframe(.local).rtype" + latitude = intent_data.geolocation["latitude"] + longitude = intent_data.geolocation["longitude"] - Arguments: - timeframe (str): 'current' or 'future'. - report (dict): Dictionary with report information (temperatures - and such. - rtype (str): report type, defaults to 'weather' - separate_min_max (bool): a separate dialog for min max temperatures - will be output if True (default: False) - """ + return latitude, longitude - # Convert code to matching weather icon on Mark 1 - if report['location']: - report['location'] = self.owm.location_translations.get( - report['location'], report['location']) - weather_code = str(report['icon']) - img_code = self.CODES[weather_code] - - # Display info on a screen - # Mark-2 - self.gui["current"] = report["temp"] - self.gui["min"] = report["temp_min"] - self.gui["max"] = report["temp_max"] - self.gui["location"] = report["full_location"].replace(', ', '\n') - self.gui["condition"] = report["condition"] - self.gui["icon"] = report["icon"] - self.gui["weathercode"] = img_code - self.gui["humidity"] = report.get("humidity", "--") - self.gui["wind"] = report.get("wind", "--") - self.gui.show_pages(["weather.qml", "highlow.qml", - "forecast1.qml", "forecast2.qml"]) - # Mark-1 - self.enclosure.deactivate_mouth_events() - self.enclosure.weather_display(img_code, report['temp']) - - dialog_name = timeframe - if report['location'] == self.location_pretty: - dialog_name += ".local" - dialog_name += "." + rtype - self.log.debug("Dialog: " + dialog_name) - self.speak_dialog(dialog_name, report) - - # Just show the icons while still speaking + def _speak_weather(self, dialog: WeatherDialog): + self.log.info("Speaking dialog: " + dialog.name) + self.speak_dialog(dialog.name, dialog.data) mycroft.audio.wait_while_speaking() - # Speak the high and low temperatures - if separate_min_max: - self.speak_dialog('min.max', report) - self.gui.show_page("highlow.qml") - mycroft.audio.wait_while_speaking() - - self.enclosure.activate_mouth_events() - self.enclosure.mouth_reset() - - def __report_condition(self, name, value, when, location=None): - # Report a specific value - data = { - "condition": name, - "value": value, - } - report_type = "report.condition" - today, _ = self.__extract_datetime("today") - if when and when.date() != today.date(): - data["day"] = self.__to_day(when, preface=True) - report_type += ".future" - if location: - data["location"] = location - report_type += ".at.location" - self.speak_dialog(report_type, data) - - def __get_forecast(self, when, location, lat, lon): - """ Get a forecast for the given time and location. + def _report_no_data(self, data: dict = None) -> None: + """Do processes when Report Processes malfunction Arguments: - when (datetime): Local datetime for report - location: location - lat: Latitude for report - lon: Longitude for report + data: Needed data for dialog on weather error processing """ - - # search for the requested date in the returned forecast data - forecasts = self.owm.daily_forecast(location, lat, lon, limit=14) - forecasts = forecasts.get_forecast() - for weather in forecasts.get_weathers(): - forecastDate = weather.get_reference_time("date") - if forecastDate.date() == when.date(): - # found the right day, now format up the results - return weather - - # No forecast for the given day - return None - - def __get_requested_unit(self, message): - """ Get selected unit from message. - - Arguments: - message (Message): messagebus message from intent service - - Returns: - 'fahrenheit', 'celsius' or None - """ - if message and message.data and 'Unit' in message.data: - if self.voc_match(message.data['Unit'], 'Fahrenheit'): - return 'fahrenheit' - else: - return 'celsius' + if data is None: + self.speak_dialog("cant.get.forecast") else: - return None - - def concat_dialog(self, current, dialog, data=None): - return current + " " + self.translate(dialog, data) - - def __get_seqs_from_list(self, nums): - """Get lists of sequential numbers from list. - - Arguments: - nums (list): list of int eg indices - - Returns: - None if no sequential numbers found - seq_nums (list[list]): list of sequence lists - """ - current_seq, seq_nums = [], [] - seq_active = False - for idx, day in enumerate(nums): - if idx+1 < len(nums) and nums[idx+1] == (day + 1): - current_seq.append(day) - seq_active = True - elif seq_active: - # last day in sequence - current_seq.append(day) - seq_nums.append(current_seq.copy()) - current_seq = [] - seq_active = False - - # if len(seq_nums) == 0: - # return None - return seq_nums - - def __get_speed_unit(self): - """ Get speed unit based on config setting. - - Config setting of 'metric' will return "meters_sec", otherwise 'mph' - - Returns: (str) 'meters_sec' or 'mph' - """ - system_unit = self.config_core.get('system_unit') - return system_unit == "metric" and "meters_sec" or "mph" - - def __get_temperature_unit(self): - """ Get temperature unit from config and skill settings. - - Config setting of 'metric' implies celsius for unit - - Returns: (str) "celcius" or "fahrenheit" - """ - system_unit = self.config_core.get('system_unit') - override = self.settings.get("units", "") - if override: - if override[0].lower() == "f": - return "fahrenheit" - elif override[0].lower() == "c": - return "celsius" - - return system_unit == "metric" and "celsius" or "fahrenheit" - - def __get_temperature(self, weather, key, unit=None): - # Extract one of the temperatures from the weather data. - # Typically it has: 'temp', 'min', 'max', 'morn', 'day', 'night' - try: - unit = unit or self.__get_temperature_unit() - # fallback to general temperature if missing - temp = weather.get_temperature(unit)[key] - if temp is not None: - return str(int(round(temp))) - else: - return '' - except Exception as e: - self.log.warning('No temperature available ({})'.format(repr(e))) - return '' - - def __api_error(self, e): - if isinstance(e, LocationNotFoundError): - self.speak_dialog('location.not.found') - elif e.response.status_code == 401: - from mycroft import Message - self.bus.emit(Message("mycroft.not.paired")) - else: - self.__report_no_data('weather') - - def __to_day(self, when, preface=False): - """ Provide date in speakable form - - Arguments: - when (datetime) - preface (bool): if appropriate preface should be included - eg "on Monday" but NOT "on tomorrow" - Returns: - string: the speakable date text - """ - now = datetime.now() - speakable_date = nice_date(when, lang=self.lang, now=now) - # Test if speakable_date is a relative reference eg "tomorrow" - days_diff = (when.date() - now.date()).days - if preface and (-1 > days_diff or days_diff > 1): - speakable_date = "{} {}".format(self.translate('on.date'), - speakable_date) - # If day is less than a week in advance, just say day of week. - if days_diff <= 6: - speakable_date = speakable_date.split(',')[0] - return speakable_date - - def __to_Local(self, when): - try: - # First try with modern mycroft.util.time functions - return to_local(when) - except Exception: - # Fallback to the old pytz code - if not when.tzinfo: - when = when.replace(tzinfo=pytz.utc) - timezone = pytz.timezone(self.location["timezone"]["code"]) - return when.astimezone(timezone) - - def __to_time_period(self, when): - # Translate a specific time '9am' to period of the day 'morning' - hour = when.time().hour - period = None - if hour >= 1 and hour < 5: - period = "early morning" - if hour >= 5 and hour < 12: - period = "morning" - if hour >= 12 and hour < 17: - period = "afternoon" - if hour >= 17 and hour < 20: - period = "evening" - if hour >= 20 or hour < 1: - period = "overnight" - if period is None: - self.log.error("Unable to parse time as a period of day") - return period - - # Suggestion TODO: Add a parameter to extract_datetime to add a default Timezone - def __extract_datetime(self, text, anchorDate=None, lang=None, default_time=None): - # Change timezone returned by extract_datetime from Local to UTC - extracted_dt = extract_datetime(text, anchorDate, lang, default_time) - if extracted_dt is None: - # allow calls to unpack values even if None returned. - return (None, None) - when, text = extracted_dt - return to_utc(when), text + self.speak_dialog("no.forecast", data) def __translate(self, condition, future=False, data=None): # behaviour of method dialog_renderer.render(...) has changed - instead @@ -1809,15 +605,6 @@ def __translate(self, condition, future=False, data=None): else: return condition - def __nice_time(self, dt, lang="en-us", speech=True, use_24hour=False, - use_ampm=False): - # compatibility wrapper for nice_time - nt_supported_languages = ['en', 'es', 'it', 'fr', 'de', - 'hu', 'nl', 'da'] - if not (lang[0:2] in nt_supported_languages): - lang = "en-us" - return nice_time(dt, lang, speech, use_24hour, use_ampm) - def create_skill(): return WeatherSkill() diff --git a/dialog/en-us/RelativeDay.voc b/dialog/en-us/RelativeDay.voc deleted file mode 100644 index ebcd27a6..00000000 --- a/dialog/en-us/RelativeDay.voc +++ /dev/null @@ -1,6 +0,0 @@ -today -today's -tomorrow -tomorrow's -yesterday -yesterday's diff --git a/dialog/en-us/and.dialog b/dialog/en-us/and.dialog deleted file mode 100644 index cfa2dcfb..00000000 --- a/dialog/en-us/and.dialog +++ /dev/null @@ -1 +0,0 @@ -, and diff --git a/dialog/en-us/at.time.forecast.affirmative.condition.dialog b/dialog/en-us/at.time.forecast.affirmative.condition.dialog deleted file mode 100644 index 2955d7db..00000000 --- a/dialog/en-us/at.time.forecast.affirmative.condition.dialog +++ /dev/null @@ -1,3 +0,0 @@ -# Affirmative response to questions like "is it going to snow in Paris tomorrow afternoon" -Yes, the {time} forecast suggests {condition} in {location} {day} -Yes, the {time} forecast calls for {condition} in {location} {day} diff --git a/dialog/en-us/at.time.forecast.cond.alternative.dialog b/dialog/en-us/at.time.forecast.cond.alternative.dialog deleted file mode 100644 index 4ff83c19..00000000 --- a/dialog/en-us/at.time.forecast.cond.alternative.dialog +++ /dev/null @@ -1,3 +0,0 @@ -# Informing that an alternative to clear skies will occur at specified time -No, the {time} forecast {day} calls for {condition} in {location} -Doesn't seem so, the {time} forecast {day} suggests {location} will have {condition} diff --git a/dialog/en-us/at.time.forecast.local.affirmative.condition.dialog b/dialog/en-us/at.time.forecast.local.affirmative.condition.dialog deleted file mode 100644 index c1fb5a12..00000000 --- a/dialog/en-us/at.time.forecast.local.affirmative.condition.dialog +++ /dev/null @@ -1,3 +0,0 @@ -# Affirmative response to questions like "is it going to snow tomorrow" -Yes, expect {condition} according to the {time} forecast {day} -Yes, the {time} forecast calls for {condition} {day} diff --git a/dialog/en-us/at.time.forecast.local.cond.alternative.dialog b/dialog/en-us/at.time.forecast.local.cond.alternative.dialog deleted file mode 100644 index a6d9dbfa..00000000 --- a/dialog/en-us/at.time.forecast.local.cond.alternative.dialog +++ /dev/null @@ -1,3 +0,0 @@ -# Informing that an alternative to clear skies will occur -No, the {time} forecast {day} calls for {condition} -Doesn't seem so, the {time} forecast {day} suggests it's going to be {condition} diff --git a/dialog/en-us/at.time.forecast.local.no.cond.predicted.dialog b/dialog/en-us/at.time.forecast.local.no.cond.predicted.dialog deleted file mode 100644 index d787d2e8..00000000 --- a/dialog/en-us/at.time.forecast.local.no.cond.predicted.dialog +++ /dev/null @@ -1,2 +0,0 @@ -# When user asks if it's one condition but it's not -No, the {time} forecast {day} suggests {condition} diff --git a/dialog/en-us/at.time.forecast.no.cond.predicted.dialog b/dialog/en-us/at.time.forecast.no.cond.predicted.dialog deleted file mode 100644 index ff6d148e..00000000 --- a/dialog/en-us/at.time.forecast.no.cond.predicted.dialog +++ /dev/null @@ -1,3 +0,0 @@ -# When user asks if it's a condition but it's not -No, the {time} forecast in {location} suggests it will be {condition} -It doesn't seem so, the {time} forecast in {location} suggests it will be {condition} diff --git a/dialog/en-us/at.time.local.high.temperature.dialog b/dialog/en-us/at.time.local.high.temperature.dialog deleted file mode 100644 index 38a48b84..00000000 --- a/dialog/en-us/at.time.local.high.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be as high as {temp} degrees {time} -{time}, it will get up to {temp} degrees diff --git a/dialog/en-us/at.time.local.low.temperature.dialog b/dialog/en-us/at.time.local.low.temperature.dialog deleted file mode 100644 index 7b074b0b..00000000 --- a/dialog/en-us/at.time.local.low.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be as low as {temp} degrees {time} -{time}, it will get down to {temp} degrees diff --git a/dialog/en-us/at.time.local.no.cond.predicted.dialog b/dialog/en-us/at.time.local.no.cond.predicted.dialog deleted file mode 100644 index f4b343c0..00000000 --- a/dialog/en-us/at.time.local.no.cond.predicted.dialog +++ /dev/null @@ -1,2 +0,0 @@ -# When user asks if it's raining but no cloud or alternative is forcasted -No the {time} forecast suggests not diff --git a/dialog/en-us/at.time.local.temperature.dialog b/dialog/en-us/at.time.local.temperature.dialog deleted file mode 100644 index a9ee4112..00000000 --- a/dialog/en-us/at.time.local.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be about {temp} degrees {time} -{time}, it will be {temp} degrees diff --git a/dialog/en-us/at.time.local.weather.dialog b/dialog/en-us/at.time.local.weather.dialog deleted file mode 100644 index 4f00a10f..00000000 --- a/dialog/en-us/at.time.local.weather.dialog +++ /dev/null @@ -1,4 +0,0 @@ -It will be {condition}, with temperatures near {temp} in the {time} -{time}, it will be {condition} and around {temp} degrees -{time}, it will be {condition} and {temp} degrees -{time}, it will be {temp} degrees with {condition} diff --git a/dialog/en-us/at.time.no.cond.predicted.dialog b/dialog/en-us/at.time.no.cond.predicted.dialog deleted file mode 100644 index 708d79d4..00000000 --- a/dialog/en-us/at.time.no.cond.predicted.dialog +++ /dev/null @@ -1,2 +0,0 @@ -# When user asks if it's a conditions but it's not -No, the {time} forecast in {location} suggests not diff --git a/dialog/en-us/condition.category.value b/dialog/en-us/condition.category.value deleted file mode 100644 index d8a78348..00000000 --- a/dialog/en-us/condition.category.value +++ /dev/null @@ -1,15 +0,0 @@ -Clouds,cloudy -Clear,a clear sky -Thunderstorm,storming -Drizzle,drizzling -Rain,raining -Snow,snowing -Mist,misty -Smoke,smokey -Haze,hazey -Dust,dusty -Fog,foggy -Sand,sandy -Ash,cloudy with possible volcanic ash -Squall,storming -Tornado,storming with a possible tornado diff --git a/dialog/en-us/clear.alternative.dialog b/dialog/en-us/current.clear.alternative.location.dialog similarity index 100% rename from dialog/en-us/clear.alternative.dialog rename to dialog/en-us/current.clear.alternative.location.dialog diff --git a/dialog/en-us/local.clear.alternative.dialog b/dialog/en-us/current.clear.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.clear.alternative.dialog rename to dialog/en-us/current.clear.not.expected.local.dialog diff --git a/dialog/en-us/no.clear.predicted.dialog b/dialog/en-us/current.clear.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.clear.predicted.dialog rename to dialog/en-us/current.clear.not.expected.location.dialog diff --git a/dialog/en-us/cloudy.alternative.dialog b/dialog/en-us/current.clouds.alternative.location.dialog similarity index 100% rename from dialog/en-us/cloudy.alternative.dialog rename to dialog/en-us/current.clouds.alternative.location.dialog diff --git a/dialog/en-us/local.no.cloudy.predicted.dialog b/dialog/en-us/current.clouds.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.no.cloudy.predicted.dialog rename to dialog/en-us/current.clouds.not.expected.local.dialog diff --git a/dialog/en-us/no.cloudy.predicted.dialog b/dialog/en-us/current.clouds.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.cloudy.predicted.dialog rename to dialog/en-us/current.clouds.not.expected.location.dialog diff --git a/dialog/en-us/local.affirmative.condition.dialog b/dialog/en-us/current.condition.expected.local.dialog similarity index 100% rename from dialog/en-us/local.affirmative.condition.dialog rename to dialog/en-us/current.condition.expected.local.dialog diff --git a/dialog/en-us/affirmative.condition.dialog b/dialog/en-us/current.condition.expected.location.dialog similarity index 100% rename from dialog/en-us/affirmative.condition.dialog rename to dialog/en-us/current.condition.expected.location.dialog diff --git a/dialog/en-us/local.foggy.alternative.dialog b/dialog/en-us/current.fog.alternative.local.dialog similarity index 100% rename from dialog/en-us/local.foggy.alternative.dialog rename to dialog/en-us/current.fog.alternative.local.dialog diff --git a/dialog/en-us/fog.alternative.dialog b/dialog/en-us/current.fog.alternative.location.dialog similarity index 100% rename from dialog/en-us/fog.alternative.dialog rename to dialog/en-us/current.fog.alternative.location.dialog diff --git a/dialog/en-us/local.no.fog.predicted.dialog b/dialog/en-us/current.fog.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.no.fog.predicted.dialog rename to dialog/en-us/current.fog.not.expected.local.dialog diff --git a/dialog/en-us/no.fog.predicted.dialog b/dialog/en-us/current.fog.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.fog.predicted.dialog rename to dialog/en-us/current.fog.not.expected.location.dialog diff --git a/dialog/en-us/current.high.temperature.dialog b/dialog/en-us/current.high.temperature.dialog deleted file mode 100644 index 36619745..00000000 --- a/dialog/en-us/current.high.temperature.dialog +++ /dev/null @@ -1,4 +0,0 @@ -A high of {temp_max} degrees {scale} is expected in {location}. -A high of {temp_max} degrees can be expected in {location}. -Today a the temperature will reach {temp_max} degrees in {location}. - diff --git a/dialog/en-us/current.hot.dialog b/dialog/en-us/current.hot.dialog deleted file mode 100644 index dab62958..00000000 --- a/dialog/en-us/current.hot.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It's {temp} degrees {scale} in {location}, well within my operational temperature. -It's {temp} degrees in {location} which suits an A.I. just fine. diff --git a/dialog/en-us/current.humidity.local.dialog b/dialog/en-us/current.humidity.local.dialog new file mode 100644 index 00000000..1c3cf997 --- /dev/null +++ b/dialog/en-us/current.humidity.local.dialog @@ -0,0 +1 @@ +Currently, the humidity is {percent} diff --git a/dialog/en-us/current.humidity.location.dialog b/dialog/en-us/current.humidity.location.dialog new file mode 100644 index 00000000..5e9b9fb3 --- /dev/null +++ b/dialog/en-us/current.humidity.location.dialog @@ -0,0 +1 @@ +Currently, the humidity in {location} is {percent} diff --git a/dialog/en-us/current.local.cold.dialog b/dialog/en-us/current.local.cold.dialog deleted file mode 100644 index a2d3aec2..00000000 --- a/dialog/en-us/current.local.cold.dialog +++ /dev/null @@ -1,2 +0,0 @@ -currently it's {temp}, well within my operational temperature -It's {temp}, quite comfy for an A. I. diff --git a/dialog/en-us/current.local.high.temperature.dialog b/dialog/en-us/current.local.high.temperature.dialog deleted file mode 100644 index 789f7c3e..00000000 --- a/dialog/en-us/current.local.high.temperature.dialog +++ /dev/null @@ -1,4 +0,0 @@ -A high of {temp_max} degrees {scale} is expected. -A high of {temp_max} degrees can be expected. -Today the temperature will reach {temp_max} degrees. - diff --git a/dialog/en-us/current.local.hot.dialog b/dialog/en-us/current.local.hot.dialog deleted file mode 100644 index 8b6a065c..00000000 --- a/dialog/en-us/current.local.hot.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It's within my operational temperature, currently at {temp} degrees -It's {temp} degrees, quite comfy for an AI diff --git a/dialog/en-us/current.local.low.temperature.dialog b/dialog/en-us/current.local.low.temperature.dialog deleted file mode 100644 index 8cde8694..00000000 --- a/dialog/en-us/current.local.low.temperature.dialog +++ /dev/null @@ -1,4 +0,0 @@ -A low of {temp_min} degrees {scale} is expected. -A low of {temp_min} degrees can be expected. -Today it will be as low as {temp_min} degrees. - diff --git a/dialog/en-us/current.local.temperature.dialog b/dialog/en-us/current.local.temperature.dialog deleted file mode 100644 index 728a4c23..00000000 --- a/dialog/en-us/current.local.temperature.dialog +++ /dev/null @@ -1,4 +0,0 @@ -It's currently {temp} degrees {scale}. -It's currently {temp} degrees. -Right now, it's {temp} degrees. - diff --git a/dialog/en-us/current.local.weather.dialog b/dialog/en-us/current.local.weather.dialog deleted file mode 100644 index b38fd0a1..00000000 --- a/dialog/en-us/current.local.weather.dialog +++ /dev/null @@ -1,3 +0,0 @@ -It's currently {condition} and {temp} degrees {scale}. -It's currently {condition} and {temp} degrees. -Right now, it's {condition} and {temp} degrees. diff --git a/dialog/en-us/current.low.temperature.dialog b/dialog/en-us/current.low.temperature.dialog deleted file mode 100644 index 0f45c4e5..00000000 --- a/dialog/en-us/current.low.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -Today the temperature will be as low as {temp_min} degrees {scale} in {location}. -Temperatures can be as low as {temp_min} degrees in {location}. diff --git a/dialog/en-us/local.raining.alternative.dialog b/dialog/en-us/current.rain.alternative.local.dialog similarity index 100% rename from dialog/en-us/local.raining.alternative.dialog rename to dialog/en-us/current.rain.alternative.local.dialog diff --git a/dialog/en-us/raining.alternative.dialog b/dialog/en-us/current.rain.alternative.location.dialog similarity index 100% rename from dialog/en-us/raining.alternative.dialog rename to dialog/en-us/current.rain.alternative.location.dialog diff --git a/dialog/en-us/local.no.rain.predicted.dialog b/dialog/en-us/current.rain.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.no.rain.predicted.dialog rename to dialog/en-us/current.rain.not.expected.local.dialog diff --git a/dialog/en-us/no.rain.predicted.dialog b/dialog/en-us/current.rain.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.rain.predicted.dialog rename to dialog/en-us/current.rain.not.expected.location.dialog diff --git a/dialog/en-us/local.snowing.alternative.dialog b/dialog/en-us/current.snow.alternative.local.dialog similarity index 100% rename from dialog/en-us/local.snowing.alternative.dialog rename to dialog/en-us/current.snow.alternative.local.dialog diff --git a/dialog/en-us/snowing.alternative.dialog b/dialog/en-us/current.snow.alternative.location.dialog similarity index 100% rename from dialog/en-us/snowing.alternative.dialog rename to dialog/en-us/current.snow.alternative.location.dialog diff --git a/dialog/en-us/local.no.snow.predicted.dialog b/dialog/en-us/current.snow.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.no.snow.predicted.dialog rename to dialog/en-us/current.snow.not.expected.local.dialog diff --git a/dialog/en-us/no.snow.predicted.dialog b/dialog/en-us/current.snow.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.snow.predicted.dialog rename to dialog/en-us/current.snow.not.expected.location.dialog diff --git a/dialog/en-us/current.sunrise.future.local.dialog b/dialog/en-us/current.sunrise.future.local.dialog new file mode 100644 index 00000000..59405362 --- /dev/null +++ b/dialog/en-us/current.sunrise.future.local.dialog @@ -0,0 +1,2 @@ +the sun will rise at {time} today +sunrise will be at {time} today diff --git a/dialog/en-us/current.sunrise.future.location.dialog b/dialog/en-us/current.sunrise.future.location.dialog new file mode 100644 index 00000000..81a9fe72 --- /dev/null +++ b/dialog/en-us/current.sunrise.future.location.dialog @@ -0,0 +1,2 @@ +the sun rose at {time} today in {location} +sunrise was at {time} today in {location} diff --git a/dialog/en-us/sunrise.dialog b/dialog/en-us/current.sunrise.past.local.dialog similarity index 100% rename from dialog/en-us/sunrise.dialog rename to dialog/en-us/current.sunrise.past.local.dialog diff --git a/dialog/en-us/current.sunrise.past.location.dialog b/dialog/en-us/current.sunrise.past.location.dialog new file mode 100644 index 00000000..81a9fe72 --- /dev/null +++ b/dialog/en-us/current.sunrise.past.location.dialog @@ -0,0 +1,2 @@ +the sun rose at {time} today in {location} +sunrise was at {time} today in {location} diff --git a/dialog/en-us/sunset.dialog b/dialog/en-us/current.sunset.future.local.dialog similarity index 100% rename from dialog/en-us/sunset.dialog rename to dialog/en-us/current.sunset.future.local.dialog diff --git a/dialog/en-us/current.sunset.future.location.dialog b/dialog/en-us/current.sunset.future.location.dialog new file mode 100644 index 00000000..46a732c9 --- /dev/null +++ b/dialog/en-us/current.sunset.future.location.dialog @@ -0,0 +1,3 @@ +the sun will set at {time} today in {location} +in {location} the sun will go down at {time} today +sunset will be at {time} today in {location} diff --git a/dialog/en-us/current.sunset.past.local.dialog b/dialog/en-us/current.sunset.past.local.dialog new file mode 100644 index 00000000..e8987e39 --- /dev/null +++ b/dialog/en-us/current.sunset.past.local.dialog @@ -0,0 +1,3 @@ +the sun set at {time} today +the sun went down at {time} today +sunset was at {time} today diff --git a/dialog/en-us/current.sunset.past.location.dialog b/dialog/en-us/current.sunset.past.location.dialog new file mode 100644 index 00000000..bc24bede --- /dev/null +++ b/dialog/en-us/current.sunset.past.location.dialog @@ -0,0 +1,3 @@ +the sun set at {time} today in {location} +in {location} the sun went down at {time} today +sunset was at {time} today in {location} diff --git a/dialog/en-us/current.temperature.dialog b/dialog/en-us/current.temperature.dialog deleted file mode 100644 index c7857bef..00000000 --- a/dialog/en-us/current.temperature.dialog +++ /dev/null @@ -1,4 +0,0 @@ -It's currently {temp} degrees {scale} in {location}. -It's currently {temp} degrees in {location}. -Right now, it's {temp} degrees in {location}. - diff --git a/dialog/en-us/current.temperature.high.local.dialog b/dialog/en-us/current.temperature.high.local.dialog new file mode 100644 index 00000000..a4e3c12c --- /dev/null +++ b/dialog/en-us/current.temperature.high.local.dialog @@ -0,0 +1,4 @@ +A high of {temperature} degrees {temperature_unit} is expected. +A high of {temperature} degrees can be expected. +Today the temperature will reach {temperature} degrees. + diff --git a/dialog/en-us/current.temperature.high.location.dialog b/dialog/en-us/current.temperature.high.location.dialog new file mode 100644 index 00000000..a9a40040 --- /dev/null +++ b/dialog/en-us/current.temperature.high.location.dialog @@ -0,0 +1,4 @@ +A high of {temperature} degrees {temperature_unit} is expected in {location}. +A high of {temperature} degrees can be expected in {location}. +Today a the temperature will reach {temperature} degrees in {location}. + diff --git a/dialog/en-us/current.temperature.high.low.dialog b/dialog/en-us/current.temperature.high.low.dialog new file mode 100644 index 00000000..9b88dc9f --- /dev/null +++ b/dialog/en-us/current.temperature.high.low.dialog @@ -0,0 +1 @@ +Today's forecast is for a high of {high_temperature} and a low of {low_temperature}. diff --git a/dialog/en-us/current.temperature.local.dialog b/dialog/en-us/current.temperature.local.dialog new file mode 100644 index 00000000..e01594ba --- /dev/null +++ b/dialog/en-us/current.temperature.local.dialog @@ -0,0 +1,4 @@ +It's currently {temperature} degrees {temperature_unit}. +It's currently {temperature} degrees. +Right now, it's {temperature} degrees. + diff --git a/dialog/en-us/current.temperature.location.dialog b/dialog/en-us/current.temperature.location.dialog new file mode 100644 index 00000000..7cd53599 --- /dev/null +++ b/dialog/en-us/current.temperature.location.dialog @@ -0,0 +1,4 @@ +It's currently {temperature} degrees {temperature_unit} in {location}. +It's currently {temperature} degrees in {location}. +Right now, it's {temperature} degrees in {location}. + diff --git a/dialog/en-us/current.temperature.low.local.dialog b/dialog/en-us/current.temperature.low.local.dialog new file mode 100644 index 00000000..4388ed1d --- /dev/null +++ b/dialog/en-us/current.temperature.low.local.dialog @@ -0,0 +1,4 @@ +A low of {temperature} degrees {temperature_unit} is expected. +A low of {temperature} degrees can be expected. +Today it will be as low as {temperature} degrees. + diff --git a/dialog/en-us/current.temperature.low.location.dialog b/dialog/en-us/current.temperature.low.location.dialog new file mode 100644 index 00000000..72d6b4db --- /dev/null +++ b/dialog/en-us/current.temperature.low.location.dialog @@ -0,0 +1,2 @@ +Today the temperature will be as low as {temperature} degrees {temperature_unit} in {location}. +Temperatures can be as low as {temperature} degrees in {location}. diff --git a/dialog/en-us/local.storm.alternative.dialog b/dialog/en-us/current.thunderstorm.alternative.local.dialog similarity index 100% rename from dialog/en-us/local.storm.alternative.dialog rename to dialog/en-us/current.thunderstorm.alternative.local.dialog diff --git a/dialog/en-us/storm.alternative.dialog b/dialog/en-us/current.thunderstorm.alternative.location.dialog similarity index 100% rename from dialog/en-us/storm.alternative.dialog rename to dialog/en-us/current.thunderstorm.alternative.location.dialog diff --git a/dialog/en-us/local.no.storm.predicted.dialog b/dialog/en-us/current.thunderstorm.not.expected.local.dialog similarity index 100% rename from dialog/en-us/local.no.storm.predicted.dialog rename to dialog/en-us/current.thunderstorm.not.expected.local.dialog diff --git a/dialog/en-us/no.storm.predicted.dialog b/dialog/en-us/current.thunderstorm.not.expected.location.dialog similarity index 100% rename from dialog/en-us/no.storm.predicted.dialog rename to dialog/en-us/current.thunderstorm.not.expected.location.dialog diff --git a/dialog/en-us/current.weather.dialog b/dialog/en-us/current.weather.dialog deleted file mode 100644 index 72eed86d..00000000 --- a/dialog/en-us/current.weather.dialog +++ /dev/null @@ -1,3 +0,0 @@ -It's currently {condition} and {temp} degrees {scale} in {location}. Today's forecast is for a high of {temp_max} and a low of {temp_min}. -Right now, it's {condition} and {temp} degrees, for a high of {temp_max} and a low of {temp_min} in {location}. -With a high of {temp_max} and a low of {temp_min}, {location} has {condition} and is currently {temp} degrees. diff --git a/dialog/en-us/current.weather.local.dialog b/dialog/en-us/current.weather.local.dialog new file mode 100644 index 00000000..ee85a282 --- /dev/null +++ b/dialog/en-us/current.weather.local.dialog @@ -0,0 +1,3 @@ +It's currently {condition} and {temperature} degrees {temperature_unit}. +It's currently {condition} and {temperature} degrees. +Right now, it's {condition} and {temperature} degrees. diff --git a/dialog/en-us/current.weather.location.dialog b/dialog/en-us/current.weather.location.dialog new file mode 100644 index 00000000..7453f380 --- /dev/null +++ b/dialog/en-us/current.weather.location.dialog @@ -0,0 +1,3 @@ +It's currently {condition} and {temperature} degrees {temperature_unit} in {location}. +Right now, it's {condition} and {temperature} degrees in {location}. +{location} has {condition} and is currently {temperature} degrees. diff --git a/dialog/en-us/current.wind.light.local.dialog b/dialog/en-us/current.wind.light.local.dialog new file mode 100644 index 00000000..75da0bb2 --- /dev/null +++ b/dialog/en-us/current.wind.light.local.dialog @@ -0,0 +1,2 @@ +The wind is light at {speed} {speed_unit} from the {direction} +Today there is light wind from the {direction} at {speed} {speed_unit} diff --git a/dialog/en-us/current.wind.light.location.dialog b/dialog/en-us/current.wind.light.location.dialog new file mode 100644 index 00000000..53551eae --- /dev/null +++ b/dialog/en-us/current.wind.light.location.dialog @@ -0,0 +1,2 @@ +The wind is light in {location} at {speed} {speed_unit} from the {direction} +In {location} today there is light wind from the {direction} at {speed} {speed_unit} diff --git a/dialog/en-us/current.wind.moderate.local.dialog b/dialog/en-us/current.wind.moderate.local.dialog new file mode 100644 index 00000000..4ed52f18 --- /dev/null +++ b/dialog/en-us/current.wind.moderate.local.dialog @@ -0,0 +1,2 @@ +Currently the wind is a moderate {speed} {speed_unit} from the {direction} +It's a bit windy today, currently {speed} {speed_unit} diff --git a/dialog/en-us/current.wind.moderate.location.dialog b/dialog/en-us/current.wind.moderate.location.dialog new file mode 100644 index 00000000..effb2d47 --- /dev/null +++ b/dialog/en-us/current.wind.moderate.location.dialog @@ -0,0 +1,2 @@ +Currently the wind is a moderate {speed} {speed_unit} from the {direction} in {location} +It's a bit windy in {location} today, currently {speed} {speed_unit} diff --git a/dialog/en-us/current.wind.strong.local.dialog b/dialog/en-us/current.wind.strong.local.dialog new file mode 100644 index 00000000..5bf2cce8 --- /dev/null +++ b/dialog/en-us/current.wind.strong.local.dialog @@ -0,0 +1,3 @@ +Currently, the wind is from the {direction} at {speed} {speed_unit}, might be good to stay inside today +The wind is very strong from the {direction} today, {speed} {speed_unit} +It's very windy today, {speed} {speed_unit} diff --git a/dialog/en-us/current.wind.strong.location.dialog b/dialog/en-us/current.wind.strong.location.dialog new file mode 100644 index 00000000..c80c5a26 --- /dev/null +++ b/dialog/en-us/current.wind.strong.location.dialog @@ -0,0 +1,3 @@ +Currently, the wind is from the {direction} at {speed} {speed_unit} in {location}, a good day to stay inside +The wind is very strong from the {direction} in {location} today, {speed} {speed_unit} +{location} will have strong winds today, {speed} {speed_unit} diff --git a/dialog/en-us/local.cloudy.alternative.dialog b/dialog/en-us/currrent.clouds.alternative.local.dialog similarity index 100% rename from dialog/en-us/local.cloudy.alternative.dialog rename to dialog/en-us/currrent.clouds.alternative.local.dialog diff --git a/dialog/en-us/forecast.local.clear.alternative.dialog b/dialog/en-us/daily.clear.alternative.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.clear.alternative.dialog rename to dialog/en-us/daily.clear.alternative.local.dialog diff --git a/dialog/en-us/forecast.clear.alternative.dialog b/dialog/en-us/daily.clear.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.clear.alternative.dialog rename to dialog/en-us/daily.clear.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.clear.predicted.dialog b/dialog/en-us/daily.clear.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.clear.predicted.dialog rename to dialog/en-us/daily.clear.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.clear.predicted.dialog b/dialog/en-us/daily.clear.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.clear.predicted.dialog rename to dialog/en-us/daily.clear.not.expected.location.dialog diff --git a/dialog/en-us/forecast.local.cloudy.alternative.dialog b/dialog/en-us/daily.clouds.alternative.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.cloudy.alternative.dialog rename to dialog/en-us/daily.clouds.alternative.local.dialog diff --git a/dialog/en-us/forecast.cloudy.alternative.dialog b/dialog/en-us/daily.clouds.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.cloudy.alternative.dialog rename to dialog/en-us/daily.clouds.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.cloudy.predicted.dialog b/dialog/en-us/daily.clouds.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.cloudy.predicted.dialog rename to dialog/en-us/daily.clouds.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.cloudy.predicted.dialog b/dialog/en-us/daily.clouds.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.cloudy.predicted.dialog rename to dialog/en-us/daily.clouds.not.expected.location.dialog diff --git a/dialog/en-us/forecast.local.affirmative.condition.dialog b/dialog/en-us/daily.condition.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.affirmative.condition.dialog rename to dialog/en-us/daily.condition.expected.local.dialog diff --git a/dialog/en-us/forecast.affirmative.condition.dialog b/dialog/en-us/daily.condition.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.affirmative.condition.dialog rename to dialog/en-us/daily.condition.expected.location.dialog diff --git a/dialog/en-us/forecast.local.foggy.alternative.dialog b/dialog/en-us/daily.fog.alternative.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.foggy.alternative.dialog rename to dialog/en-us/daily.fog.alternative.local.dialog diff --git a/dialog/en-us/forecast.foggy.alternative.dialog b/dialog/en-us/daily.fog.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.foggy.alternative.dialog rename to dialog/en-us/daily.fog.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.fog.predicted.dialog b/dialog/en-us/daily.fog.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.fog.predicted.dialog rename to dialog/en-us/daily.fog.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.fog.predicted.dialog b/dialog/en-us/daily.fog.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.fog.predicted.dialog rename to dialog/en-us/daily.fog.not.expected.location.dialog diff --git a/dialog/en-us/daily.humidity.local.dialog b/dialog/en-us/daily.humidity.local.dialog new file mode 100644 index 00000000..0794fd6a --- /dev/null +++ b/dialog/en-us/daily.humidity.local.dialog @@ -0,0 +1,2 @@ +The humidity {day} will be {percent} +{day} the forecast calls for a humidity of {percent} diff --git a/dialog/en-us/daily.humidity.location.dialog b/dialog/en-us/daily.humidity.location.dialog new file mode 100644 index 00000000..0c7744f9 --- /dev/null +++ b/dialog/en-us/daily.humidity.location.dialog @@ -0,0 +1,2 @@ +The humidity in {location} {day} will be {percent} +{day} the forecast in {location} calls for a humidity of {percent} diff --git a/dialog/en-us/daily.precipitation.next.local.dialog b/dialog/en-us/daily.precipitation.next.local.dialog new file mode 100644 index 00000000..3d6674a0 --- /dev/null +++ b/dialog/en-us/daily.precipitation.next.local.dialog @@ -0,0 +1,2 @@ +There is a {percent} chance of {precipitation} {day} +The forecast calls for a {percent} chance of {precipitation} {day} diff --git a/dialog/en-us/daily.precipitation.next.location.dialog b/dialog/en-us/daily.precipitation.next.location.dialog new file mode 100644 index 00000000..7617928c --- /dev/null +++ b/dialog/en-us/daily.precipitation.next.location.dialog @@ -0,0 +1,2 @@ +In {location} there is a {percent} chance of {precipitation} {day} +The forecast calls for a {percent} chance of {precipitation} in {location} {day} diff --git a/dialog/en-us/daily.precipitation.next.none.local.dialog b/dialog/en-us/daily.precipitation.next.none.local.dialog new file mode 100644 index 00000000..07885fde --- /dev/null +++ b/dialog/en-us/daily.precipitation.next.none.local.dialog @@ -0,0 +1,2 @@ +No precipitation is in the forecast for the next 7 days +None is forecasted in the next 7 days. diff --git a/dialog/en-us/daily.precipitation.next.none.location.dialog b/dialog/en-us/daily.precipitation.next.none.location.dialog new file mode 100644 index 00000000..baba3d6c --- /dev/null +++ b/dialog/en-us/daily.precipitation.next.none.location.dialog @@ -0,0 +1,2 @@ +No precipitation is in the forecast for the next seven days in {location} +In {location} none is forecasted diff --git a/dialog/en-us/forecast.local.raining.alternative.dialog b/dialog/en-us/daily.rain.alternative.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.raining.alternative.dialog rename to dialog/en-us/daily.rain.alternative.local.dialog diff --git a/dialog/en-us/forecast.raining.alternative.dialog b/dialog/en-us/daily.rain.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.raining.alternative.dialog rename to dialog/en-us/daily.rain.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.rain.predicted.dialog b/dialog/en-us/daily.rain.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.rain.predicted.dialog rename to dialog/en-us/daily.rain.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.rain.predicted.dialog b/dialog/en-us/daily.rain.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.rain.predicted.dialog rename to dialog/en-us/daily.rain.not.expected.location.dialog diff --git a/dialog/en-us/forecast.snowing.alternative.dialog b/dialog/en-us/daily.snow.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.snowing.alternative.dialog rename to dialog/en-us/daily.snow.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.snow.predicted.dialog b/dialog/en-us/daily.snow.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.snow.predicted.dialog rename to dialog/en-us/daily.snow.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.snow.predicted.dialog b/dialog/en-us/daily.snow.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.snow.predicted.dialog rename to dialog/en-us/daily.snow.not.expected.location.dialog diff --git a/dialog/en-us/daily.sunrise.local.dialog b/dialog/en-us/daily.sunrise.local.dialog new file mode 100644 index 00000000..b25f109f --- /dev/null +++ b/dialog/en-us/daily.sunrise.local.dialog @@ -0,0 +1,3 @@ +the sun will rise at {time} {day} +sunrise will be at {time} {day} +{day} the sun will rise at {time} diff --git a/dialog/en-us/daily.sunrise.location.dialog b/dialog/en-us/daily.sunrise.location.dialog new file mode 100644 index 00000000..87ca92af --- /dev/null +++ b/dialog/en-us/daily.sunrise.location.dialog @@ -0,0 +1,3 @@ +the sun will rise at {time} {day} in {location} +in {location} sunrise will be at {time} {day} +{day} the sun will rise at {time} in {location} diff --git a/dialog/en-us/daily.sunset.local.dialog b/dialog/en-us/daily.sunset.local.dialog new file mode 100644 index 00000000..8007f447 --- /dev/null +++ b/dialog/en-us/daily.sunset.local.dialog @@ -0,0 +1,4 @@ +the sun will set at {time} {day} +the sun will go down at {time} {day} +sunset will be at {time} {day} +{day} the sun will set at {time} diff --git a/dialog/en-us/daily.sunset.location.dialog b/dialog/en-us/daily.sunset.location.dialog new file mode 100644 index 00000000..0576df04 --- /dev/null +++ b/dialog/en-us/daily.sunset.location.dialog @@ -0,0 +1,3 @@ +the sun will set at {time} {day} in {location} +in {location} sunset will be at {time} {day} +{day} the sun will set at {time} in {location} diff --git a/dialog/en-us/daily.temperature.high.local.dialog b/dialog/en-us/daily.temperature.high.local.dialog new file mode 100644 index 00000000..6651c3c1 --- /dev/null +++ b/dialog/en-us/daily.temperature.high.local.dialog @@ -0,0 +1,2 @@ +{day} it will be as high as {temperature} +{day} the temperature will be as high as {temperature} degrees. diff --git a/dialog/en-us/daily.temperature.high.location.dialog b/dialog/en-us/daily.temperature.high.location.dialog new file mode 100644 index 00000000..531dcb51 --- /dev/null +++ b/dialog/en-us/daily.temperature.high.location.dialog @@ -0,0 +1,2 @@ +It will be as high as {temperature} degrees in {location} {day} +{location} will have a high of {temperature} degrees {day} diff --git a/dialog/en-us/daily.temperature.local.dialog b/dialog/en-us/daily.temperature.local.dialog new file mode 100644 index 00000000..7ee9a2fd --- /dev/null +++ b/dialog/en-us/daily.temperature.local.dialog @@ -0,0 +1,2 @@ +{day} it will be {temperature} +{day} the temperature will be {temperature} degrees. diff --git a/dialog/en-us/daily.temperature.location.dialog b/dialog/en-us/daily.temperature.location.dialog new file mode 100644 index 00000000..3c8ab784 --- /dev/null +++ b/dialog/en-us/daily.temperature.location.dialog @@ -0,0 +1,2 @@ +{day} it will be {temperature} degrees in {location} +{day}, {location} will have a temperature of {temperature} degrees diff --git a/dialog/en-us/daily.temperature.low.local.dialog b/dialog/en-us/daily.temperature.low.local.dialog new file mode 100644 index 00000000..55db29c1 --- /dev/null +++ b/dialog/en-us/daily.temperature.low.local.dialog @@ -0,0 +1,2 @@ +{day} it will be as low as {temperature} +{day} the temperature will be as low as {temperature} degrees. diff --git a/dialog/en-us/daily.temperature.low.location.dialog b/dialog/en-us/daily.temperature.low.location.dialog new file mode 100644 index 00000000..40a40865 --- /dev/null +++ b/dialog/en-us/daily.temperature.low.location.dialog @@ -0,0 +1,2 @@ +{day} it will be as low as {temperature} degrees in {location} +{day}, {location} will be as low as {temperature} degrees diff --git a/dialog/en-us/forecast.local.storm.alternative.dialog b/dialog/en-us/daily.thunderstorm.alternative.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.storm.alternative.dialog rename to dialog/en-us/daily.thunderstorm.alternative.local.dialog diff --git a/dialog/en-us/forecast.storm.alternative.dialog b/dialog/en-us/daily.thunderstorm.alternative.location.dialog similarity index 100% rename from dialog/en-us/forecast.storm.alternative.dialog rename to dialog/en-us/daily.thunderstorm.alternative.location.dialog diff --git a/dialog/en-us/forecast.local.no.storm.predicted.dialog b/dialog/en-us/daily.thunderstorm.not.expected.local.dialog similarity index 100% rename from dialog/en-us/forecast.local.no.storm.predicted.dialog rename to dialog/en-us/daily.thunderstorm.not.expected.local.dialog diff --git a/dialog/en-us/forecast.no.storm.predicted.dialog b/dialog/en-us/daily.thunderstorm.not.expected.location.dialog similarity index 100% rename from dialog/en-us/forecast.no.storm.predicted.dialog rename to dialog/en-us/daily.thunderstorm.not.expected.location.dialog diff --git a/dialog/en-us/daily.weather.local.dialog b/dialog/en-us/daily.weather.local.dialog new file mode 100644 index 00000000..a69707e6 --- /dev/null +++ b/dialog/en-us/daily.weather.local.dialog @@ -0,0 +1,5 @@ +{day} expect {condition}, with a high of {high_temperature} and a low of {low_temperature} +Expect {condition}, with a high of {high_temperature} and a low of {low_temperature} {day} +{day} the high will be {high_temperature} and the low {low_temperature}, with {condition} +{day} it will be {condition} with a high of {high_temperature} and a low of {low_temperature} +The forecast {day} is {condition} with a high of {high_temperature} and a low of {low_temperature} diff --git a/dialog/en-us/daily.weather.location.dialog b/dialog/en-us/daily.weather.location.dialog new file mode 100644 index 00000000..5a44dd21 --- /dev/null +++ b/dialog/en-us/daily.weather.location.dialog @@ -0,0 +1,3 @@ +{day} it will be {condition}, with a high of {high_temperature} and a low of {low_temperature} in {location} +{day}, {location} will have a high of {high_temperature} and a low of {low_temperature}, with {condition} +The forecast {day} is {high_temperature} for a high and {low_temperature} for a low in {location} diff --git a/dialog/en-us/daily.wind.light.local.dialog b/dialog/en-us/daily.wind.light.local.dialog new file mode 100644 index 00000000..c686a43e --- /dev/null +++ b/dialog/en-us/daily.wind.light.local.dialog @@ -0,0 +1,2 @@ +There will be a light wind coming from the {direction} {day} at {speed} {speed_unit} +It will not be very windy {day} diff --git a/dialog/en-us/daily.wind.light.location.dialog b/dialog/en-us/daily.wind.light.location.dialog new file mode 100644 index 00000000..43223313 --- /dev/null +++ b/dialog/en-us/daily.wind.light.location.dialog @@ -0,0 +1,2 @@ +There will be a light wind coming from the {direction} in {location} {day} at {speed} {speed_unit} +It will not be very windy in {location} {day} diff --git a/dialog/en-us/daily.wind.moderate.local.dialog b/dialog/en-us/daily.wind.moderate.local.dialog new file mode 100644 index 00000000..8729037b --- /dev/null +++ b/dialog/en-us/daily.wind.moderate.local.dialog @@ -0,0 +1,3 @@ +The wind will be moderate, about {speed} {speed_unit} from the {direction} {day} +The forecast calls for a moderate wind from the {direction} at {speed} {speed_unit} {day} +You can expect a moderate wind of about {speed} {speed_unit} {day} diff --git a/dialog/en-us/daily.wind.moderate.location.dialog b/dialog/en-us/daily.wind.moderate.location.dialog new file mode 100644 index 00000000..a395d5d6 --- /dev/null +++ b/dialog/en-us/daily.wind.moderate.location.dialog @@ -0,0 +1,3 @@ +The wind will be moderate in {location} {day}, about {speed} {speed_unit} from the {direction} +The forecast {day} predicts {location} will have moderate wind from the {direction} of {speed} {speed_unit} +You can expect a wind of about {speed} {speed_unit} in {location} {day} diff --git a/dialog/en-us/daily.wind.strong.local.dialog b/dialog/en-us/daily.wind.strong.local.dialog new file mode 100644 index 00000000..66278185 --- /dev/null +++ b/dialog/en-us/daily.wind.strong.local.dialog @@ -0,0 +1,2 @@ +There will be strong wind from the {direction} of {speed} {speed_unit} {day} +The wind will be as strong as {speed} {speed_unit} {day} diff --git a/dialog/en-us/daily.wind.strong.location.dialog b/dialog/en-us/daily.wind.strong.location.dialog new file mode 100644 index 00000000..4e7ea4b2 --- /dev/null +++ b/dialog/en-us/daily.wind.strong.location.dialog @@ -0,0 +1,2 @@ +There will be a strong wind from the {direction} of {speed} {speed_unit} in {location} {day} +The wind will be as strong as {speed} {speed_unit} in {location} {day} diff --git a/dialog/en-us/E.dialog b/dialog/en-us/east.dialog similarity index 100% rename from dialog/en-us/E.dialog rename to dialog/en-us/east.dialog diff --git a/dialog/en-us/forecast.hard.wind.dialog b/dialog/en-us/forecast.hard.wind.dialog deleted file mode 100644 index f0cca68f..00000000 --- a/dialog/en-us/forecast.hard.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be {wind} {wind_unit} in {location} {day} -The wind will be as strong as {wind} {wind_unit} in {location} {day} diff --git a/dialog/en-us/forecast.high.temperature.dialog b/dialog/en-us/forecast.high.temperature.dialog deleted file mode 100644 index c90de69b..00000000 --- a/dialog/en-us/forecast.high.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be as high as {temp_max} degrees in {location} {day} -{location} will have a high of {temp_max} degrees {day} diff --git a/dialog/en-us/forecast.hot.dialog b/dialog/en-us/forecast.hot.dialog deleted file mode 100644 index b1c64f99..00000000 --- a/dialog/en-us/forecast.hot.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it'll be {temp} degrees in {location}, quite comfy for an A.I. -{day} the temperature will be {temp} in {location}, well within my specifications diff --git a/dialog/en-us/forecast.light.wind.dialog b/dialog/en-us/forecast.light.wind.dialog deleted file mode 100644 index 0e010c1b..00000000 --- a/dialog/en-us/forecast.light.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will not be very windy in {location} {day} -In {location} {day} there's going to be no wind to speak of diff --git a/dialog/en-us/forecast.local.hard.wind.dialog b/dialog/en-us/forecast.local.hard.wind.dialog deleted file mode 100644 index eb7794f3..00000000 --- a/dialog/en-us/forecast.local.hard.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will be {wind} {wind_unit} {day} -The wind will be as strong as {wind} {wind_unit} {day} diff --git a/dialog/en-us/forecast.local.high.temperature.dialog b/dialog/en-us/forecast.local.high.temperature.dialog deleted file mode 100644 index 156cddfc..00000000 --- a/dialog/en-us/forecast.local.high.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it will be as high as {temp_max} -{day} the temperature will be as high as {temp_max} degrees. diff --git a/dialog/en-us/forecast.local.hot.dialog b/dialog/en-us/forecast.local.hot.dialog deleted file mode 100644 index 6ad4c1a2..00000000 --- a/dialog/en-us/forecast.local.hot.dialog +++ /dev/null @@ -1 +0,0 @@ -{day} it'll be {temp}, quite comfy for an AI diff --git a/dialog/en-us/forecast.local.light.wind.dialog b/dialog/en-us/forecast.local.light.wind.dialog deleted file mode 100644 index 8d7efbf0..00000000 --- a/dialog/en-us/forecast.local.light.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -It will not be very windy {day} -There's going to be no wind to speak of {day} diff --git a/dialog/en-us/forecast.local.low.temperature.dialog b/dialog/en-us/forecast.local.low.temperature.dialog deleted file mode 100644 index af82ed3f..00000000 --- a/dialog/en-us/forecast.local.low.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it will be as low as {temp_min} -{day} the temperature will be as low as {temp_min} degrees. diff --git a/dialog/en-us/forecast.local.medium.wind.dialog b/dialog/en-us/forecast.local.medium.wind.dialog deleted file mode 100644 index 927f3f4b..00000000 --- a/dialog/en-us/forecast.local.medium.wind.dialog +++ /dev/null @@ -1,3 +0,0 @@ -The wind will not be too bad, about {wind} {wind_unit} {day} -The forecast says {wind} {wind_unit} {day}, not too bad -You can expect a wind of about {wind} {wind_unit} {day} diff --git a/dialog/en-us/forecast.local.temperature.dialog b/dialog/en-us/forecast.local.temperature.dialog deleted file mode 100644 index 9b0f64bd..00000000 --- a/dialog/en-us/forecast.local.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it will be {temp} -{day} the temperature will be {temp} degrees. diff --git a/dialog/en-us/forecast.local.weather.dialog b/dialog/en-us/forecast.local.weather.dialog deleted file mode 100644 index 3f43accd..00000000 --- a/dialog/en-us/forecast.local.weather.dialog +++ /dev/null @@ -1,5 +0,0 @@ -{day} expect {condition}, with a high of {temp_max} and a low of {temp_min} -Expect {condition}, with a high of {temp_max} and a low of {temp_min} {day} -{day} the high will be {temp_max} and the low {temp_min}, with {condition} -{day} it will be {condition} with a high of {temp_max} and a low of {temp_min} -The forecast {day} is a high of {temp_max} and a low of {temp_min} diff --git a/dialog/en-us/forecast.low.temperature.dialog b/dialog/en-us/forecast.low.temperature.dialog deleted file mode 100644 index 4b5bd097..00000000 --- a/dialog/en-us/forecast.low.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it will be as low as {temp_min} degrees in {location} -{day}, {location} will be as low as {temp} degrees diff --git a/dialog/en-us/forecast.medium.wind.dialog b/dialog/en-us/forecast.medium.wind.dialog deleted file mode 100644 index 5c20f443..00000000 --- a/dialog/en-us/forecast.medium.wind.dialog +++ /dev/null @@ -1,3 +0,0 @@ -The wind will not be too bad in {location}, about {wind} {wind_unit} {day} -The forecast {day} says {location} will have {wind} {wind_unit}, not too bad -You can expect a wind of about {wind} {wind_unit} in {location} {day} diff --git a/dialog/en-us/forecast.temperature.dialog b/dialog/en-us/forecast.temperature.dialog deleted file mode 100644 index c0bf8ffd..00000000 --- a/dialog/en-us/forecast.temperature.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{day} it will be {temp} degrees in {location} -{day}, {location} will have a temperature of {temp} degrees diff --git a/dialog/en-us/forecast.weather.dialog b/dialog/en-us/forecast.weather.dialog deleted file mode 100644 index 061322ea..00000000 --- a/dialog/en-us/forecast.weather.dialog +++ /dev/null @@ -1,3 +0,0 @@ -{day} it will be {condition}, with a high of {temp_max} and a low of {temp_min} in {location} -{day}, {location} will have a high of {temp_max} and a low of {temp_min}, with {condition} -The forecast {day} is {temp_max} for a high and {temp_min} for a low in {location} diff --git a/dialog/en-us/hard.wind.dialog b/dialog/en-us/hard.wind.dialog deleted file mode 100644 index fdfae231..00000000 --- a/dialog/en-us/hard.wind.dialog +++ /dev/null @@ -1,3 +0,0 @@ -It's {wind} {wind_unit} in {location}, a good day to stay inside -The wind is very strong in {location} today, {wind} {wind_unit} -{location} will have quite strong winds today, {wind} {wind_unit} diff --git a/dialog/en-us/hour.local.weather.dialog b/dialog/en-us/hour.local.weather.dialog deleted file mode 100644 index 64df1274..00000000 --- a/dialog/en-us/hour.local.weather.dialog +++ /dev/null @@ -1,4 +0,0 @@ -It will be {condition}, with temperatures near {temp} -Later it will be {condition} and around {temp} degrees -Later it will be {condition} and {temp} degrees -Around {temp} degrees with {condition} diff --git a/dialog/en-us/hour.weather.dialog b/dialog/en-us/hour.weather.dialog deleted file mode 100644 index f796ff3a..00000000 --- a/dialog/en-us/hour.weather.dialog +++ /dev/null @@ -1,4 +0,0 @@ -{location} weather in the next few hours will be {condition} and {temp} degrees -Later it will be {condition} in {location}, with temperatures around {temp} -{location} will be around {temp} with {condition} -{location} will be about {temp} degrees with {condition} diff --git a/dialog/en-us/at.time.local.cond.alternative.dialog b/dialog/en-us/hourly.condition.alternative.local.dialog similarity index 100% rename from dialog/en-us/at.time.local.cond.alternative.dialog rename to dialog/en-us/hourly.condition.alternative.local.dialog diff --git a/dialog/en-us/at.time.cond.alternative.dialog b/dialog/en-us/hourly.condition.alternative.location.dialog similarity index 100% rename from dialog/en-us/at.time.cond.alternative.dialog rename to dialog/en-us/hourly.condition.alternative.location.dialog diff --git a/dialog/en-us/at.time.local.affirmative.condition.dialog b/dialog/en-us/hourly.condition.expected.local.dialog similarity index 100% rename from dialog/en-us/at.time.local.affirmative.condition.dialog rename to dialog/en-us/hourly.condition.expected.local.dialog diff --git a/dialog/en-us/at.time.affirmative.condition.dialog b/dialog/en-us/hourly.condition.expected.location.dialog similarity index 100% rename from dialog/en-us/at.time.affirmative.condition.dialog rename to dialog/en-us/hourly.condition.expected.location.dialog diff --git a/dialog/en-us/hourly.precipitation.next.local.dialog b/dialog/en-us/hourly.precipitation.next.local.dialog new file mode 100644 index 00000000..917c4c14 --- /dev/null +++ b/dialog/en-us/hourly.precipitation.next.local.dialog @@ -0,0 +1,2 @@ +There is a {percent} chance of {precipitation} at {time} +The forecast calls for a {percent} chance of {precipitation} at {time} diff --git a/dialog/en-us/hourly.precipitation.next.location.dialog b/dialog/en-us/hourly.precipitation.next.location.dialog new file mode 100644 index 00000000..c6f35430 --- /dev/null +++ b/dialog/en-us/hourly.precipitation.next.location.dialog @@ -0,0 +1,2 @@ +In {location} there is a {percent} chance of {precipitation} at {time} +The forecast calls for a {percent} chance of {precipitation} in {location} at {time} diff --git a/dialog/en-us/hourly.temperature.local.dialog b/dialog/en-us/hourly.temperature.local.dialog new file mode 100644 index 00000000..ddfbaa37 --- /dev/null +++ b/dialog/en-us/hourly.temperature.local.dialog @@ -0,0 +1,2 @@ +It will be about {temperature} degrees in the {time} +In the {time}, it will be {temperature} degrees diff --git a/dialog/en-us/hourly.temperature.location.dialog b/dialog/en-us/hourly.temperature.location.dialog new file mode 100644 index 00000000..046bc42b --- /dev/null +++ b/dialog/en-us/hourly.temperature.location.dialog @@ -0,0 +1,2 @@ +In {location} it will be about {temperature} degrees in the {time} +In the {time}, it will be {temperature} degrees in {location} diff --git a/dialog/en-us/hourly.weather.local.dialog b/dialog/en-us/hourly.weather.local.dialog new file mode 100644 index 00000000..77bbe4fc --- /dev/null +++ b/dialog/en-us/hourly.weather.local.dialog @@ -0,0 +1,4 @@ +It will be {condition}, with temperatures near {temperature} +Later it will be {condition} and around {temperature} degrees +Later it will be {condition} and {temperature} degrees +Around {temperature} degrees with {condition} diff --git a/dialog/en-us/hourly.weather.location.dialog b/dialog/en-us/hourly.weather.location.dialog new file mode 100644 index 00000000..e0e62e39 --- /dev/null +++ b/dialog/en-us/hourly.weather.location.dialog @@ -0,0 +1,4 @@ +{location} weather in the next few hours will be {condition} and {temperature} degrees +Later it will be {condition} in {location}, with temperatures around {temperature} +{location} will be around {temperature} with {condition} +{location} will be about {temperature} degrees with {condition} diff --git a/dialog/en-us/light.wind.dialog b/dialog/en-us/light.wind.dialog deleted file mode 100644 index f589cdd2..00000000 --- a/dialog/en-us/light.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -There's no wind to speak about today in {location} -It's not very windy at all in {location} today diff --git a/dialog/en-us/local.hard.wind.dialog b/dialog/en-us/local.hard.wind.dialog deleted file mode 100644 index d50d1bc0..00000000 --- a/dialog/en-us/local.hard.wind.dialog +++ /dev/null @@ -1,3 +0,0 @@ -It's {wind} {wind_unit}, might be good to stay inside today -The wind is very strong today, {wind} {wind_unit} -It's very windy today, {wind} {wind_unit} diff --git a/dialog/en-us/local.light.wind.dialog b/dialog/en-us/local.light.wind.dialog deleted file mode 100644 index 8bfa0ad7..00000000 --- a/dialog/en-us/local.light.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -Today there's no wind to speak about -It's not very windy at all today diff --git a/dialog/en-us/local.medium.wind.dialog b/dialog/en-us/local.medium.wind.dialog deleted file mode 100644 index 6ef8a31b..00000000 --- a/dialog/en-us/local.medium.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -Currently {wind} {wind_unit}, so a bit windy -It's a bit windy today, currently {wind} {wind_unit} diff --git a/dialog/en-us/location.not.found.dialog b/dialog/en-us/location.not.found.dialog index e08d065a..e95e7679 100644 --- a/dialog/en-us/location.not.found.dialog +++ b/dialog/en-us/location.not.found.dialog @@ -1,3 +1,2 @@ -I not sure where that is -I don't know that location -I don't know that place +I can't find a city named {location}. Please try again +The city {location} is not in my memory banks. Please try again diff --git a/dialog/en-us/medium.wind.dialog b/dialog/en-us/medium.wind.dialog deleted file mode 100644 index ae05ccc9..00000000 --- a/dialog/en-us/medium.wind.dialog +++ /dev/null @@ -1,2 +0,0 @@ -Currently {wind} {wind_unit} in {location}, so a bit windy today -It's a bit windy in {location} today, currently {wind} {wind_unit} diff --git a/dialog/en-us/min.max.dialog b/dialog/en-us/min.max.dialog deleted file mode 100644 index 1e6e8093..00000000 --- a/dialog/en-us/min.max.dialog +++ /dev/null @@ -1 +0,0 @@ -Today's forecast is for a high of {temp_max} and a low of {temp_min}. diff --git a/dialog/en-us/no precipitation expected.dialog b/dialog/en-us/no precipitation expected.dialog deleted file mode 100644 index 6ccf889a..00000000 --- a/dialog/en-us/no precipitation expected.dialog +++ /dev/null @@ -1,2 +0,0 @@ -No precipitation is in the forecast -None is forecasted diff --git a/dialog/en-us/N.dialog b/dialog/en-us/north.dialog similarity index 100% rename from dialog/en-us/N.dialog rename to dialog/en-us/north.dialog diff --git a/dialog/en-us/NE.dialog b/dialog/en-us/northeast.dialog similarity index 100% rename from dialog/en-us/NE.dialog rename to dialog/en-us/northeast.dialog diff --git a/dialog/en-us/NW.dialog b/dialog/en-us/northwest.dialog similarity index 100% rename from dialog/en-us/NW.dialog rename to dialog/en-us/northwest.dialog diff --git a/dialog/en-us/on.date.dialog b/dialog/en-us/on.date.dialog deleted file mode 100644 index 59cef6f7..00000000 --- a/dialog/en-us/on.date.dialog +++ /dev/null @@ -1 +0,0 @@ -on diff --git a/dialog/en-us/on.dialog b/dialog/en-us/on.dialog deleted file mode 100644 index e8fd9030..00000000 --- a/dialog/en-us/on.dialog +++ /dev/null @@ -1 +0,0 @@ -on \ No newline at end of file diff --git a/dialog/en-us/percentage.number.dialog b/dialog/en-us/percentage.number.dialog index 1cbe1219..1feed403 100644 --- a/dialog/en-us/percentage.number.dialog +++ b/dialog/en-us/percentage.number.dialog @@ -1 +1 @@ -{num} percent +{number} percent diff --git a/dialog/en-us/precipitation expected.dialog b/dialog/en-us/precipitation expected.dialog deleted file mode 100644 index cdd515a3..00000000 --- a/dialog/en-us/precipitation expected.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{modifier} {precip} is expected {day} -The forecast calls for {modifier} {precip} {day} diff --git a/dialog/en-us/report.condition.at.location.dialog b/dialog/en-us/report.condition.at.location.dialog deleted file mode 100644 index 2ee6d8dd..00000000 --- a/dialog/en-us/report.condition.at.location.dialog +++ /dev/null @@ -1 +0,0 @@ -Currently, {condition} in {location} is {value} diff --git a/dialog/en-us/report.condition.dialog b/dialog/en-us/report.condition.dialog deleted file mode 100644 index 50caa9e6..00000000 --- a/dialog/en-us/report.condition.dialog +++ /dev/null @@ -1 +0,0 @@ -Currently, {condition} is {value} diff --git a/dialog/en-us/report.condition.future.at.location.dialog b/dialog/en-us/report.condition.future.at.location.dialog deleted file mode 100644 index b77cc567..00000000 --- a/dialog/en-us/report.condition.future.at.location.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{condition} in {location} {day} will be {value} -{day} the forecast in {location} calls for {condition} of {value} diff --git a/dialog/en-us/report.condition.future.dialog b/dialog/en-us/report.condition.future.dialog deleted file mode 100644 index dd7f8a54..00000000 --- a/dialog/en-us/report.condition.future.dialog +++ /dev/null @@ -1,2 +0,0 @@ -{condition} {day} will be {value} -{day} the forecast calls for {condition} of {value} diff --git a/dialog/en-us/report.wind.dialog b/dialog/en-us/report.wind.dialog deleted file mode 100644 index 02fe30e5..00000000 --- a/dialog/en-us/report.wind.dialog +++ /dev/null @@ -1 +0,0 @@ -{condition} is {value} diff --git a/dialog/en-us/seven.days.available.dialog b/dialog/en-us/seven.days.available.dialog new file mode 100644 index 00000000..6c9af6e3 --- /dev/null +++ b/dialog/en-us/seven.days.available.dialog @@ -0,0 +1,2 @@ +I have seven days of forecasted weather available. +I can tell you the forecast for the next seven days. diff --git a/dialog/en-us/S.dialog b/dialog/en-us/south.dialog similarity index 100% rename from dialog/en-us/S.dialog rename to dialog/en-us/south.dialog diff --git a/dialog/en-us/SE.dialog b/dialog/en-us/southeast.dialog similarity index 100% rename from dialog/en-us/SE.dialog rename to dialog/en-us/southeast.dialog diff --git a/dialog/en-us/SW.dialog b/dialog/en-us/southwest.dialog similarity index 100% rename from dialog/en-us/SW.dialog rename to dialog/en-us/southwest.dialog diff --git a/dialog/en-us/tonight.local.weather.dialog b/dialog/en-us/tonight.local.weather.dialog deleted file mode 100644 index 67efb070..00000000 --- a/dialog/en-us/tonight.local.weather.dialog +++ /dev/null @@ -1,4 +0,0 @@ -Tonight it will be {condition}, with temperatures near {temp} -Tonight it will be {condition} and around {temp} degrees -Tonight it will be {condition} and {temp} degrees -Around {temp} degrees with {condition} tonight diff --git a/dialog/en-us/weekly.condition.on.day.dialog b/dialog/en-us/weekly.condition.on.day.dialog deleted file mode 100644 index 1634763e..00000000 --- a/dialog/en-us/weekly.condition.on.day.dialog +++ /dev/null @@ -1 +0,0 @@ -{day} will be {condition}, diff --git a/dialog/en-us/weekly.conditions.mostly.one.dialog b/dialog/en-us/weekly.conditions.mostly.one.dialog deleted file mode 100644 index b3d40af2..00000000 --- a/dialog/en-us/weekly.conditions.mostly.one.dialog +++ /dev/null @@ -1 +0,0 @@ -it will be mostly {condition}. diff --git a/dialog/en-us/weekly.conditions.seq.extra.dialog b/dialog/en-us/weekly.conditions.seq.extra.dialog deleted file mode 100644 index 498d2ed4..00000000 --- a/dialog/en-us/weekly.conditions.seq.extra.dialog +++ /dev/null @@ -1,2 +0,0 @@ -it looks like there will also be -it will also be diff --git a/dialog/en-us/weekly.conditions.seq.period.dialog b/dialog/en-us/weekly.conditions.seq.period.dialog deleted file mode 100644 index ccf787ed..00000000 --- a/dialog/en-us/weekly.conditions.seq.period.dialog +++ /dev/null @@ -1 +0,0 @@ -from {from} to {to} diff --git a/dialog/en-us/weekly.conditions.seq.start.dialog b/dialog/en-us/weekly.conditions.seq.start.dialog deleted file mode 100644 index cd5183ae..00000000 --- a/dialog/en-us/weekly.conditions.seq.start.dialog +++ /dev/null @@ -1 +0,0 @@ -it will be {condition} diff --git a/dialog/en-us/weekly.conditions.some.days.dialog b/dialog/en-us/weekly.conditions.some.days.dialog deleted file mode 100644 index 244dc951..00000000 --- a/dialog/en-us/weekly.conditions.some.days.dialog +++ /dev/null @@ -1 +0,0 @@ -it will be {condition} some days. diff --git a/dialog/en-us/weekly.temp.range.dialog b/dialog/en-us/weekly.temp.range.dialog deleted file mode 100644 index b67b3143..00000000 --- a/dialog/en-us/weekly.temp.range.dialog +++ /dev/null @@ -1 +0,0 @@ -lows will be between {low_min} and {low_max}, with highs between {high_min} and {high_max} diff --git a/dialog/en-us/W.dialog b/dialog/en-us/west.dialog similarity index 100% rename from dialog/en-us/W.dialog rename to dialog/en-us/west.dialog diff --git a/dialog/en-us/wind.speed.dialog b/dialog/en-us/wind.speed.dialog deleted file mode 100644 index b98efb6f..00000000 --- a/dialog/en-us/wind.speed.dialog +++ /dev/null @@ -1 +0,0 @@ -{speed} {unit} diff --git a/dialog/en-us/wind.speed.dir.dialog b/dialog/en-us/wind.speed.dir.dialog deleted file mode 100644 index 9baf235d..00000000 --- a/dialog/en-us/wind.speed.dir.dialog +++ /dev/null @@ -1,2 +0,0 @@ -at {speed} {unit} from the {dir} -from the {dir} at {speed} {unit} diff --git a/dialog/en-us/wind.strength.hard.dialog b/dialog/en-us/wind.strength.hard.dialog deleted file mode 100644 index d22605f7..00000000 --- a/dialog/en-us/wind.strength.hard.dialog +++ /dev/null @@ -1 +0,0 @@ -That's quite strong diff --git a/dialog/en-us/wind.strength.light.dialog b/dialog/en-us/wind.strength.light.dialog deleted file mode 100644 index 54d137e5..00000000 --- a/dialog/en-us/wind.strength.light.dialog +++ /dev/null @@ -1,3 +0,0 @@ -Not very windy -Not much wind -Quite calm diff --git a/dialog/en-us/wind.strength.medium.dialog b/dialog/en-us/wind.strength.medium.dialog deleted file mode 100644 index 718d8843..00000000 --- a/dialog/en-us/wind.strength.medium.dialog +++ /dev/null @@ -1,2 +0,0 @@ -That's getting breezy. -You might want to take a jacket diff --git a/dialog/en-us/winds.dialog b/dialog/en-us/winds.dialog deleted file mode 100644 index 7042ea3d..00000000 --- a/dialog/en-us/winds.dialog +++ /dev/null @@ -1 +0,0 @@ -wind speed diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 00000000..9d93bdf3 --- /dev/null +++ b/source/__init__.py @@ -0,0 +1,14 @@ +from .api import APIErrors, LocationNotFoundError, OWMApi, OpenWeatherMapApi +from .config import ( + CELSIUS, + FAHRENHEIT, + METERS_PER_SECOND, + MILES_PER_HOUR, + WeatherConfig +) +from .dialog import WeatherDialog +from .intent import WeatherIntent +from .weather import ( + is_current_weather, is_daily_forecast, is_hourly_forecast, WeatherReport +) +from .util import get_sequence_of_days diff --git a/source/api.py b/source/api.py new file mode 100644 index 00000000..46f471e3 --- /dev/null +++ b/source/api.py @@ -0,0 +1,28 @@ +from requests import HTTPError + +from mycroft.api import Api +from .weather import WeatherReport +from .util import LocationNotFoundError + +MINUTES = 60 # Minutes to seconds multiplier + +APIErrors = (LocationNotFoundError, HTTPError) + + +class OpenWeatherMapApi(Api): + def __init__(self): + super().__init__(path="owm") + + def get_weather_for_coordinates(self, measurement_system, latitude, longitude): + """Use the One Call API to get local weather conditions.""" + query_parameters = dict( + exclude="minutely", + lat=latitude, + lon=longitude, + units=measurement_system + ) + api_request = dict(path="/onecall", query=query_parameters) + response = self.request(api_request) + local_weather = WeatherReport(response) + + return local_weather diff --git a/source/config.py b/source/config.py new file mode 100644 index 00000000..937ebd83 --- /dev/null +++ b/source/config.py @@ -0,0 +1,55 @@ +FAHRENHEIT = "fahrenheit" +CELSIUS = "celsius" +METRIC = "metric" +METERS_PER_SECOND = "meters per second" +MILES_PER_HOUR = "miles per hour" + + +class WeatherConfig: + + def __init__(self, core_config: dict, settings: dict): + self.core_config = core_config + self.settings = settings + config_location = self.core_config["location"] + self.latitude = config_location["coordinate"]["latitude"] + self.longitude = config_location["coordinate"]["longitude"] + city = config_location["city"] + state = city["state"] + country = state["country"] + self.city = city["name"] + self.state = state["name"] + self.country = country["name"] + self.speed_unit = self._determine_speed_unit() + self.temperature_unit = self._determine_temperature_unit() + + def _determine_speed_unit(self) -> str: + """Use the core configuration to determine the unit of speed. + + Returns: (str) 'meters_sec' or 'mph' + """ + system_unit = self.core_config['system_unit'] + if system_unit == METRIC: + speed_unit = METERS_PER_SECOND + else: + speed_unit = MILES_PER_HOUR + + return speed_unit + + def _determine_temperature_unit(self) -> str: + """Use the core configuration to determine the unit of temperature. + + Returns: "celsius" or "fahrenheit" + """ + unit_from_settings = self.settings.get("units") + measurement_system = self.core_config['system_unit'] + if measurement_system == METRIC: + temperature_unit = CELSIUS + else: + temperature_unit = FAHRENHEIT + if unit_from_settings is not None and unit_from_settings != 'default': + if unit_from_settings.lower() == FAHRENHEIT: + temperature_unit = FAHRENHEIT + elif unit_from_settings.lower() == CELSIUS: + temperature_unit = CELSIUS + + return temperature_unit diff --git a/source/dialog.py b/source/dialog.py new file mode 100644 index 00000000..48be27ce --- /dev/null +++ b/source/dialog.py @@ -0,0 +1,226 @@ +from mycroft.util.format import nice_number, nice_time +from mycroft.util.time import now_local +from .util import get_time_period +from .weather import ( + is_current_weather, is_hourly_forecast, is_daily_forecast +) + +CURRENT = "current" +DAILY = "daily" +HOURLY = "hourly" + + +# TODO: MISSING DIALOGS +# - current.clear.alternative.local +# - current.clouds.alternative.local +# - daily.snow.alternative.local +# - all hourly..alternative.local/location +# - all hourly..not.expected.local/location +class WeatherDialog: + def __init__(self, weather, config, intent_data): + self.weather = weather + self.config = config + self.intent_data = intent_data + self.current_weather = is_current_weather(weather) + self.daily_forecast = is_daily_forecast(weather) + self.hourly_forecast = is_hourly_forecast(weather) + self.name = None + self.data = None + + def build_current_weather_dialog(self): + self.name = "current.weather" + self.data = dict( + condition=self.weather.condition.description, + temperature=self.weather.temperature, + temperature_unit=self.config.temperature_unit + ) + self._add_location() + + def build_high_low_temperature_dialog(self): + self.name = "current.temperature.high.low" + self.data = dict( + high_temperature=self.weather.high_temperature, + low_temperature=self.weather.low_temperature + ) + + def build_hourly_weather_dialog(self): + self.name = "hourly.weather" + self.data = dict( + condition=self.weather.condition.description, + time=self.weather.date_time.strftime("%H:00"), + temperature=self.weather.temperature, + ) + self._add_location() + + def build_daily_weather_dialog(self): + self.name = "daily.weather" + self.data = dict( + condition=self.weather.condition.description, + day=self.weather.date_time.strftime("%A"), + high_temperature=self.weather.temperature.high, + low_temperature=self.weather.temperature.low + ) + if self.weather.date_time.date() == self.intent_data.location_datetime.date(): + self.data.update(day="Today") + self._add_location() + + def build_temperature_dialog(self, temperature_type): + if self.daily_forecast: + self._build_daily_temperature_dialog(temperature_type) + elif self.hourly_forecast: + self._build_hourly_temperature_dialog() + else: + self._build_current_temperature_dialog(temperature_type) + self.data.update( + temperature_unit=self.intent_data.unit or self.config.temperature_unit + ) + self._add_location() + + def _build_current_temperature_dialog(self, temperature_type): + self.name = "current.temperature" + if temperature_type == "high": + self.name += ".high" + self.data = dict(temperature=self.weather.high_temperature) + elif temperature_type == "low": + self.name += ".low" + self.data = dict(temperature=self.weather.low_temperature) + else: + self.data = dict(temperature=self.weather.temperature) + + def _build_daily_temperature_dialog(self, temperature_type): + self.name = "daily.temperature" + if temperature_type == "high": + self.name += ".high" + self.data = dict(temperature=self.weather.temperature.high) + elif temperature_type == "low": + self.name += ".low" + self.data = dict(temperature=self.weather.temperature.low) + else: + self.data = dict(temperature=self.weather.temperature.day) + self.data.update(day=self.weather.date_time.strftime('%A')) + + def _build_hourly_temperature_dialog(self): + self.name = "hourly.temperature" + self.data = dict( + temperature=self.weather.temperature, + time=get_time_period(self.weather.date_time) + ) + + def build_wind_dialog(self): + wind_strength = self.weather.determine_wind_strength( + self.config.speed_unit + ) + self.data = dict( + speed=nice_number(self.weather.wind_speed), + speed_unit=self.config.speed_unit, + direction=self.weather.wind_direction + ) + self.name = self.intent_data.timeframe + if self.intent_data.timeframe == DAILY: + self.data.update(day=self.weather.date_time.strftime("%A")) + elif self.hourly_forecast: + self.data.update(time=nice_time(self.weather.date_time)) + self.name += '.wind.' + wind_strength + self._add_location() + + def build_humidity_dialog(self): + self.data = dict(percent=self.weather.humidity) + if self.intent_data.timeframe == DAILY: + self.name = "daily.humidity" + self.data.update(day=self.weather.date_time.strftime("%A")) + else: + self.name = "current.humidity" + self._add_location() + + def build_condition_dialog(self, condition, intent_match, alternative): + """Select the relevant dialog file for condition based reports. + + A condition can for example be "snow" or "rain". + + Arguments: + condition (string): name of condition eg snow + + Returns: + dialog (string): name of dialog file + """ + self.data = dict(condition=condition.lower()) + if self.daily_forecast: + self.name = "daily" + self.data.update(day=self.weather.date_time.strftime("%A")) + elif self.hourly_forecast: + self.name = "hourly" + self.data.update(time=nice_time(self.weather.date_time)) + else: + self.name = "current" + if intent_match: + self.name += ".condition.expected" + elif alternative: + self.name += ".{}.alternative".format(condition.lower()) + else: + self.name += ".{}.not.expected".format(condition.lower()) + self._add_location() + + def build_next_precipitation_dialog(self): + if self.weather is None: + self.name = 'daily.precipitation.next.none' + self.data = dict() + else: + if self.intent_data.timeframe == DAILY: + self.name = 'daily.precipitation.next' + self.data = dict(day=self.weather.date_time.strftime("%A")) + else: + self.name = 'hourly.precipitation.next' + self.data = dict(time=get_time_period(self.weather.date_time)) + self.data = dict( + percent=self.weather.chance_of_precipitation, + precipitation="rain", + day=self.weather.date_time.strftime("%A") + ) + self._add_location() + + def build_sunrise_dialog(self): + if self.daily_forecast: + self.name = "daily.sunrise" + else: + if self.intent_data.location is None: + now = now_local() + else: + now = now_local(tz=self.intent_data.geolocation["timezone"]) + if now < self.weather.sunrise: + self.name = "current.sunrise.future" + else: + self.name = "current.sunrise.past" + self._add_location() + self.data = dict(time=nice_time(self.weather.sunrise.strftime("%H:%M"))) + + def build_sunset_dialog(self): + if self.daily_forecast: + self.name = "daily.sunset" + else: + if self.intent_data.location is None: + now = now_local() + else: + now = now_local(tz=self.intent_data.geolocation["timezone"]) + if now < self.weather.sunset: + self.name = "current.sunset.future" + else: + self.name = "current.sunset.past" + self._add_location() + self.data = dict(time=nice_time(self.weather.sunset.strftime("%H:%M"))) + + def _add_location(self): + if self.intent_data.location is None: + self.name += ".local" + else: + self.name += ".location" + if self.config.country == self.intent_data.geolocation["country"]: + spoken_location = ', '.join([ + self.intent_data.geolocation["city"], + self.intent_data.geolocation["region"] + ]) + else: + spoken_location = ', '.join([ + self.intent_data.geolocation["city"], + self.intent_data.geolocation["country"] + ]) + self.data.update(location=spoken_location) diff --git a/source/intent.py b/source/intent.py new file mode 100644 index 00000000..65200b88 --- /dev/null +++ b/source/intent.py @@ -0,0 +1,64 @@ +from datetime import timedelta + +from mycroft.util.time import now_local +from .util import ( + get_utterance_datetime, get_geolocation, get_tz_info, LocationNotFoundError +) + + +class WeatherIntent: + _geolocation = None + _intent_datetime = None + _location_datetime = None + + def __init__(self, message, language): + self.utterance = message.data["utterance"] + self.location = message.data.get("Location") + self.language = language + self.unit = message.data.get("Unit") + self.timeframe = "current" + + @property + def geolocation(self): + if self._geolocation is None: + if self.location is None: + self._geolocation = dict() + else: + self._geolocation = get_geolocation(self.location) + if self._geolocation["city"].lower() not in self.location.lower(): + raise LocationNotFoundError( + self.location + " is not a city" + ) + + return self._geolocation + + @property + def intent_datetime(self): + if self._intent_datetime is None: + utterance_datetime = get_utterance_datetime( + self.utterance, + timezone=self.geolocation.get("timezone"), + language=self.language + ) + if utterance_datetime is not None: + delta = utterance_datetime - self.location_datetime + if int(delta / timedelta(days=1)) > 7: + raise ValueError("Weather forecasts only supported up to 7 days") + if utterance_datetime.date() < self.location_datetime.date(): + raise ValueError("Historical weather is not supported") + self._intent_datetime = utterance_datetime + else: + self._intent_datetime = self.location_datetime + + return self._intent_datetime + + @property + def location_datetime(self): + if self._location_datetime is None: + if self.location is None: + self._location_datetime = now_local() + else: + tz_info = get_tz_info(self.geolocation["timezone"]) + self._location_datetime = now_local(tz_info) + + return self._location_datetime diff --git a/source/util.py b/source/util.py new file mode 100644 index 00000000..eb16675c --- /dev/null +++ b/source/util.py @@ -0,0 +1,93 @@ +from datetime import datetime +from typing import Optional + +import pytz + +from mycroft.api import GeolocationApi +from mycroft.util.format import nice_time +from mycroft.util.parse import extract_datetime +from mycroft.util.time import now_local, to_utc + + +class LocationNotFoundError(ValueError): + pass + + +def convert_to_local_datetime(timestamp, timezone): + naive_datetime = datetime.fromtimestamp(timestamp) + utc_datetime = pytz.utc.localize(naive_datetime) + local_timezone = pytz.timezone(timezone) + local_datetime = utc_datetime.astimezone(local_timezone) + + return local_datetime + + +def get_utterance_datetime(utterance, timezone=None, language=None): + # Change timezone returned by extract_datetime from Local to UTC + utterance_datetime = None + if timezone is None: + anchor_date = None + else: + intent_timezone = get_tz_info(timezone) + anchor_date = datetime.now(intent_timezone) + extract = extract_datetime(utterance, anchor_date, language) + if extract is not None: + utterance_datetime, _ = extract + + return utterance_datetime + + +def get_tz_info(timezone): + return pytz.timezone(timezone) +# def get_spoken_time(date): +# # compatibility wrapper for nice_time +# nt_supported_languages = ['en', 'es', 'it', 'fr', 'de', +# 'hu', 'nl', 'da'] +# if not (lang[0:2] in nt_supported_languages): +# lang = "en-us" +# return nice_time(dt, lang, speech, use_24hour, use_ampm) + + +def get_geolocation(location: str): + geolocation_api = GeolocationApi() + geolocation = geolocation_api.get_geolocation(location) + + if geolocation is None: + raise LocationNotFoundError("Location {} is unknown".format(location)) + + return geolocation + + +def get_time_period(intent_datetime): + # Translate a specific time '9am' to period of the day 'morning' + hour = intent_datetime.time().hour + if 1 <= hour < 5: + period = "early morning" + elif 5 <= hour < 12: + period = "morning" + elif 12 <= hour < 17: + period = "afternoon" + elif 17 <= hour < 20: + period = "evening" + else: + period = "overnight" + + return period + + +def get_sequence_of_days(weather, condition_category): + longest_sequence = [] + this_sequence = [] + last_in_sequence = 0 + for day_count, daily in enumerate(weather.daily): + if daily.condition.catetory == condition_category: + if not last_in_sequence or day_count == last_in_sequence + 1: + this_sequence.append(daily) + last_in_sequence += 1 + else: + if 1 > len(this_sequence) > len(longest_sequence): + longest_sequence = this_sequence + this_sequence = [] + last_in_sequence = day_count + + return longest_sequence diff --git a/source/weather.py b/source/weather.py new file mode 100644 index 00000000..422be3ea --- /dev/null +++ b/source/weather.py @@ -0,0 +1,209 @@ +from datetime import time, timedelta + +from mycroft.util.time import now_local +from .config import MILES_PER_HOUR +from .util import convert_to_local_datetime + +THIRTY_PERCENT = 30 +SATURDAY = 5 +SUNDAY = 6 +WIND_DIRECTION_CONVERSION = ( + (22.5, "north"), + (67.5, "northeast"), + (112.5, "east"), + (157.5, "southeast"), + (202.5, "south"), + (247.5, "southwest"), + (292.5, "west"), + (337.5, "northwest") +) + + +def is_daily_forecast(weather_report): + return isinstance(weather_report, DailyWeather) + + +def is_hourly_forecast(weather_report): + return isinstance(weather_report, HourlyWeather) + + +def is_current_weather(weather_report): + return isinstance(weather_report, CurrentWeather) + + +class WeatherCondition: + def __init__(self, conditions): + self.id = conditions["id"] + self.category = conditions["main"] + self.description = conditions["description"] + self.icon = conditions["icon"] + + +class Weather: + def __init__(self, weather, timezone): + self.date_time = convert_to_local_datetime(weather["dt"], timezone) + self.feels_like = weather["feelsLike"] + self.pressure = weather["pressure"] + self.humidity = weather["humidity"] + self.dew_point = weather["dewPoint"] + self.clouds = weather["clouds"] + self.wind_speed = int(weather["windSpeed"]) + self.wind_direction = self._determine_wind_direction(weather["windDeg"]) + self.condition = WeatherCondition(weather["weather"][0]) + + @staticmethod + def _determine_wind_direction(degree_direction): + wind_direction = None + for min_degree, compass_direction in WIND_DIRECTION_CONVERSION: + if degree_direction < min_degree: + wind_direction = compass_direction + break + if wind_direction is None: + wind_direction = "north" + + return wind_direction + + def determine_wind_strength(self, speed_unit): + if speed_unit == MILES_PER_HOUR: + limits = dict(strong=20, moderate=11) + else: + limits = dict(strong=9, moderate=5) + if self.wind_speed >= limits["strong"]: + wind_strength = "strong" + elif self.wind_speed >= limits["moderate"]: + wind_strength = "moderate" + else: + wind_strength = "light" + + return wind_strength + + +class CurrentWeather(Weather): + def __init__(self, weather, timezone): + super().__init__(weather, timezone) + self.sunrise = convert_to_local_datetime(weather["sunrise"], timezone) + self.sunset = convert_to_local_datetime(weather["sunset"], timezone) + self.temperature = round(weather["temp"]) + self.visibility = weather["visibility"] + self.low_temperature = None + self.high_temperature = None + + +class DailyFeelsLike: + def __init__(self, temperatures): + self.day = round(temperatures["day"]) + self.night = round(temperatures["night"]) + self.evening = round(temperatures["eve"]) + self.morning = round(temperatures["morn"]) + + +class DailyTemperature(DailyFeelsLike): + def __init__(self, temperatures): + super().__init__(temperatures) + self.low = round(temperatures["min"]) + self.high = round(temperatures["max"]) + + +class DailyWeather(Weather): + def __init__(self, weather, timezone): + super().__init__(weather, timezone) + self.sunrise = convert_to_local_datetime(weather["sunrise"], timezone) + self.sunset = convert_to_local_datetime(weather["sunset"], timezone) + self.temperature = DailyTemperature(weather["temp"]) + self.feels_like = DailyFeelsLike(weather["feelsLike"]) + self.chance_of_precipitation = int(weather["pop"] * 100) + + +class HourlyWeather(Weather): + def __init__(self, weather, timezone): + super().__init__(weather, timezone) + self.temperature = round(weather["temp"]) + self.chance_of_precipitation = int(weather["pop"] * 100) + + +class WeatherAlert: + def __init__(self, alert, timezone): + self.sender = alert.get("sender_name") + self.event = alert["event"] + self.start = convert_to_local_datetime(alert["start"], timezone) + self.end = convert_to_local_datetime(alert["end"], timezone) + self.description = alert["description"] + + +class WeatherReport: + def __init__(self, report): + timezone = report["timezone"] + self.current = CurrentWeather(report["current"], timezone) + self.hourly = [HourlyWeather(hour, timezone) for hour in report["hourly"]] + self.daily = [DailyWeather(day, timezone) for day in report["daily"]] + today = self.daily[0] + self.current.high_temperature = today.temperature.high + self.current.low_temperature = today.temperature.low + if "alerts" in report: + self.alerts = [ + WeatherAlert(alert, timezone) for alert in report["alerts"]] + else: + self.alerts = None + + def get_weather_for_intent(self, intent_data): + if intent_data.timeframe == "hourly": + weather = self.get_forecast_for_hour(intent_data) + elif intent_data.timeframe == "daily": + weather = self.get_forecast_for_date(intent_data) + else: + weather = self.current + + return weather + + def get_forecast_for_date(self, intent_data): + if intent_data.intent_datetime.date() == intent_data.location_datetime.date(): + forecast = self.daily[0] + else: + delta = intent_data.intent_datetime - intent_data.location_datetime + day_delta = int(delta / timedelta(days=1)) + day_index = day_delta + 1 + forecast = self.daily[day_index] + + return forecast + + def get_forecast_for_hour(self, intent_data): + delta = intent_data.intent_datetime - intent_data.location_datetime + hour_delta = int(delta / timedelta(hours=1)) + hour_index = hour_delta + 1 + report = self.hourly[hour_index] + + return report + + def get_weekend_forecast(self): + forecast = list() + for forecast_day in self.daily: + report_date = forecast_day.date_time.date() + if report_date.weekday() in (SATURDAY, SUNDAY): + forecast.append(forecast_day) + + return forecast + + def get_next_precipitation(self, intent_data): + report = None + current_precipitation = True + timeframe = "hourly" + for hourly in self.hourly: + if hourly.date_time.date() > intent_data.location_datetime.date(): + break + if hourly.chance_of_precipitation > THIRTY_PERCENT: + if not current_precipitation: + report = hourly + break + else: + current_precipitation = False + + if report is None: + timeframe = "daily" + for daily in self.daily: + if daily.date_time.date() == intent_data.location_datetime.date(): + continue + if daily.chance_of_precipitation > THIRTY_PERCENT: + report = daily + break + + return report, timeframe diff --git a/test/behave/current-temperature-local.feature b/test/behave/current-temperature-local.feature new file mode 100644 index 00000000..019bbfad --- /dev/null +++ b/test/behave/current-temperature-local.feature @@ -0,0 +1,44 @@ +#Feature: Mycroft Weather Skill current local weather +# +# Scenario Outline: What is the temperature today +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.local.dialog" +# +# Examples: What is the temperature today +# | what is the temperature today | +# | what is the temperature today | +# | temperature | +# | what's the temperature | +# | what will be the temperature today | +# | temperature today | +# | what's the temp | +# | temperature outside | +# +# +# Scenario Outline: What is the high temperature today +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.high.local.dialog" +# +# Examples: What is the high temperature today +# | what is the high temperature today | +# | what is the high temperature today | +# | what's the high temp today | +# | what's the high temperature | +# | how hot will it be today | +# | how hot is it today | +# | what's the current high temperature | +# | high temperature | +# +# +# Scenario Outline: What is the low temperature today +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.low.local.dialog" +# +# Examples: What is the low temperature today +# | what is the low temperature today | +# | what is the low temperature today | +# | what will the lowest temperature be today | +# diff --git a/test/behave/current-temperature-location.feature b/test/behave/current-temperature-location.feature new file mode 100644 index 00000000..707fdf55 --- /dev/null +++ b/test/behave/current-temperature-location.feature @@ -0,0 +1,47 @@ +#Feature: Mycroft Weather Skill current temperature at specified location +# +# Scenario Outline: User asks for the temperature today in a location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.location.dialog" +# +# Examples: what is the temperature today in location +# | what is the temperature today in location | +# | temperature in sydney | +# | temperature today in san francisco, california | +# | temperature outside in kansas city | +# | In tokyo what's the temp | +# | what will be the temperature today in berlin | +# | what's the temperature in new york city | +# +# +# Scenario Outline: User asks for the high temperature today in a location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.high.location.dialog" +# +# Examples: what is the high temperature today in location +# | what is the high temperature today in location | +# | what's the high temperature in san francisco california | +# | how hot will it be today in kansas city | +# | what's the current high temperature in kansas | +# | how hot is it today in tokyo | +# | what is the high temperature today in sydney | +# | what's the high temp today in berlin | +# | high temperature in new york city | +# +# +# Scenario Outline: User asks for the low temperature in a location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.temperature.low.location.dialog" +# +# Examples: low temperature today in location +# | what is the low temperature today in location | +# | what's the low temperature in san francisco california | +# | how cold will it be today in kansas city | +# | low temperature today in sydney | +# | what's the low temp today in berlin | +# | what's the current low temperature in kansas | +# | how cold is it today in tokyo | +# | low temperature in new york city | diff --git a/test/behave/current-weather-local.feature b/test/behave/current-weather-local.feature new file mode 100644 index 00000000..1e8b3a9e --- /dev/null +++ b/test/behave/current-weather-local.feature @@ -0,0 +1,29 @@ +#Feature: Mycroft Weather Skill local current weather conditions +# +# Scenario Outline: What is the current local weather +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.weather.local.dialog" +# +# Examples: What is the current local weather +# | current local weather | +# | tell me the current weather | +# | what's the current weather like | +# | what is the current weather like | +# | current weather | +# | what is it like outside | +# | what's the current weather conditions | +# | give me the current weather | +# | tell me the current weather | +# | how's the weather | +# | tell me the weather | +# | what's the weather like | +# | weather | +# | what's the weather conditions | +# | give me the weather | +# | tell me the weather | +# | what's the forecast | +# | weather forecast | +# | what's the weather forecast | +# | how is the weather now | +# | what is it like outside right now | diff --git a/test/behave/current-weather-location.feature b/test/behave/current-weather-location.feature new file mode 100644 index 00000000..c554f8e4 --- /dev/null +++ b/test/behave/current-weather-location.feature @@ -0,0 +1,40 @@ +#Feature: Mycroft Weather Skill current weather at a specified location +# +# Scenario Outline: User asks for the current weather in a location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.weather.location.dialog" +# +# Examples: what is the current local weather in a location +# | what is the current weather in location | +# | what is the current weather in san francisco, california | +# | current weather in kansas city | +# | tell me the current weather in sydney | +# | what's the current weather like in berlin | +# | how's the weather in Paris | +# | tell me the weather in Paris, Texas | +# | give me the current weather in Kansas | +# | what is it like outside in italy | +# | In tokyo what is it like outside | +# | how is the weather in new york city | +# +# +# @xfail +# Scenario Outline: FAILING User asks for the current weather in a location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "current.weather.location.dialog" +# +# Examples: what is the current local weather in a location +# | what is the current weather in location | +# | what's the current weather conditions in Washington, D.C. | +# +# +# Scenario Outline: User asks for the current weather in an unknown location +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "location.not.found.dialog" +# +# Examples: what is the current local weather in a location +# | what is the current weather in location | +# | tell me the current weather in Missouri | diff --git a/test/behave/daily-temperature-local.feature b/test/behave/daily-temperature-local.feature new file mode 100644 index 00000000..5a7d3898 --- /dev/null +++ b/test/behave/daily-temperature-local.feature @@ -0,0 +1,97 @@ +#Feature: Mycroft Weather Skill local forecasted temperatures +# +# Scenario Outline: What is the temperature for tomorrow +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.local.dialog" +# +# Examples: what is the temperature for tomorrow +# | what is the temperature tomorrow | +# | what will be the temperature for tomorrow | +# +# +# @xfail +# # Jira MS-98 https://mycroft.atlassian.net/browse/MS-98 +# Scenario Outline: Failing what is the temperature for tomorrow +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.local.dialog" +# +# Examples: what is the temperature for tomorrow +# | what is the temperature tomorrow | +# | what's the temperature tomorrow | +# +# +# Scenario Outline: what is the high temperature for tomorrow +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.high.local.dialog" +# +# Examples: what is the high temperature for tomorrow +# | what is the high temperature tomorrow | +# | what is the high temperature tomorrow | +# | tomorrow what is the high temperature | +# | tomorrow how hot will it get | +# | how hot will it be tomorrow | +# | what should I expect for a high temperature tomorrow | +# | what is the expected high temperature for tomorrow | +# +# +# Scenario Outline: what is the low temperature for tomorrow +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.low.local.dialog" +# +# Examples: what is the low temperature for tomorrow +# | what is the low temperature tomorrow | +# | what is the low temperature tomorrow | +# | tomorrow what is the low temperature | +# | how cold will it be tomorrow | +# | what should I expect for a low temperature tomorrow | +# | what is the expected low temperature for tomorrow | +# +# +# Scenario Outline: what is the temperature for a future date +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.local.dialog" +# +# Examples: what is the temperature for a future date +# | what is the temperature for a future date | +# | what is the temperature for wednesday | +# | what is the temperature for saturday | +# | what is the temperature 5 days from now | +# +# Scenario Outline: what is the high temperature for a future date +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.high.local.dialog" +# +# Examples: what is the high temperature for a future date +# | what is the high temperature for a future date | +# | what is the high temperature for wednesday | +# | what is the high temperature for saturday | +# | what is the high temperature 5 days from now | +# +# Scenario Outline: what is the low temperature for a future date +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.temperature.low.local.dialog" +# +# Examples: what is the low temperature for a future date +# | what is the low temperature for a future date | +# | what is the low temperature for wednesday | +# | what is the low temperature for saturday | +# | what is the low temperature 5 days from now | +# +# Scenario Outline: what is the temperature at a certain time +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "hourly.temperature.local.dialog" +# +# Examples: what is the temperature at a certain time +# | what is the temperature at a certain time | +# | what will the temperature be tonight | +# | what will the temperature be this evening | +# | what is the temperature this morning | +# | temperature in the afternoon | diff --git a/test/behave/daily-weather-local.feature b/test/behave/daily-weather-local.feature new file mode 100644 index 00000000..f1b3f15f --- /dev/null +++ b/test/behave/daily-weather-local.feature @@ -0,0 +1,25 @@ +#Feature: Mycroft Weather Skill local daily forecasts +# +# Scenario Outline: what is the forecast for tomorrow +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.weather.local.dialog" +# +# Examples: What is the forecast for tomorrow +# | what is the forecast for tomorrow | +# | what is the forecast for tomorrow | +# | what is the weather tomorrow | +# | what is the weather like tomorrow | +# | tomorrow what will the weather be like | +# +# Scenario Outline: what is the forecast for a future date +# Given an english speaking user +# When the user says "" +# Then "mycroft-weather" should reply with dialog from "daily.weather.local.dialog" +# +# Examples: what is the forecast for a future date +# | what is the forecast for a future date | +# | what is the weather like tuesday | +# | what is the weather like on saturday | +# | what is the weather like monday | +# | what is the weather like in 5 days from now | diff --git a/test/behave/daily-weather-location.feature b/test/behave/daily-weather-location.feature new file mode 100644 index 00000000..7371904b --- /dev/null +++ b/test/behave/daily-weather-location.feature @@ -0,0 +1,14 @@ +Feature: Mycroft Weather Skill daily forecast for a specified location. + + Scenario Outline: User asks for the forecast on a future date in a location + Given an english speaking user + When the user says "" + Then "mycroft-weather" should reply with dialog from "daily.weather.location.dialog" + + Examples: what is the forecast for a future date in location + | what is the forecast on a future date in a location | + | what is the weather tomorrow in sydney | + | what is the weather like in new york city tuesday | + | what is the weather like in san francisco california saturday | + | what is the weather like in kansas city friday | + | what is the weather like in berlin on sunday | diff --git a/test/behave/weather-local.feature b/test/behave/weather-local.feature deleted file mode 100644 index 067e1acb..00000000 --- a/test/behave/weather-local.feature +++ /dev/null @@ -1,239 +0,0 @@ -Feature: Mycroft Weather Skill local forecasts and temperature - - Scenario Outline: What is the current local weather - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.weather.dialog" - - Examples: What is the current local weather - | current local weather | - | tell me the current weather | - | what's the current weather like | - | what is the current weather like | - | current weather | - | what is it like outside | - | what's the current weather conditions | - | give me the current weather | - | tell me the current weather | - | how's the weather | - | tell me the weather | - | what's the weather like | - | weather | - | what's the weather conditions | - | give me the weather | - | tell me the weather | - | what's the forecast | - | weather forecast | - | what's the weather forecast | - - @xfail - # JIra MS-54 https://mycroft.atlassian.net/browse/MS-54 - Scenario Outline: Failing what is the current local weather - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.weather.dialog" - - Examples: What is the current local weather - | current local weather | - | how is the weather now | - | what is it like outside right now | - - Scenario Outline: What is the temperature today - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.temperature.dialog" - - Examples: What is the temperature today - | what is the temperature today | - | what is the temperature today | - | temperature | - | what's the temperature | - | what will be the temperature today | - | temperature today | - - - @xfail - # JIra MS-55 https://mycroft.atlassian.net/browse/MS-55 - Scenario Outline: Failing What is the temperature today - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.temperature.dialog" - - Examples: Failing temperature examples - | what is the temperature today | - | what's the temp | - | temperature outside | - - Scenario Outline: What is the high temperature today - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.high.temperature.dialog" - - Examples: What is the high temperature today - | what is the high temperature today | - | what is the high temperature today | - | what's the high temp today | - | what's the high temperature | - | how hot will it be today | - | how hot is it today | - | what's the current high temperature | - - @xfail - # JIra MS-97 https://mycroft.atlassian.net/browse/MS-97 - Scenario Outline: Failing what is the high temperature today - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.high.temperature.dialog" - - Examples: What is the high temperature today - | what is the high temperature today | - | high temperature | - - Scenario Outline: What is the low temperature today - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.local.low.temperature.dialog" - - Examples: What is the low temperature today - | what is the low temperature today | - | what is the low temperature today | - | what will the lowest temperature be today | - - Scenario Outline: what is the forecast for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.weather.dialog" - - Examples: What is the forecast for tomorrow - | what is the forecast for tomorrow | - | what is the forecast for tomorrow | - | what is the weather tomorrow | - | what is the weather like tomorrow | - | tomorrow what will the weather be like | - - Scenario Outline: what is the forecast for a future date - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.weather.dialog" - - Examples: what is the forecast for a future date - | what is the forecast for a future date | - | what is the weather like next tuesday | - | what is the weather like on next saturday | - | what is the weather like next monday | - - @xfail - # Jira MS-57 https://mycroft.atlassian.net/browse/MS-57 - Scenario Outline: failing what is the forecast for a future date - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.weather.dialog" - - Examples: what is the forecast for a future date - | what is the forecast for a future date | - | what is the weather like in 9 days from now | - - @xfail - # Jira MS-98 https://mycroft.atlassian.net/browse/MS-98 - Scenario Outline: What is the temperature for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.temperature.dialog" - - Examples: what is the temperature for tomorrow - | what is the temperature tomorrow | - | what will be the temperature for tomorrow | - | what's the temperature tomorrow | - - Scenario Outline: what is the high temperature for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.high.temperature.dialog" - - Examples: what is the high temperature for tomorrow - | what is the high temperature tomorrow | - | what is the high temperature tomorrow | - | tomorrow what is the high temperature | - | tomorrow how hot will it get | - | how hot will it be tomorrow | - - @xfail - # Jira Ms-98 https://mycroft.atlassian.net/browse/MS-98 - Scenario Outline: failing what is the high temperature for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.high.temperature.dialog" - - Examples: what is the high temperature for tomorrow - | what is the high temperature tomorrow | - | what should I expect for a high temperature tomorrow | - | what is the expected high temperature for tomorrow | - - Scenario Outline: what is the low temperature for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.low.temperature.dialog" - - Examples: what is the low temperature for tomorrow - | what is the low temperature tomorrow | - | what is the low temperature tomorrow | - | tomorrow what is the low temperature | - | how cold will it be tomorrow | - - @xfail - # Jira Ms-99 https://mycroft.atlassian.net/browse/MS-99 - Scenario Outline: failing what is the low temperature for tomorrow - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.low.temperature.dialog" - - Examples: what is the low temperature for tomorrow - | what is the low temperature tomorrow | - | what should I expect for a low temperature tomorrow | - | what is the expected low temperature for tomorrow | - - Scenario Outline: what is the temperature for a future date - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.temperature.dialog" - - Examples: what is the temperature for a future date - | what is the temperature for a future date | - | what is the temperature for next wednesday | - | what is the temperature for next saturday | - | what is the temperature 5 days from now | - - Scenario Outline: what is the high temperature for a future date - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.high.temperature.dialog" - - Examples: what is the high temperature for a future date - | what is the high temperature for a future date | - | what is the high temperature for next wednesday | - | what is the high temperature for next saturday | - | what is the high temperature 5 days from now | - - Scenario Outline: what is the low temperature for a future date - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.local.low.temperature.dialog" - - Examples: what is the low temperature for a future date - | what is the low temperature for a future date | - | what is the low temperature for next wednesday | - | what is the low temperature for next saturday | - | what is the low temperature 5 days from now | - - @xfail - Scenario Outline: what is the temperature at a certain time - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "at.time.local.temperature.dialog" - - Examples: what is the temperature at a certain time - | what is the temperature at a certain time | - | what will the temperature be tonight | - | what will the temperature be this evening | - | what is the temperature this morning | - | temperature in the afternoon | diff --git a/test/behave/weather-location.feature b/test/behave/weather-location.feature deleted file mode 100644 index b4acee87..00000000 --- a/test/behave/weather-location.feature +++ /dev/null @@ -1,115 +0,0 @@ -Feature: Mycroft Weather Skill location forecasts and temperature - - Scenario Outline: User asks for the current weather in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.weather.dialog" - - Examples: what is the current local weather in a location - | what is the current weather in location | - | how is the weather in new york city | - | what is the current weather in san francisco, california | - | current weather in kansas city | - | what's the current weather conditions in Washington DC | - - @xfail - Scenario Outline: Failing User asks for the current weather in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.weather.dialog" - - Examples: what is the current local weather in a location - | what is the current weather in location | - | what is it like outside in italy | - | In tokyo what is it like outside | - | give me the current weather in Kansas | - | tell me the current weather Missouri | - | tell me the current weather in sydney | - | what's the current weather like in berlin | - | how's the weather in Paris | - | tell me the weather in Paris, Texas | - - @xfail - Scenario Outline: Failing user asks for the temperature today in location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.temperature.dialog" - - Examples: what is the temperature today in location - | what is the temperature today in location | - | temperature in sydney | - | temperature today in san francisco, california | - | temperature outside in kansas city | - | In tokyo what's the temp | - | what's the temperature in new york city | - | what will be the temperature today in berlin | - - Scenario Outline: User asks for the high temperature today in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.high.temperature.dialog" - - Examples: what is the high temperature today in location - | what is the high temperature today in location | - | what's the high temperature in san francisco california | - | how hot will it be today in kansas city | - - @xfail - Scenario Outline: Failing user asks for the high temperature today in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.high.temperature.dialog" - - Examples: what is the high temperature today in location - | what is the high temperature today in location | - | high temperature in new york city | - | what's the current high temperature in kansas | - | how hot is it today in tokyo | - | what is the high temperature today in sydney | - | what's the high temp today in berlin | - - Scenario Outline: User asks for the low temperature in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.low.temperature.dialog" - - Examples: low temperature today in location - | what is the low temperature today in location | - | what's the low temperature in san francisco california | - | how cold will it be today in kansas city | - - @xfail - Scenario Outline: Failing user asks for the low temperature in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "current.low.temperature.dialog" - - Examples: low temperature today in location - | what is the low temperature today in location | - | what is the low temperature today in sydney | - | what's the low temp today in berlin | - | what's the current low temperature in kansas | - | low temperature in new york city | - | how cold is it today in tokyo | - - Scenario Outline: User asks for the forecast on a future date in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.weather.dialog" - - Examples: what is the forecast for a future date in location - | what is the forecast on a future date in a location | - | what is the weather tomorrow in sydney | - | what is the weather like in new york city next tuesday | - | what is the weather like in san francisco california next saturday | - | what is the weather like in kansas city next friday | - - @xfail - Scenario Outline: Failing User asks for the forecast on a future date in a location - Given an english speaking user - When the user says "" - Then "mycroft-weather" should reply with dialog from "forecast.weather.dialog" - - Examples: what is the forecast for a future date in location - | what is the forecast on a future date in a location | - | what is the weather like in berlin on sunday | diff --git a/test/behave/weather-precipitation.feature b/test/behave/weather-precipitation.feature index f2de1665..ca1ec177 100644 --- a/test/behave/weather-precipitation.feature +++ b/test/behave/weather-precipitation.feature @@ -1,86 +1,86 @@ - @xfail - Feature: Mycroft Weather Skill precipitation forecasts - - Scenario Outline: will it rain locally today, when it is expected - Given an english speaking user - And there is rain predicted for today - When the user says "" - Then "mycroft-weather" should reply with "rain is expected today." - - Examples: will it rain locally today when expected - - | rain locally today when expected | - | will it rain today | - | will it be rainy today | - | should I bring an umbrella | - | do I need an umbrella | - | should I bring a rain coat | - | do I need a rain jacket | - | does it look like rain today | - - Scenario Outline: will it rain locally today, when it is not expected - Given an english speaking user - And there is no rain predicted for today - When the user says "" - Then "mycroft-weather" should reply with "no rain is expected today." - - Examples: will it rain locally today when not expected - - | rain locally today when not expected | - | will it rain today | - | will it be rainy today | - | should I bring an umbrella | - | do I need an umbrella | - | should I bring a rain coat | - | do I need a rain jacket | - | does it look like rain today | - - Scenario Outline: will it snow locally today, when it is expected - Given an english speaking user - And there is snow predicted for today - When the user says "" - Then "mycroft-weather" should reply with "snow is expected today." - - Examples: will it snow locally today when expected - - | snow locally today when expected | - | will it snow today | - | will it be snowy today | - | does it look like snow today | - - Scenario Outline: Will it snow locally today, when it is not expected - Given an english speaking user - And there is no snow predicted for today - When the user says "" - Then "mycroft-weather" should reply with "no snow is expected today." - - Examples: will it snow locally today when not expected - - | snow locally today when not expected | - | will it snow today | - | will it be snowy today | - | does it look like snow today | - - Scenario Outline: Will it rain in a location today, when it is expected - Given an english speaking user - And there is rain predicted for today in a location - When the user says "" - Then "mycroft-weather" should reply with "yes, expect rain in Kansas City Missouri today" - - Examples: will it rain in a location today when expected - - | rain in a location today when expected | - | will it rain in Kansas city today | - | is there a chance of rain in charleston south carolina today | - | is there a chance of rain in paris | - - Scenario Outline: will it rain in a location in the future, when it is expected - Given an english speaking user - And there is rain predicted for the future in a location - When the user says "" - Then "mycroft-weather" should reply with "yes, the forecast calls for light rain in charleston south carolina tomorrow" - - Examples: will it rain in a location in the future when expected - - | will it rain in charleston south carolina tomorrow | - | will it rain in chicago on wednesday | +# @xfail +# Feature: Mycroft Weather Skill precipitation forecasts +# +# Scenario Outline: will it rain locally today, when it is expected +# Given an english speaking user +# And there is rain predicted for today +# When the user says "" +# Then "mycroft-weather" should reply with "rain is expected today." +# +# Examples: will it rain locally today when expected +# +# | rain locally today when expected | +# | will it rain today | +# | will it be rainy today | +# | should I bring an umbrella | +# | do I need an umbrella | +# | should I bring a rain coat | +# | do I need a rain jacket | +# | does it look like rain today | +# +# Scenario Outline: will it rain locally today, when it is not expected +# Given an english speaking user +# And there is no rain predicted for today +# When the user says "" +# Then "mycroft-weather" should reply with "no rain is expected today." +# +# Examples: will it rain locally today when not expected +# +# | rain locally today when not expected | +# | will it rain today | +# | will it be rainy today | +# | should I bring an umbrella | +# | do I need an umbrella | +# | should I bring a rain coat | +# | do I need a rain jacket | +# | does it look like rain today | +# +# Scenario Outline: will it snow locally today, when it is expected +# Given an english speaking user +# And there is snow predicted for today +# When the user says "" +# Then "mycroft-weather" should reply with "snow is expected today." +# +# Examples: will it snow locally today when expected +# +# | snow locally today when expected | +# | will it snow today | +# | will it be snowy today | +# | does it look like snow today | +# +# Scenario Outline: Will it snow locally today, when it is not expected +# Given an english speaking user +# And there is no snow predicted for today +# When the user says "" +# Then "mycroft-weather" should reply with "no snow is expected today." +# +# Examples: will it snow locally today when not expected +# +# | snow locally today when not expected | +# | will it snow today | +# | will it be snowy today | +# | does it look like snow today | +# +# Scenario Outline: Will it rain in a location today, when it is expected +# Given an english speaking user +# And there is rain predicted for today in a location +# When the user says "" +# Then "mycroft-weather" should reply with "yes, expect rain in Kansas City Missouri today" +# +# Examples: will it rain in a location today when expected +# +# | rain in a location today when expected | +# | will it rain in Kansas city today | +# | is there a chance of rain in charleston south carolina today | +# | is there a chance of rain in paris | +# +# Scenario Outline: will it rain in a location in the future, when it is expected +# Given an english speaking user +# And there is rain predicted for the future in a location +# When the user says "" +# Then "mycroft-weather" should reply with "yes, the forecast calls for light rain in charleston south carolina tomorrow" +# +# Examples: will it rain in a location in the future when expected +# +# | will it rain in charleston south carolina tomorrow | +# | will it rain in chicago on wednesday | diff --git a/dialog/en-us/this.week.dialog b/test/unit/__init__.py similarity index 100% rename from dialog/en-us/this.week.dialog rename to test/unit/__init__.py diff --git a/vocab/en-us/Cloudy.voc b/vocab/en-us/Clouds.voc similarity index 100% rename from vocab/en-us/Cloudy.voc rename to vocab/en-us/Clouds.voc diff --git a/vocab/en-us/CloudyAlternatives.voc b/vocab/en-us/CloudsAlternatives.voc similarity index 100% rename from vocab/en-us/CloudyAlternatives.voc rename to vocab/en-us/CloudsAlternatives.voc diff --git a/vocab/en-us/Foggy.voc b/vocab/en-us/Fog.voc similarity index 100% rename from vocab/en-us/Foggy.voc rename to vocab/en-us/Fog.voc diff --git a/vocab/en-us/Like.voc b/vocab/en-us/Like.voc new file mode 100644 index 00000000..2f673b18 --- /dev/null +++ b/vocab/en-us/Like.voc @@ -0,0 +1 @@ +like diff --git a/vocab/en-us/Location.voc b/vocab/en-us/Location.voc index a5de9c71..3c2b6969 100644 --- a/vocab/en-us/Location.voc +++ b/vocab/en-us/Location.voc @@ -3,7 +3,6 @@ Los Angeles california|los angeles|la Lawrence kansas portland oregon|portland spokane -new york chicago houston austin @@ -11,6 +10,6 @@ san diego san francisco san jose boston -washington dc kansas city atlanta +berlin diff --git a/vocab/en-us/Outside.voc b/vocab/en-us/Outside.voc new file mode 100644 index 00000000..e4f4c426 --- /dev/null +++ b/vocab/en-us/Outside.voc @@ -0,0 +1,2 @@ +outside +out diff --git a/vocab/en-us/Raining.voc b/vocab/en-us/Rain.voc similarity index 100% rename from vocab/en-us/Raining.voc rename to vocab/en-us/Rain.voc diff --git a/vocab/en-us/RelativeDay.voc b/vocab/en-us/RelativeDay.voc index 861e9f72..c3c0e951 100644 --- a/vocab/en-us/RelativeDay.voc +++ b/vocab/en-us/RelativeDay.voc @@ -5,9 +5,17 @@ tomorrow's yesterday yesterday's monday +on monday tuesday +on tuesday wednesday +on wednesday thursday +on thursday friday +on friday saturday -sunday \ No newline at end of file +on saturday +sunday +on sunday +days diff --git a/vocab/en-us/Snowing.voc b/vocab/en-us/Snow.voc similarity index 100% rename from vocab/en-us/Snowing.voc rename to vocab/en-us/Snow.voc diff --git a/vocab/en-us/Temperature.voc b/vocab/en-us/Temperature.voc index ce033c2d..857fb5b2 100644 --- a/vocab/en-us/Temperature.voc +++ b/vocab/en-us/Temperature.voc @@ -1,4 +1,5 @@ temperature +temp cold chilly cool diff --git a/vocab/en-us/Storm.voc b/vocab/en-us/Thunderstorm.voc similarity index 100% rename from vocab/en-us/Storm.voc rename to vocab/en-us/Thunderstorm.voc diff --git a/vocab/en-us/StormAlternatives.voc b/vocab/en-us/ThunderstormAlternatives.voc similarity index 100% rename from vocab/en-us/StormAlternatives.voc rename to vocab/en-us/ThunderstormAlternatives.voc diff --git a/vocab/en-us/what.is.multi.day.forecast.intent b/vocab/en-us/what.is.multi.day.forecast.intent index 71d040d9..4b46089d 100644 --- a/vocab/en-us/what.is.multi.day.forecast.intent +++ b/vocab/en-us/what.is.multi.day.forecast.intent @@ -1,4 +1,4 @@ -weather (in|over|for) the next {num} days -what is the weather (like|) (in|over|for) the next {num} days +(weather|forecast) (in|over|for) the next {num} days +what is the (weather|forecast) (like|) (in|over|for) the next {num} days what will the weather be (like|) (in|over|for) the next {num} days -what is the weather (going to |gonna |)be (like |)(in|over|for) the next {num} days +what is the weather (going to|gonna|) be (like|) (in|over|for) the next {num} days diff --git a/vocab/en-us/what.is.three.day.forecast.intent b/vocab/en-us/what.is.three.day.forecast.intent deleted file mode 100644 index 32f77149..00000000 --- a/vocab/en-us/what.is.three.day.forecast.intent +++ /dev/null @@ -1,2 +0,0 @@ -(3|three) day forecast -(tell me|what is) the (3 day|three day|extended) (forecast|weather forecast) diff --git a/vocab/en-us/what.is.three.day.forecast.location.intent b/vocab/en-us/what.is.three.day.forecast.location.intent deleted file mode 100644 index add0ff10..00000000 --- a/vocab/en-us/what.is.three.day.forecast.location.intent +++ /dev/null @@ -1 +0,0 @@ -(tell me|what is) the (3 day|three-day|extended) (weather|forecast|weather forecast) (for|in) {Location} diff --git a/vocab/en-us/what.is.two.day.forecast.intent b/vocab/en-us/what.is.two.day.forecast.intent deleted file mode 100644 index 1a905ca0..00000000 --- a/vocab/en-us/what.is.two.day.forecast.intent +++ /dev/null @@ -1,3 +0,0 @@ -weather (for|on|) (next|) {day_one} and {day_two} -what('s| is) the weather (going to |gonna |)be (like |)(for|on|) (next|) {day_one} and {day_two} -what will the weather be (like |)(for|on|) (next|) {day_one} and {day_two} diff --git a/vocab/en-us/whats.weather.like.intent b/vocab/en-us/whats.weather.like.intent deleted file mode 100644 index 2229cb86..00000000 --- a/vocab/en-us/whats.weather.like.intent +++ /dev/null @@ -1,5 +0,0 @@ -what is it like out (today|) -what is it like outside -weather -forecast -weather forecast