-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First commit and set version to v0.0.2
- Loading branch information
1 parent
25a3002
commit dcd7557
Showing
5 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import logging | ||
import async_timeout | ||
import requests | ||
import datetime | ||
import re | ||
from datetime import timedelta | ||
from homeassistant.helpers import discovery | ||
from homeassistant.core import HomeAssistant | ||
from.const import (DOMAIN, | ||
REPOS_API_URL, | ||
CONF_EXCHANGE, | ||
CONF_STOCK, | ||
MIN_UPDATE_INTERVAL, | ||
MAX_UPDATE_INTERVAL, | ||
TIME_SLICES) | ||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||
|
||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup(hass: HomeAssistant, hass_config: dict): | ||
config = hass_config[DOMAIN] | ||
coordinator = StockCorrdinator(hass, config) | ||
hass.data[DOMAIN] = coordinator | ||
await coordinator.async_refresh() | ||
hass.async_create_task(discovery.async_load_platform( | ||
hass, "sensor", DOMAIN, config, hass_config)) | ||
|
||
return True | ||
|
||
UPDATE_INTERVAL = timedelta(seconds=MIN_UPDATE_INTERVAL) | ||
|
||
|
||
class StockCorrdinator(DataUpdateCoordinator): | ||
_tencent_data_format = ['stock', 'unused', 'name', '股票代码', '当前价格', '昨收', '今开', '成交量(手)', '外盘', '内盘', | ||
'买一', '买一量(手)', '买二', '买二量(手)', '买三', '买三量(手)', '买四', '买四量(手)', '买五', | ||
'买五量(手)', '卖一', '卖一量(手)', '卖二', '卖二量(手)', '卖三', '卖三量(手)', '卖四', | ||
'卖四量(手)', '卖五', '卖五量(手)', 'unknown1', 'datetime', '涨跌', '涨跌(%)', '最高', '最低', | ||
'价格/成交量(手)/成交额', '成交量(手)', '成交额(万)', '换手率', '市盈率', 'unknown2', '最高1', '最低1', '振幅', | ||
'流通市值', '总市值', '市净率', '涨停价', '跌停价', '量比', '委差', '均价', '市盈(动)', '市盈(静)'] | ||
_tencent_data_patten = re.compile(r'v_([-/\.\w]*)="([\w]*)' + (r'~([-/\.\w]*)' * (len(_tencent_data_format) - 2))) | ||
|
||
def __init__(self, hass, config): | ||
self._hass = hass | ||
self._quest_url = REPOS_API_URL | ||
if config is not None and "time_slices" in config: | ||
self._time_slice = config["time_slices"] | ||
else: | ||
self._time_slice = TIME_SLICES | ||
_LOGGER.debug(self._time_slice) | ||
self._count = MAX_UPDATE_INTERVAL | ||
for exchange, stocks in config["stocks"].items(): | ||
for stock in stocks: | ||
self._quest_url = self._quest_url + exchange + stock + "," | ||
|
||
super().__init__( | ||
hass, | ||
_LOGGER, | ||
name=DOMAIN, | ||
update_interval=UPDATE_INTERVAL, | ||
) | ||
|
||
def format_response_data(self, res_data): | ||
res_data = res_data.replace(" ", "") | ||
data = "".join(res_data) | ||
data = self._tencent_data_patten.finditer(data) | ||
result_list = {} | ||
for item in data: | ||
assert len(self._tencent_data_format) == len(item.groups()) | ||
single = dict(zip(self._tencent_data_format, item.groups())) | ||
result_list[single["stock"]] = single | ||
return result_list | ||
|
||
def in_time_slice(self, ): | ||
now_time = datetime.datetime.now() | ||
if now_time.weekday() > 4: | ||
return False | ||
for single_slice in self._time_slice: | ||
begin_time = datetime.datetime.strptime(str(datetime.datetime.now().date()) + single_slice["begin_time"], | ||
'%Y-%m-%d%H:%M') - datetime.timedelta(minutes=2) | ||
end_time = datetime.datetime.strptime(str(datetime.datetime.now().date()) + single_slice["end_time"], | ||
'%Y-%m-%d%H:%M') + datetime.timedelta(minutes=2) | ||
if begin_time <= now_time <= end_time: | ||
return True | ||
return False | ||
|
||
async def _async_update_data(self): | ||
data = self.data | ||
if self.in_time_slice() or self._count >= MAX_UPDATE_INTERVAL: | ||
self._count = 0 | ||
async with async_timeout.timeout(3): | ||
r = await self._hass.async_add_executor_job( | ||
requests.get, | ||
self._quest_url | ||
) | ||
if r.status_code == 200: | ||
data = self.format_response_data(r.text) | ||
_LOGGER.debug(f"Successful to update stock data {data}") | ||
else: | ||
self._count = self._count + MIN_UPDATE_INTERVAL | ||
_LOGGER.debug(f"Now time not in slice, count = {self._count}") | ||
return data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
tencent_stock: | ||
time_slices: | ||
- begin_time: '9:15' | ||
end_time: '11:30' | ||
- begin_time: '13:00' | ||
end_time: '15:00' | ||
stocks: | ||
sh: ['000001','600029','600519'] | ||
sz: ['399001','399006'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
DOMAIN = "tencent_stock" | ||
REPOS_API_URL = "https://qt.gtimg.cn/q=" | ||
CONF_EXCHANGE = "exchange" | ||
CONF_STOCK = "stock" | ||
MIN_UPDATE_INTERVAL = 10 | ||
MAX_UPDATE_INTERVAL = 36000 | ||
TIME_SLICES = [{"begin_time": "9:15", "end_time": "11:30"}, {"begin_time": "13:00", "end_time": "15:00"}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"domain": "tencent_stock", | ||
"name": "Tencent Stocks", | ||
"version": "v0.0.2", | ||
"config_flow": true, | ||
"documentation": "https://github.com/georgezhao2010/tencent_stock", | ||
"issue_tracker": "https://github.com/georgezhao2010/tencent_stock/issues", | ||
"requirements": [], | ||
"dependencies": [], | ||
"iot_class": "cloud_polling", | ||
"codeowners": ["@georgezhao2010"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import logging | ||
from .const import DOMAIN | ||
from homeassistant.const import STATE_UNKNOWN | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | ||
sensors = [] | ||
coordinator = hass.data[DOMAIN] | ||
for exchange, stocks in discovery_info["stocks"].items(): | ||
for stock in stocks: | ||
sensors.append(StockSensor(coordinator, exchange + stock)) | ||
async_add_devices(sensors, True) | ||
|
||
|
||
class StockSensor(CoordinatorEntity): | ||
def __init__(self, coordinator, stock): | ||
super().__init__(coordinator) | ||
self._coordinator = coordinator | ||
self._stock = stock | ||
self._unique_id = f"{DOMAIN}.stock_{stock}" | ||
self.entity_id = self._unique_id | ||
self._is_index = True if (stock[0:2] == "sh" and stock[2:5] == "000") \ | ||
or (stock[0:2] == "sz" and stock[2:5] == "399") else False | ||
|
||
def get_value(self, key): | ||
data = self._coordinator.data.get(self._stock) | ||
if data is not None: | ||
return data.get(key) | ||
else: | ||
return STATE_UNKNOWN | ||
|
||
@staticmethod | ||
def sign(str_val): | ||
if float(str_val) != 0 and str_val[0:1] != "-": | ||
return "+" + str_val | ||
return str_val | ||
|
||
@property | ||
def name(self): | ||
return f"{self.get_value('name')}({self._stock}) [{self.sign(self.get_value('涨跌'))}" \ | ||
f"({self.sign(self.get_value('涨跌(%)'))}%)]" | ||
|
||
@property | ||
def unique_id(self): | ||
return self._unique_id | ||
|
||
@property | ||
def state(self): | ||
return float(self.get_value('当前价格')) | ||
|
||
@property | ||
def device_id(self): | ||
return self.device_id | ||
|
||
@property | ||
def device_state_attributes(self) -> dict: | ||
ret = { | ||
"股票代码": self.get_value("股票代码"), | ||
"当前价格": self.get_value("当前价格"), | ||
"昨收": self.get_value("昨收"), | ||
"今开": self.get_value("今开"), | ||
"成交量": self.get_value("成交量(手)"), | ||
"外盘": self.get_value("外盘"), | ||
"内盘": self.get_value("内盘"), | ||
"涨跌": self.sign(self.get_value("涨跌")), | ||
"涨跌幅": self.sign(self.get_value("涨跌(%)")) + "%", | ||
"最高": self.get_value("最高"), | ||
"最低": self.get_value("最低"), | ||
"成交额": self.get_value("成交额(万)"), | ||
"振幅": self.get_value("振幅") + "%", | ||
} | ||
|
||
if not self._is_index: | ||
ret.update({ | ||
"卖五(" + self.get_value("卖五") + ")": self.get_value("卖五量(手)"), | ||
"卖四(" + self.get_value("卖四") + ")": self.get_value("卖四量(手)"), | ||
"卖三(" + self.get_value("卖三") + ")": self.get_value("卖三量(手)"), | ||
"卖二(" + self.get_value("卖二") + ")": self.get_value("卖二量(手)"), | ||
"卖一(" + self.get_value("卖一") + ")": self.get_value("卖一量(手)"), | ||
"买一(" + self.get_value("买一") + ")": self.get_value("买一量(手)"), | ||
"买二(" + self.get_value("买二") + ")": self.get_value("买二量(手)"), | ||
"买三(" + self.get_value("买三") + ")": self.get_value("买三量(手)"), | ||
"买四(" + self.get_value("买四") + ")": self.get_value("买四量(手)"), | ||
"买五(" + self.get_value("买五") + ")": self.get_value("买五量(手)"), | ||
"换手率": self.get_value("换手率") + "%", | ||
"市盈率": self.get_value("市盈率"), | ||
"流通市值": self.get_value("流通市值"), | ||
"总市值": self.get_value("总市值"), | ||
"市净率": self.get_value("市净率"), | ||
"量比": self.get_value("量比"), | ||
"均价": self.get_value("均价"), | ||
"涨停价": self.get_value("涨停价"), | ||
"跌停价": self.get_value("跌停价"), | ||
"委差": self.get_value("委差"), | ||
"市盈(动)": self.get_value("市盈(动)"), | ||
"市盈(静)": self.get_value("市盈(静)") | ||
}) | ||
return ret | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
if self._is_index: | ||
return "点" | ||
else: | ||
return "元" | ||
|
||
@property | ||
def icon(self): | ||
return "hass:pulse" |